Roger Breton
Roger Breton

Reputation: 91

c++ Save application window to file yield 'incorrect' area?

I'm slowly making progress in my c++ application. I would like to 'automate' the repetitive manual screen capture I go through every time in support of my debugging process. I'm relatively new to c++ so I found this snippet that seems to work fine for me, except it's not quite capturing my application area :

RECT rectWindow;
GetWindowRect(hWnd, &rectWindow);   // hWnd defined in global scope

int x1, y1, x2, y2, w, h;

x1 = rectWindow.left;   // x1 = 780
y1 = rectWindow.top;    // y1 = 385
x2 = rectWindow.right;  // x2 = 1780
y2 = rectWindow.bottom; // y2 = 1055

// width and height
w = x2 - x1;    // w = 1000
h = y2 - y1;    // h = 670

// copy window to bitmap
HDC     hWindow = GetDC(hWnd);
HDC     hDC = CreateCompatibleDC(hWindow);
HBITMAP hBitmap = CreateCompatibleBitmap(hWindow, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL    bRet = BitBlt(hDC, 0, 0, w, h, hWindow, x1, y1, SRCCOPY);

CImage image;
image.Attach(hBitmap);
image.Save(L"C:\\TEMP\\window.bmp", Gdiplus::ImageFormatBMP);

// clean-up
SelectObject(hDC, old_obj);
DeleteDC(hDC);
::ReleaseDC(NULL, hWindow);
DeleteObject(hBitmap);

Below is a manual screen capture of my application window :

result of manual screen capture

Now, this (saved in JPEG to meet SO min size requirements) is what I'm getting :

BMP image re-saved as JPEG

Not quite what I expect. So I edited the Bit Block call this way :

BOOL    bRet = BitBlt(hDC, 0, 0, w, h, hWindow, 0, 0, SRCCOPY);

And this gives me "almost" the correct capture :

Almost correct result

What am doing wrong?

Upvotes: 0

Views: 146

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595319

GetWindowRect() returns screen coordinates, which you are then passing as-is to BitBlt(), but it is expecting coordinates relative to the window instead since you are capturing from a window DC and not a screen DC. That is why the top-left corner of your capture is so far into the window.

Change this:

BitBlt(..., hWindow, x1, y1, ...)

To this instead:

BitBlt(..., hWindow, 0, 0, ...)

Also, GetDC() retrieves an HDC for just the client area of the window:

The GetDC function retrieves a handle to a device context (DC) for the client area of a specified window or for the entire screen.

Your window's title bar and menu do not appear in your capture because they are outside of the client area. Since you want a capture of the entire window, use GetWindowDC() instead:

The GetWindowDC function retrieves the device context (DC) for the entire window, including title bar, menus, and scroll bars. A window device context permits painting anywhere in a window, because the origin of the device context is the upper-left corner of the window instead of the client area.

Upvotes: 1

Roger Breton
Roger Breton

Reputation: 91

Found some code that worked flawlessly. It's using a lot of the same code?

RECT rcSrc;
HWND hSrcWnd;
HDC hDC2, hSrcDC;
HBITMAP hBmp;
//hSrcWnd = FindWindow(NULL, szWindowClass);

GetWindowRect(hWnd, &rcSrc);

hDC2 = GetDC(hWnd);
hSrcDC = CreateCompatibleDC(NULL);
hBmp = CreateCompatibleBitmap(hDC2, rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top);
SelectObject(hSrcDC, hBmp);
PrintWindow(hWnd, hSrcDC, 0);
BitBlt(hDC2, 0, 0, rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, hSrcDC, 0, 0, SRCCOPY);

CImage image2;
image2.Attach(hBmp);
image2.Save(L"C:\\TMP\\Test.jpg", Gdiplus::ImageFormatJPEG);

DeleteObject(hBmp);
DeleteDC(hSrcDC);
ReleaseDC(hWnd, hDC2);

The result is this : enter image description here

Upvotes: 0

Related Questions