我正在尝试编写一个涉及处理从窗口捕获的数据的C++程序。我是有意用windows编写的,所以请不要推荐已经存在的库来简化这个任务。无论如何,为了进行测试,我编写了一个函数,它应该接受一个hwnd句柄和一个hdc设备上下文,并使用该信息创建由hwnd引用的窗口工作区的位图图像文件。我编写的代码很大程度上是基于此示例代码来自MSDN的。我只是暂时尝试捕捉数据,而不是在屏幕上显示或以任何方式修改它。我还对代码进行了大量的注释,以了解我认为我对每一行都做了什么。如果我在这里有什么根本的误解,请告诉我。
void screenshot(HWND hwnd, HDC hdc){
HDC clientarea = hdc; // The device context for the client area of the window
HDC memory = NULL; // client context for storing the bitmap in memory
RECT clientdim; // Defines memeory location for dimensions of client window
GetClientRect(hwnd, &clientdim); // Stores the client dimesnion info at clientdim location
BITMAP img; // Points to the where the bitmap handle will write it's data once it contains the bitmap data for the client window
HBITMAP clienthandle = CreateCompatibleBitmap(clientarea, clientdim.right - clientdim.left, clientdim.bottom - clientdim.top); // Bitmap handle, actual data of the rectangle defined at clientdim
SelectObject(memory, clienthandle); // Selecting the bitmap handle into the memory context allows us to bitblit the bitmap data to the bitmap handle
BitBlt(memory, 0, 0, clientdim.right - clientdim.left, clientdim.bottom - clientdim.top, clientarea, 0, 0, SRCCOPY);
// The bitmap handle now contains the data defined by the rectangle coordinates, which the dimensions of the hwnd client area
// The source and desitnation nXdest, nYdest, etc. fields are with respect the the window, not the screen, so they are 0
// Gets the bitmap data from the handle, and stores it to a memory location as an actual bitmap file
GetObject(clienthandle, sizeof(BITMAP), &img);
// Info head for the bitmap. Information about the dimesions and colors
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER); // Sets the size of the header literally to the size of an info header.
bmih.biWidth = img.bmWidth; // Sets the width of the bitmap to the width of the bitmap in memory
bmih.biHeight = img.bmHeight; // Sets the height of the bitmap to the height of the bitmap in memory
bmih.biPlanes = 1; // number of planes must be 1 for bitmaps to save
bmih.biBitCount = 32; // 32-bit color. Means RGB dat is stored per pixel.
bmih.biCompression = BI_RGB; // Tells it to use the RGB compression, as defined by the 32bit pixels
bmih.biSizeImage = 0; // Size of compression buffer. RGB is uncompressed, so is 0 for now
bmih.biXPelsPerMeter = 0; // Defines the number of pixels per meter in the x axis. Only used by some programs to select images, we don't care.
bmih.biYPelsPerMeter = 0; // Same as above, but fore y axis.
bmih.biClrUsed = 0; // the number of color indexes the bitmat uses. 0 means it uses all of them
bmih.biClrImportant = 0; // The number of color indexes actually required to display a pixel. 0 means we need all of them.
DWORD bitmapsize = ((img.bmWidth * bmih.biBitCount + 31) / 32) * 4 * img.bmHeight; // Total size of the bitmap. Not sure why this works, will look into it
HANDLE hDIB = GlobalAlloc(GHND, bitmapsize); // A handle to heap memory of size bitmapsize where our bitmap is processed. GHND = Initializes memory to zero an
//allows location to translate to pointer with globallock, called such because it locks the memory in place within the heap
// hDIB because it is a Device Independent Bitmap
char *lpbitmap = (char *)GlobalLock(hDIB); // Stores the contents of the char at the memory locattion defined by GlobalLock(). Basically, this is the bitmap in the heap.
GetDIBits(clientarea, clienthandle, 0, (UINT)img.bmHeight, lpbitmap, (BITMAPINFO *)&bmih, DIB_RGB_COLORS); // Gets the bits from the bitmap and copies them to the buffer found at lpbitmap
HANDLE hFile = CreateFile(L"capture.bmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // Creates file with various attributes. Check the documentation on MSDN.
DWORD dibSize = bitmapsize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //Size of the dib is the size of the actual bitmap plus the size of its headers
BITMAPFILEHEADER bmfh; // Fileheader for the bitmap file
bmfh.bfType = 0x4D42; // Defines file as bitmap. 0x4D42 is the code for this
bmfh.bfSize = dibSize; // The total size is everything in the DIB
bmfh.bfReserved1 = 0; // These reserved values must be zero
bmfh.bfReserved2 = 0;
bmfh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); // Offset to where the image begins. It begins after the header data
// File headers are written first, then the actual bitmap data.
DWORD byteswritten = 0; //Used in writefile commands to accept number of bytes written. I'd have to use an overlapped structure otherwise
WriteFile(hFile, (LPSTR)&bmfh, sizeof(BITMAPFILEHEADER), &byteswritten, NULL); // Look at documentation for remind on what the hell is happening here
WriteFile(hFile, (LPSTR)&bmih, sizeof(BITMAPINFOHEADER), &byteswritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, bitmapsize, &byteswritten, NULL);
// Unlcok DIB from the heap, then free it's memory back up
GlobalUnlock(hDIB);
GlobalFree(hDIB);
// Close the file handle
CloseHandle(hFile);
// Delete all of the objects and release the device contexts
DeleteObject(memory);
ReleaseDC(hwnd, clientarea);
// Donezo!!HDC hdc是由hwnd指示的窗口的设备上下文。我将这两种方法传递给函数,而不是仅仅在函数本身中使用GetDC(hwnd),因为我已经从代码的其他地方获得了设备上下文。我想这更有效率。
任何指导都会很有帮助。谢谢。
编辑:有人指出,我在最初的帖子中忘记了实际的问题。我遇到的问题是,这段代码是保存一个仅仅是一个黑匣子的位图。它实际上并不捕获窗口的内容。
发布于 2022-11-09 01:32:59
这是一个适合我的版本。
void screenshot(HWND hWnd,wchar_t* fileName){
HBITMAP hbmScreen{NULL};
BITMAP bmpScreen;
DWORD dwBytesWritten = 0;
DWORD dwSizeofDIB = 0;
HANDLE hFile = NULL;
char* lpbitmap = NULL;
HANDLE hDIB = NULL;
DWORD dwBmpSize = 0;
// Retrieve the handle to a display device context for the client
// area of the window.
const HDC hdcScreen = GetDC(hWnd);
// Create a compatible DC, which is used in a BitBlt from the window DC.
const HDC hdcMemDC = CreateCompatibleDC(hdcScreen);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
goto done;
}
// Get the client area for size calculation.
RECT rcClient;
GetClientRect(hWnd, &rcClient);
// This is the best stretch mode.
SetStretchBltMode(hdcScreen, HALFTONE);
// Create a compatible bitmap from the Window DC.
hbmScreen = CreateCompatibleBitmap(hdcScreen, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC, hbmScreen);
// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcScreen,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
// Get the BITMAP from the HBITMAP.
GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
// Gets the "bits" from the bitmap, and copies them into a buffer
// that's pointed to by lpbitmap.
GetDIBits(hdcScreen, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
hFile = CreateFile(fileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
// Add the size of the headers to the size of the bitmap to get the total file size.
dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// Offset to where the actual bitmap bits start.
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
// Size of the file.
bmfHeader.bfSize = dwSizeofDIB;
// bfType must always be BM for Bitmaps.
bmfHeader.bfType = 0x4D42; // BM.
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
// Unlock and Free the DIB from the heap.
GlobalUnlock(hDIB);
GlobalFree(hDIB);
// Close the handle for the file that was created.
CloseHandle(hFile);
// Clean up.
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(hWnd, hdcScreen);
}https://stackoverflow.com/questions/50660814
复制相似问题