我試圖在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回答。很想把獎勵給你們倆。
嘗試的IntPtr:INT小時= 0; IntPtr gData = IntPtr.Zero; IntPtr gSize = IntPtr.Zero; hr = ScreenCap(gData,gSize); – jdweng
謝謝。我在dllimport中將byte []更改爲intptr,並將gData僅更改爲IntPtr,並返回參數nullexception – user3882856
您獲得了多大的大小?要獲取字節,你需要使用Marshal:byte [] buffer = new byte [size]; Marshal.Copy(data,buffer,0,size); – jdweng