I am following the api from here: https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image
I am using msys ucrt64 gcc compiler for this program.
Here is the code I have:
#include <Windows.h>
#include <tlhelp32.h>
#include <wincodec.h>
typedef HWND WindowHandle;
#define ASSERT_EXIT(expr, str, ...)
((void) sizeof ((expr) ? 1 : 0), __extension__ ({
if (!(expr)) {
fprintf(stderr,"At function [%s] in file [%s:%d], assert failed: [(%s)].n" str "n", __func__, __FILE__, __LINE__, (#expr));
__VA_ARGS__;
exit(EXIT_FAILURE);
}
}))
#define TEST
#define DEBUG_UTIL
#ifdef DEBUG_UTIL
void save_bmp(BITMAP bmp, HDC winDC, HBITMAP hbmWin) {
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmp.bmWidth;
bi.biHeight = bmp.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;
DWORD dwBmpSize = ((bmp.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmp.bmHeight;
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char *lpbitmap = (char*)GlobalLock(hDIB);
GetDIBits(winDC, hbmWin, 0,
(UINT)bmp.bmHeight,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
HANDLE hFile = CreateFile("./captureqwsx.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
bmfHeader.bfType = 0x4D42;
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
GlobalUnlock(hDIB);
GlobalFree(hDIB);
CloseHandle(hFile);
}
#endif
static vector<DWORD> *pid_list = new vector<DWORD>();
static inline void fill_pid_list(const wchar_t* exeName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE) {
PROCESSENTRY32W processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(hSnapshot, &processEntry)) {
do {
if (wcsstr(processEntry.szExeFile, exeName) != NULL) {
(*pid_list).push_back(processEntry.th32ProcessID);
}
} while (Process32NextW(hSnapshot, &processEntry));
};
CloseHandle(hSnapshot);
}
}
typedef struct {
size_t list_sz;
WindowHandle win;
}clbckParam;
static inline BOOL CALLBACK winListPidClbck(WindowHandle handle, LPARAM param) {
DWORD pid = 0;
clbckParam *cp = (clbckParam *)param;
GetWindowThreadProcessId(handle, &pid);
for(size_t i = 0; i < cp->list_sz; i++) {
if(pid == (*pid_list)[i]) {
if (IsWindowVisible(handle)) {
cp->win = handle;
return FALSE;
}
break;
}
}
return TRUE;
}
static inline WindowHandle get_win_from_pid(const wchar_t* exeName) {
fill_pid_list(exeName);
clbckParam cp = { .list_sz = (*pid_list).size(), cp.win = NULL };
EnumWindows(winListPidClbck, (LPARAM)&cp);
delete pid_list;
return cp.win;
}
void CaptureWindow(const wchar_t* exeName) {
HWND win = get_win_from_pid(exeName);
ASSERT_EXIT(win, "Failed to get window handle");
RECT rect;
GetClientRect(win, &rect);
LONG w = rect.right - rect.left;
LONG h = rect.bottom - rect.top;
#ifdef TEST
printf("h:%ld, w:%ld", h, w);
#endif
HDC winDC = GetDC(win); /* if i change win to NULL it captures the whole screen and produces and image I have no problem captures w x h size of the entire screen */
ASSERT_EXIT(winDC, "Failed to get winDC");
HDC memDC = CreateCompatibleDC(winDC);
ASSERT_EXIT(memDC, "Failed to create memDC", ReleaseDC(win, winDC));
HBITMAP hbmWin = CreateCompatibleBitmap(winDC, w, h);
ASSERT_EXIT(hbmWin, "Failed to create hbmWin", DeleteDC(memDC), ReleaseDC(win, winDC));
SelectObject(memDC, hbmWin);
auto cleanup = [&]() {
DeleteObject(hbmWin);
DeleteDC(memDC);
ReleaseDC(win, winDC);
};
ASSERT_EXIT(BitBlt(memDC, 0, 0, w, h, winDC, 0, 0, SRCCOPY), "BitBlt failed", cleanup());
BITMAP bmpWin;
GetObject(hbmWin, sizeof(BITMAP), &bmpWin);
#ifdef TEST
save_bmp(bmpWin, winDC, hbmWin);
#endif
cleanup();
}
#ifdef TEST
int main() {
const wchar_t* exeName = L"Code";
CaptureWindow(exeName);
return EXIT_SUCCESS;
}
#endif
Title: Issue with Capturing Specific Window Image Using GetDC in Windows
Question:
I’m trying to capture an image of a specific window in Windows using the GetDC function. When I pass NULL
to GetDC
, it captures the whole screen correctly and produces an image without any issues. However, when I pass a specific window handle win
to GetDC
, it produces a black image.
This is the relevant line of code HDC winDC = GetDC(win);
and change this code to HDC winDC = GetDC(NULL);
it will generate a image(not correctly entirely because the rect dimensions are not correct) but it shows proof of concept. So, question is why passing win to capture a specific window does not work? how to fix it?
I am sure that I am getting the correct window before the rect dimensions match the window.
Any insights or suggestions would be greatly appreciated! (MINIMUM REPRODUCIBLE CODE ATTACHTED AT THE TOP)