2017-08-04 29 views
-2

我试图在C++中创建一个屏幕捕获DLL并将生成的字节数组发送到C#。BitBlt转换为字节数组并将其从C++解析为c#

我能够得到返回到C#的大小,但字节数组始终为空。

这里的C++代码(由位的我在互联网上找到)

__declspec(dllexport) int ScreenCap(BYTE* *data, DWORD *size) 
{ 
    try 
    { 
    //BITMAP bmpScreen; 
    HWND DesktopHwnd = GetDesktopWindow(); 
    RECT DesktopParams; 
    HDC DevC = GetDC(DesktopHwnd); 
    GetWindowRect(DesktopHwnd,&DesktopParams); 
    DWORD Width = DesktopParams.right - DesktopParams.left; 
    DWORD Height = DesktopParams.bottom - DesktopParams.top; 

    DWORD FileSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBTRIPLE)+1*(Width*Height*4)); 
    *size = FileSize; 
    char *BmpFileData = (char*)GlobalAlloc(0x0040,FileSize); 

    PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData; 
    PBITMAPINFOHEADER BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)]; 

    BFileHeader->bfType = 0x4D42; // BM 
    BFileHeader->bfSize = sizeof(BITMAPFILEHEADER); 
    BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); 

    BInfoHeader->biSize = sizeof(BITMAPINFOHEADER); 
    BInfoHeader->biPlanes = 1; 
    BInfoHeader->biBitCount = 24; 
    BInfoHeader->biCompression = BI_RGB; 
    BInfoHeader->biHeight = Height; 
    BInfoHeader->biWidth = Width; 

    RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)]; 
    RGBTRIPLE color; 

    HDC CaptureDC = CreateCompatibleDC(DevC); 
    HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC,Width,Height); 
    SelectObject(CaptureDC,CaptureBitmap); 
    BOOL bRet = BitBlt(CaptureDC,0,0,Width,Height,DevC,0,0,SRCCOPY|CAPTUREBLT); 
    //GetDIBits(CaptureDC,CaptureBitmap,0,Height,Image,(LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS); 
    //GetObject(CaptureBitmap,sizeof(BITMAPFILEHEADER),&bmpScreen); 
    //BYTE* lpPixels = new BYTE[sizeof((LPBITMAPINFO)BInfoHeader)]; 
    GetDIBits(CaptureDC, CaptureBitmap, 0, Height, *data, (LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS); 
    //DWORD Junk; 
    //DIBSECTION dib; 
    //GetObject(CaptureBitmap, sizeof(dib), (LPVOID)&dib); 

    //HANDLE FH = CreateFileA(BmpName,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0); 
    //WriteFile(FH,BmpFileData,FileSize,&Junk,0); 
    //CloseHandle(FH); 
     GlobalFree(BmpFileData); 


     return 1; 
    } 
    catch(char *p) 
    { 
     return 0; 
    } 
} 

下面是我使用的“裁判”关键字的C#代码。

 [DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)] 
     static extern int ScreenCap(ref byte[] data, ref int size); 

     public static void CapScreen() 
    { 
     try 
     { 
      int hr = 0; 
      byte[] gData = null; 
      int gSize = 0; 
      hr = ScreenCap(ref gData, ref gSize); 
      int a = 1; 
     } 
     catch(Exception ex) 
     { 
      int a = 1; 
     } 

我在想我的GetDIBits可能存在问题,但我不确定。我希望你们中的一些人能够让我走上正确的道路。谢谢。

*编辑*

OK非常感谢这两个jdweng和菲利普指着我在正确的方向。我现在将屏幕作为IntPtr接收并能够将其呈现给我的C#应用​​程序。下面是现在工作的代码(种我的会在底部解释)

__declspec(dllexport) char* ScreenCap(DWORD * size) 

{ 尝试 { //获取屏幕尺寸 INT nScreenWidth = GetSystemMetrics的(SM_CXSCREEN); int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);

*size = ((((24 * nScreenWidth + 31)&(~31))/8)*nScreenHeight); 

    BITMAPINFO MyBMInfo = {0}; 
    MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader); 

    // Create compatible DC, create a compatible bitmap and copy the screen using BitBlt() 
    HDC hdcScreen = GetDC(GetDesktopWindow()); 
    HDC hdcCompatible = CreateCompatibleDC(hdcScreen); 
    HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nScreenWidth, nScreenHeight); 
    //HGDIOBJ hOldBmp = SelectObject(hdcCompatible, hBmp); 
    HGDIOBJ hOldBmp = (HGDIOBJ) SelectObject(hdcCompatible, hBmp); 

    BOOL bOK = BitBlt(hdcCompatible,0,0,nScreenWidth, nScreenHeight, hdcScreen,0,0,SRCCOPY|CAPTUREBLT); 

    SelectObject(hdcCompatible, hOldBmp); // always select the previously selected object once done 
    // Get the BITMAPINFO structure from the bitmap 
    GetDIBits(hdcScreen, hBmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS); 

    // create the bitmap buffer 
    char* lpPixels = new char[MyBMInfo.bmiHeader.biSizeImage]; 

    MyBMInfo.bmiHeader.biCompression = BI_RGB; 
    MyBMInfo.bmiHeader.biBitCount = 24; 

    // get the actual bitmap buffer 
    GetDIBits(hdcScreen, hBmp, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS); 

    //Clean Up 
    DeleteDC(hdcCompatible); 
    ReleaseDC(GetDesktopWindow(), hdcScreen); 
    DeleteDC(hdcScreen); 
    DeleteObject(hBmp); 
    //DeleteObject(hOldBmp); 

    return lpPixels; 
} 
catch(char *p) 
{ 
    return 0; 
} 

}

和C#

 [DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)] 
    static extern IntPtr ScreenCap(ref int size); 
       while(true) 
      { 
       int size = 0; 
       IntPtr ptr = ScreenCap(ref size); 
       byte[] result = new byte[size]; 
       Marshal.Copy(ptr, result, 0, size); 
       Marshal.Release(ptr); 
       ptr = IntPtr.Zero; 
       MainWindow.Dispatcher.Invoke(new Action(
       () => 
        { 
         System.Windows.Media.ImageSource ThumbnailImage = System.Windows.Media.Imaging.BitmapSource.Create(1920, 1080, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null, result, 1920 * 3); 
         MainWindow.canvasMain.Background = new System.Windows.Media.ImageBrush(ThumbnailImage); 
         result = null; 
         GC.Collect(); 
         GC.WaitForPendingFinalizers(); 
        } 
       ), null); 
      } 

不知道如果我现在面临的问题是由于做这种方式但我从C得到一个重大的内存泄漏++ DLL现在。如果这是另一个问题,我是否需要创建一个新线程?

** **编辑

问题通过在C创建一个新的功能++ DLL删除返回char数组解决。可能不够高雅,但它的工作原理。

我想非常感谢Filip,他的评论帮助我查看各种地方并查看其他内容。 Jdweng,你的IntPtr是上帝派来的。不知道我如何设置这个由Jdweng回答。很想把奖励给你们俩。

+0

尝试的IntPtr:INT小时= 0; IntPtr gData = IntPtr.Zero; IntPtr gSize = IntPtr.Zero; hr = ScreenCap(gData,gSize); – jdweng

+0

谢谢。我在dllimport中将byte []更改为intptr,并将gData仅更改为IntPtr,并返回参数nullexception – user3882856

+0

您获得了多大的大小?要获取字节,你需要使用Marshal:byte [] buffer = new byte [size]; Marshal.Copy(data,buffer,0,size); – jdweng

回答

0

如果有人正在寻找相同的东西,这是我的工作。我想非常感谢Filip Kocica和jdweng,他们的建议给了我一些想法并且非常有帮助。非常感谢和赞赏。

C++

char* lpPixels; 
__declspec(dllexport) BOOL ScreenCapClean() 
{ 
    delete[] lpPixels; 
    return 1; 
} 

__declspec(dllexport) char* ScreenCap(DWORD * size) 
{ 
    try 
    { 
     // Get screen dimensions 
     int nScreenWidth = GetSystemMetrics(SM_CXSCREEN); 
     int nScreenHeight = GetSystemMetrics(SM_CYSCREEN); 

     *size = ((((24 * nScreenWidth + 31)&(~31))/8)*nScreenHeight); 

     BITMAPINFO MyBMInfo = {0}; 
     MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader); 
     MyBMInfo.bmiHeader.biWidth = nScreenWidth; 
     MyBMInfo.bmiHeader.biHeight = -nScreenHeight; 
     MyBMInfo.bmiHeader.biPlanes = 1; 
     MyBMInfo.bmiHeader.biBitCount = 24; 
     MyBMInfo.bmiHeader.biCompression = BI_RGB; 
     MyBMInfo.bmiHeader.biSizeImage = 0; 
     MyBMInfo.bmiHeader.biXPelsPerMeter = 0; 
     MyBMInfo.bmiHeader.biYPelsPerMeter = 0; 
     MyBMInfo.bmiHeader.biClrUsed = 0; 
     MyBMInfo.bmiHeader.biClrImportant = 0; 

     // Create compatible DC, create a compatible bitmap and copy the screen using BitBlt() 
     HDC hdcScreen = GetDC(0); 
     HDC hdcCompatible = CreateCompatibleDC(hdcScreen); 
     HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nScreenWidth, nScreenHeight); 
     HGDIOBJ hOldBmp = (HGDIOBJ) SelectObject(hdcCompatible, hBmp); 

     BOOL bOK = BitBlt(hdcCompatible,0,0,nScreenWidth, nScreenHeight, hdcScreen,0,0,SRCCOPY|CAPTUREBLT); 

     SelectObject(hdcCompatible, hOldBmp); // always select the previously selected object once done 
     // Get the BITMAPINFO structure from the bitmap 
     GetDIBits(hdcScreen, hBmp, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS); 

     // create the bitmap buffer 
     lpPixels = new char[MyBMInfo.bmiHeader.biSizeImage]; 

     MyBMInfo.bmiHeader.biCompression = BI_RGB; 
     MyBMInfo.bmiHeader.biBitCount = 24; 

     // get the actual bitmap buffer 
     GetDIBits(hdcScreen, hBmp, 0, -MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS); 

     //Clean Up 
     ReleaseDC(0, hdcScreen); 
     ReleaseDC(0, hdcCompatible); 
     DeleteDC(hdcCompatible); 
     DeleteDC(hdcScreen); 
     DeleteObject(hBmp); 
     DeleteObject(hOldBmp); 

     return lpPixels; 
    } 
    catch(char *p) 
    { 
     return 0; 
    } 
} 

C#

[DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)] 
    static extern IntPtr ScreenCap(ref int size); 
    [DllImport("consoleapplication1.dll", CallingConvention = CallingConvention.Cdecl)] 
    static extern bool ScreenCapClean(); 

    int size = 0; 
    IntPtr ptr = ScreenCap(ref size); 
    byte[] result = new byte[size]; 
    Marshal.Copy(ptr, result, 0, size); 
    Marshal.Release(ptr); 
    ptr = IntPtr.Zero; 
    //After doing what's required with the byte array 
    bool hr = ScreenCapClean();