Capturing a program window with BitBlt always returns the same image

半世苍凉 提交于 2019-12-10 15:19:19

问题


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.


回答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).




回答2:


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.



来源:https://stackoverflow.com/questions/11197705/capturing-a-program-window-with-bitblt-always-returns-the-same-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!