最近试过几套截图软件,发现其中有些无法实现对半透明窗口或主题的图片截取,包括早期版本的QQ截图工具也无法截取,于是打算写一个简单抓屏函数的来测试下,以下采用Win32 API方式编写:
HBITMAP CapScreen()
{
HDC hDispDC,hMemDC;
// 获取屏幕DC
hDispDC = CreateDC("DISPLAY",NULL,NULL,NULL);
hMemDC = CreateCompatibleDC(hDispDC);
int cx,cy;
cx = GetSystemMetrics(SM_CXSCREEN);
cy = GetSystemMetrics(SM_CYSCREEN);
HBITMAP hSnapBt,hOldBt;
hSnapBt = CreateCompatibleBitmap(hDispDC,cx,cy);
hOldBt = (HBITMAP)SelectObject(hMemDC,hSnapBt);
BitBlt(hMemDC,0,0,cx,cy,hDispDC,0,0,SRCCOPY);
// cleanup
SelectObject(hMemDC,hOldBt);
DeleteDC(hMemDC);
DeleteDC(hDispDC);
return hSnapBt;
}
测试发现果然无法截取到使用WindowBlinds模拟vista半透明主题的窗口栏,还有所有半透明的窗口!
利用Spy++对以上无法截取到的窗口进行抓捕,发现这些窗口都具有WS_EX_LAYERED这个扩展属性,又仔细看了下MSDN中关于BitBlt的说明,原型如下:
BOOL BitBlt(
HDC hdcDest, // handle to destination DC
int nXDest, // x-coord of destination upper-left corner
int nYDest, // y-coord of destination upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
HDC hdcSrc, // handle to source DC
int nXSrc, // x-coordinate of source upper-left corner
int nYSrc, // y-coordinate of source upper-left corner
DWORD dwRop // raster operation code
);
对于其他参数我们并不用关心,主要该注意下最后这个参数dwRop:
[in] Specifies a raster-operation code. These codes define how the color data for the source rectangle is to be combined with the color data for the destination rectangle to achieve the final color. The following list shows some common raster operation codes.
反复看过几次所有参数的含义后发现对于参数CAPTUREBLT,有如下描述:
Windows 98, Windows 2000: Includes any windows that are layered on top of your window in the resulting image. By default, the image only contains your window.
猜想大概就是这个了,不过在引用这个参数时貌似要先更新vc6的SDK,否则会提示CAPTUREBLT未定义,不过也可以手工来给它作个宏定义:
#ifndef CAPTUREBLT
#define CAPTUREBLT 0x40000000
#endif
然后试着再调用BitBlt时组合上这个参数,结果果然可以截获半透明窗口了!
上述截屏函数也可以用MFC方式来写:
HBITMAP CapScreen()
{
CDC dispDC,memDC;
dispDC.CreateDC("DISPLAY",NULL,NULL,NULL);
memDC.CreateCompatibleDC(&dispDC);
int cx,cy;
cx = GetSystemMetrics(SM_CXSCREEN);
cy = GetSystemMetrics(SM_CYSCREEN);
CBitmap snapBt,*pOldBt;
snapBt.CreateCompatibleBitmap(&dispDC,cx,cy);
pOldBt = memDC.SelectObject(&snapBt);
// BitBlt使用CAPTUREBLT参数
memDC.BitBlt(0,0,cx,cy,&dispDC,0,0,SRCCOPY | CAPTUREBLT);
// cleanup
memDC.SelectObject(pOldBt);
// 不能返回snapBt.m_hObject或snapBt.GetSafeHandle()
// 因为CBitmap对象在析构时会调用DeleteObject来释放位图句柄
return (HBITMAP)snapBt.Detach();
}