2017-08-04 28 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();