使用 BitBlt 截取程序窗口始终返回相同的图像

7
我写了以下代码(C++ Win32)来捕获游戏窗口屏幕并从图像获取像素颜色数组。函数autoB()完成了这项工作。
然后,我将结果数组绘制到我的窗口中,以便进行视觉检查。
问题在于,此程序仅在我启动计算机后第一次运行时有效,之后它会“缓存”从游戏中获取的第一张屏幕截图,并且我总是得到相同的像素数组。即使我关闭并重新启动程序,我也会得到相同的屏幕截图。
游戏未使用DirectX在屏幕上绘制,并且我始终能够使用Alt+PrtSc进行截屏。
感谢任何帮助理解为什么会出现这种情况。
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;

}

唯一更改截图的方法是重新启动游戏。然后,第一张截图将被捕获,并且无论在游戏窗口中发生什么情况,都会反复显示。


autoB 是否会提前返回? - Chris O
2个回答

1
你确定你获取的是相同的像素,还是只是在调试窗口中看到相同的图像?原始图像复制代码看起来没问题,但是在你的“调试”代码中,即使直接调用 SetPixel(),你仍然需要调用 InvalidateRect() 来引起 Windows 发送一个新的 WM_PAINT 消息。如果你没有这样做,那么你可能只是在看旧的图像,尽管新的位已经被捕获(但未绘制)。

0
我遇到了同样的问题,我注意到用于调用CreateCompatibleDC和BitBlt的HDC变量(在您的情况下为hDC0)会产生差异。如果您使用GetDC(NULL),则会获得整个屏幕的图像,在这种情况下,图像每次都会更新,而使用GetDC(myWindowHwnd)会出现您提到的问题,即始终返回相同的图像而没有任何刷新。
我想捕获的窗口是一个全屏、最顶层的窗口,它是这样创建的:
    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);

即使窗口显示在所有其他窗口之上,当另一个线程更新窗口时,BitBlt函数仍无法捕获窗口的更新帧。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接