Capturing a program window with BitBlt always returns the same image
Asked Answered
M

2

7

I wrote the following code (C++ Win32) to capture a game window screen and get pixel color array from the image. Function autoB() does the job.

Then I draw the result array into my window to visually check what I got.

The problem is that this program works only once after I start the computer, after the first time it "caches" the first screenshot taken from the game and I always get the same array of pixels. Even if I close and restart the program I get the same screenshot.

The game is not using DirectX to draw on the screen and I'm always able to take screenshots using Alt+PrtSc.

Any help in understanding why it's happening this way is appreciated.

int getPixels(HDC *eClientHdcMem, HBITMAP *eClientBmp, unsigned char **lp) {

BITMAP bmpScreen;   
    BITMAPINFOHEADER bi;

GetObject(*eClientBmp, sizeof(BITMAP), &bmpScreen);

LONG bW = bmpScreen.bmWidth, bH = bmpScreen.bmHeight;

bi.biSize = sizeof(BITMAPINFOHEADER);    
bi.biWidth = bW;    
bi.biHeight = -bH;  
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 dw = ((bW * bi.biBitCount + 31) / 32) * 4 * bH;
*lp = new unsigned char[dw];

return GetDIBits(*eClientHdcMem, *eClientBmp, 0, (UINT)bH, *lp, (BITMAPINFO *)&bi, DIB_RGB_COLORS);

}

void autoB() {
HWND hwnd;
HDC hDC0 = NULL, eClientHdcMem = NULL;
HBITMAP eClientBmp = NULL;
BITMAP bmp = {0};
unsigned char *lp = NULL, *sp = NULL;
WINDOWINFO wi;
wi.cbSize = sizeof(WINDOWINFO);
RECT vp;
int vpW, vpH;
long iW, iH;

if (!(hwnd = FindWindow(NULL,TEXT("Client")))) return;
if (!(hDC0 = GetDC(hwnd))) return;

GetWindowInfo(hwnd,&wi);
vp = wi.rcClient;
vpW = vp.right - vp.left;
vpH = vp.bottom - vp.top;

if (!(eClientBmp = CreateCompatibleBitmap(hDC0, vpW, vpH))) return;
if (!(eClientHdcMem = CreateCompatibleDC(hDC0))) return;
SelectObject(eClientHdcMem, eClientBmp);

BitBlt(eClientHdcMem, 0, 0, vpW, vpH, hDC0, 0, 0, SRCCOPY);

int res = getPixels(&eClientHdcMem, &eClientBmp, &lp);

DeleteObject(eClientBmp);
DeleteObject(eClientHdcMem);

    // begin testing
HDC sts = GetDC(hStats);
HBITMAP stsBmp = CreateCompatibleBitmap(sts, vpW, vpH);
HBITMAP stsBmpOld = (HBITMAP)SelectObject(sts, stsBmp);
unsigned char r,g,b;
for(unsigned int i=0;i<vpW;i++) {
    for(unsigned int j=0;j<vpH;j++) {
        r = lp[(vpW*j+i) * 4 + 2];
        g = lp[(vpW*j+i) * 4 + 1];
        b = lp[(vpW*j+i) * 4 + 0];
        SetPixel(sts,i,j,RGB(r,g,b));
    }
}
SelectObject(sts, stsBmpOld);
DeleteObject(stsBmp);
DeleteObject(stsBmpOld);
ReleaseDC(hStats,sts);
    // end testing

DeleteDC(eClientHdcMem);
ReleaseDC(hwnd,hDC0);

delete [] lp;
lp = NULL;
delete [] sp;
sp = NULL;

}

The only way to change the screenshot is to restart the game. Then again, the first screenshot is captured and will be displayed over and over again regardless of what's happening in the game window.

Mast answered 25/6, 2012 at 21:35 Comment(1)
Does the autoB ever return too early?Errecart
A
1

Are you sure you're getting back the same pixels, or are you just seeing the same image on your screen in your debug window? The original image copy code looks OK, but in your "debug" code, even though you are calling SetPixel() directly, you still need to call InvalidateRect() to cause Windows to send a new WM_PAINT message. If you're not doing that, you're probably just looking at the old image, even though the new bits have indeed been captured (but not drawn).

Adriaadriaens answered 25/6, 2012 at 22:26 Comment(0)
P
0

I came across the same problem and I noticed that the HDC variable (hDC0 in your case) used to call CreateCompatibleDC and in turn BitBlt makes the difference. If you use GetDC(NULL) you will get the iamge of the whole screen and in this case the image is updated every time, while using GetDC(myWindowHwnd) gives the problem you were mentioning that is always the same image is returned without any refresh.

The window I want to capture is a full screen, topmost window which has been created like this:

    hwnd = CreateWindowExA(0, "myWindow", "myWindow", WS_POPUP, x, y, w, h, NULL, NULL, wc.hInstance, NULL);

    SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT);
    SetLayeredWindowAttributes(hwnd, 0, 0xFF, LWA_ALPHA);
    ShowWindow(hwnd, SW_SHOW);
    SetWindowPos(hwnd, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE);

Even if the windows is displayed and is above all other windows the BitBlt function fails to capture the updated frames of the window while this is updated by another thread.

Putnem answered 11/4, 2019 at 16:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.