C++ 使用BitBlt捕获程序窗口始终返回相同的图像

C++ 使用BitBlt捕获程序窗口始终返回相同的图像,c++,winapi,bitblt,getdibits,C++,Winapi,Bitblt,Getdibits,我编写了以下代码(C++Win32)来捕获游戏窗口屏幕并从图像中获取像素颜色数组。函数autoB()完成此任务 然后我将结果数组绘制到窗口中,以直观地检查得到的结果 问题是,这个程序只在我启动电脑后工作一次,在它第一次“缓存”从游戏中拍摄的第一个屏幕截图之后,我总是得到相同的像素阵列。即使我关闭并重新启动程序,我也会得到相同的屏幕截图 游戏没有使用DirectX在屏幕上绘图,我总是能够使用Alt+PrtSc截图 任何有助于理解为什么会发生这种情况的人都将不胜感激 int getPixels(HD

我编写了以下代码(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;

}
int getPixels(HDC*eClientHdcMem,HBITMAP*eClientBmp,unsigned char**lp){
位图屏幕;
BitMapInfo头bi;
GetObject(*eClientBmp、sizeof(位图)和bmpScreen);
长bW=bmpScreen.bmWidth,bH=bmpScreen.bmHeight;
bi.biSize=sizeof(BitMapInfo头);
bi.biWidth=bW;
bi.biHeight=-bH;
双平面=1;
bibibitcount=32;
bi.biCompression=bi_RGB;
bi.biSizeImage=0;
bi.biXPelsPerMeter=0;
bi.biYPelsPerMeter=0;
bi.BICLRUSE=0;
bi.biclr=0;
DWORD dw=((bW*bi.BIBIBitCount+31)/32)*4*bH;
*lp=新的无符号字符[dw];
返回GetDIBits(*eClientHdcMem,*eClientBmp,0,(UINT)bH,*lp,(BITMAPINFO*)&bi,DIB\u RGB\u颜色);
}
void autoB(){
HWND-HWND;
HDC hDC0=NULL,eClientHdcMem=NULL;
HBITMAP eClientBmp=NULL;
位图bmp={0};
无符号字符*lp=NULL,*sp=NULL;
WINDOWINFO-wi;
wi.cbSize=sizeof(WINDOWINFO);
RECT-vp;
int-vpW,vpH;
长iW,iH;
如果(!(hwnd=FindWindow(NULL,文本(“客户端”))返回;
如果(!(hDC0=GetDC(hwnd))返回;
获取窗口信息(hwnd和wi);
vp=wi.rcClient;
vpW=vp.右-vp.左;
vpH=vp.bottom-vp.top;
if(!(eClientBmp=CreateCompatibleBitmap(hDC0,vpW,vpH))返回;
如果(!(eClientHdcMem=CreateCompatibleDC(hDC0)),返回;
选择对象(eClientHdcMem、eClientBmp);
BitBlt(eClientHdcMem,0,0,vpW,vpH,hDC0,0,SRCCOPY);
int res=getPixels(&eClientHdcMem,&eClientBmp,&lp);
DeleteObject(eClientBmp);
DeleteObject(eClientHdcMem);
//开始测试
HDC sts=GetDC(hStats);
HBITMAP stsBmp=创建兼容的EBITMAP(sts、vpW、vpH);
HBITMAP stsBmpOld=(HBITMAP)SelectObject(sts,stsBmp);
无符号字符r,g,b;

对于(unsigned int i=0;i您确定要恢复相同的像素,还是只是在调试窗口的屏幕上看到相同的图像?原始图像复制代码看起来不错,但在“调试”代码中,即使您直接调用SetPixel(),您仍然需要调用
invalidate()
使Windows发送新的WM_PAINT消息。如果不这样做,您可能只是在查看旧图像,即使新位确实已被捕获(但未绘制)。

我遇到了相同的问题,我注意到HDC变量(在您的情况下为hDC0)用于调用CreateCompatibleDC,反过来BitBlt会产生不同。如果使用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函数也无法捕获窗口的更新帧,而另一个线程正在更新该帧。

autoB
是否过早返回?不,不会。如果我在sp=NUll之后添加MessageBox,每次调用该函数时都会看到它。SetPixel的工作速度非常慢,因此每次调用此函数时,我都会看到它是如何将位图绘制到程序窗口中的。