最近试过几套截图软件,发现其中有些无法实现对半透明窗口或主题的图片截取,包括早期版本的QQ截图工具也无法截取,写一个简单抓屏函数的来测试下,以下采用Win32
API方式编写:
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
_tprintf(_T("错误: MFC 初始化失败\n"));
nRetCode = 1;
}
else
{
// TODO: 在此处为应用程序的行为编写代码。
HWND hwnd = (HWND)::GetDesktopWindow();
HDC dstDC = ::GetDC(NULL);
HDC srcDC = ::GetWindowDC(hwnd); //full window (::GetDC(hwnd); = clientarea)
HDC memDC = ::CreateCompatibleDC(NULL);
CRect CaptureRect;
::GetWindowRect(hwnd,&CaptureRect);
int xScreen,yScreen;
//check if the window is out of the screen | maximixed <Qiang>
int xshift = 0, yshift = 0;
xScreen = GetSystemMetrics(SM_CXSCREEN);
yScreen = GetSystemMetrics(SM_CYSCREEN);
if(CaptureRect.right > xScreen)
CaptureRect.right = xScreen;
if(CaptureRect.bottom > yScreen)
CaptureRect.bottom = yScreen;
if(CaptureRect.left < 0){
xshift = -CaptureRect.left;
CaptureRect.left = 0;
}
if(CaptureRect.top < 0){
yshift = -CaptureRect.top;
CaptureRect.top = 0;
}
CSize sz(CaptureRect.Width(), CaptureRect.Height());
HBITMAP hBitmap = ::CreateCompatibleBitmap(dstDC, sz.cx, sz.cy);
HBITMAP hOldBitmap = (HBITMAP)::SelectObject(memDC,hBitmap);
::BitBlt(memDC, 0, 0, sz.cx, sz.cy, srcDC, xshift, yshift, SRCCOPY);
if (OpenClipboard(NULL))
{
//清空剪贴板
EmptyClipboard();
//把屏幕内容粘贴到剪贴板上,
//hBitmap 为刚才的屏幕位图句柄
SetClipboardData(CF_BITMAP, hBitmap);
//关闭剪贴板
CloseClipboard();
}
HDC hDC;//Device Context(设备环境、设备描述表、设备上下文)的句柄
//当前显示分辨率下每个像素所占字节数
WORD wBitCount = 8;//v_iBitCount
//计算调色板大小
DWORD dwPaletteSize = 0;
if(wBitCount <= 8)//小于八位按八位处理
dwPaletteSize = (1 << wBitCount) * sizeof(RGBQUAD);
//设置位图信息头结构
BITMAP bm;//位图属性结构
BITMAPINFOHEADER bi;//位图信息头结构
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bm.bmWidth;
bi.biHeight = bm.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
//位图中像素字节大小
DWORD dwBmpBitsSize = ((bm.bmWidth * wBitCount+31)/32)* 4 * bm.bmHeight;
//为位图内容分配内存
HANDLE hDib = GlobalAlloc(GHND, dwBmpBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
//处理调色板
HANDLE hPal = NULL;
HANDLE hOldPal = NULL;
hPal = GetStockObject(DEFAULT_PALETTE);
if(hPal)
{
hDC = ::GetDC(NULL);
hOldPal = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
RealizePalette(hDC);
}
//获取该调色板下新的像素值
GetDIBits(hDC, hBitmap, 0, (UINT)bm.bmHeight,
(LPSTR)lpbi + sizeof(BITMAPINFOHEADER) + dwPaletteSize,
(BITMAPINFO*)lpbi, DIB_RGB_COLORS);
//恢复调色板
if(hOldPal)
{
SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL, hDC);
}
HANDLE hFile = CreateFile(_T("c:\\screen.bmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if(hFile == INVALID_HANDLE_VALUE)
return FALSE;
//设置位图文件头
BITMAPFILEHEADER bf;//位图文件头结构
bf.bfType = 0x4d42;//"bm"
DWORD dwDibSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
dwPaletteSize + dwBmpBitsSize;//位图文件大小
bf.bfSize = dwDibSize;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER)+ dwPaletteSize;
//写入位图文件头
DWORD dwWritten;//写入文件字节数
WriteFile(hFile, (LPSTR)&bf, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
//写入位图文件其余内容
WriteFile(hFile, (LPSTR)lpbi, dwDibSize, &dwWritten, NULL);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(hFile);
//没有压缩保存
/*BITMAP bmp;
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmp);//获得位图信息
FILE *fp = fopen("c:\\screen.bmp", "w+b");
BITMAPINFOHEADER bih = {0};//位图信息头
bih.biBitCount = bmp.bmBitsPixel;//每个像素字节大小
bih.biCompression = BI_RGB;
bih.biHeight = bmp.bmHeight;//高度
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight;//图像数据大小
bih.biWidth = bmp.bmWidth;//宽度
BITMAPFILEHEADER bfh = {0};//位图文件头
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//到位图数据的偏移量
bfh.bfSize = bfh.bfOffBits + bmp.bmWidthBytes * bmp.bmHeight;//文件总的大小
bfh.bfType = (WORD)0x4d42;
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);//写入位图文件头
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);//写入位图信息头
byte * p = new byte[bmp.bmWidthBytes * bmp.bmHeight];//申请内存保存位图数据
GetDIBits(memDC, hBitmap, 0, bmp.bmHeight, p, (LPBITMAPINFO) &bih, DIB_RGB_COLORS);//获取位图数据
fwrite(p, 1, bmp.bmWidthBytes * bmp.bmHeight, fp);//写入位图数据
delete [] p;
fclose(fp);*/
//DeleteObject(hBitmap);
//hBitmap=NULL;
SelectObject(memDC,hOldBitmap);
DeleteObject( hOldBitmap );
hOldBitmap=NULL;
DeleteDC( memDC );
ReleaseDC( NULL, dstDC );
ReleaseDC( hwnd, srcDC );
}
return nRetCode;
}
测试发现果然无法截取到使用WindowBlinds模拟vista半透明主题的窗口栏,还有所有半透明的窗口!
发现原BitBlt最后一个参数组合上CAPTUREBLT就能解决这个问题:
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
);
不过在引用这个参数时貌似要先更新vc6的SDK,否则会提示CAPTUREBLT未定义,不过也可以手工来给它作个宏定义:
#ifndef CAPTUREBLT
#define CAPTUREBLT 0x40000000
#endif
int
_tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
_tprintf(_T("错误: MFC 初始化失败\n"));
nRetCode = 1;
}
else
{
// TODO: 在此处为应用程序的行为编写代码。
HWND hwnd = (HWND)::GetDesktopWindow();
HDC dstDC = ::GetDC(NULL);
HDC srcDC = ::GetWindowDC(hwnd); //full window (::GetDC(hwnd); = clientarea)
HDC memDC = ::CreateCompatibleDC(NULL);
CRect CaptureRect;
::GetWindowRect(hwnd,&CaptureRect);
int xScreen,yScreen;
//check if the window is out of the screen | maximixed <Qiang>
int xshift = 0, yshift = 0;
xScreen = GetSystemMetrics(SM_CXSCREEN);
yScreen = GetSystemMetrics(SM_CYSCREEN);
if(CaptureRect.right > xScreen)
CaptureRect.right = xScreen;
if(CaptureRect.bottom > yScreen)
CaptureRect.bottom = yScreen;
if(CaptureRect.left < 0){
xshift = -CaptureRect.left;
CaptureRect.left = 0;
}
if(CaptureRect.top < 0){
yshift = -CaptureRect.top;
CaptureRect.top = 0;
}
CSize sz(CaptureRect.Width(), CaptureRect.Height());
HBITMAP hBitmap = ::CreateCompatibleBitmap(dstDC, sz.cx, sz.cy);
HBITMAP hOldBitmap = (HBITMAP)::SelectObject(memDC,hBitmap);
::BitBlt(memDC, 0, 0, sz.cx, sz.cy, srcDC, xshift, yshift, SRCCOPY | CAPTUREBLT);
if (OpenClipboard(NULL))
{
//清空剪贴板
EmptyClipboard();
//把屏幕内容粘贴到剪贴板上,
//hBitmap 为刚才的屏幕位图句柄
SetClipboardData(CF_BITMAP, hBitmap);
//关闭剪贴板
CloseClipboard();
}
HDC hDC;//Device Context(设备环境、设备描述表、设备上下文)的句柄
//当前显示分辨率下每个像素所占字节数
WORD wBitCount = 8;//v_iBitCount
//计算调色板大小
DWORD dwPaletteSize = 0;
if(wBitCount <= 8)//小于八位按八位处理
dwPaletteSize = (1 << wBitCount) * sizeof(RGBQUAD);
//设置位图信息头结构
BITMAP bm;//位图属性结构
BITMAPINFOHEADER bi;//位图信息头结构
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bm.bmWidth;
bi.biHeight = bm.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
//位图中像素字节大小
DWORD dwBmpBitsSize = ((bm.bmWidth * wBitCount+31)/32)* 4 * bm.bmHeight;
//为位图内容分配内存
HANDLE hDib = GlobalAlloc(GHND, dwBmpBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
//处理调色板
HANDLE hPal = NULL;
HANDLE hOldPal = NULL;
hPal = GetStockObject(DEFAULT_PALETTE);
if(hPal)
{
hDC = ::GetDC(NULL);
hOldPal = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
RealizePalette(hDC);
}
//获取该调色板下新的像素值
GetDIBits(hDC, hBitmap, 0, (UINT)bm.bmHeight,
(LPSTR)lpbi + sizeof(BITMAPINFOHEADER) + dwPaletteSize,
(BITMAPINFO*)lpbi, DIB_RGB_COLORS);
//恢复调色板
if(hOldPal)
{
SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL, hDC);
}
HANDLE hFile = CreateFile(_T("c:\\screen.bmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if(hFile == INVALID_HANDLE_VALUE)
return FALSE;
//设置位图文件头
BITMAPFILEHEADER bf;//位图文件头结构
bf.bfType = 0x4d42;//"bm"
DWORD dwDibSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
dwPaletteSize + dwBmpBitsSize;//位图文件大小
bf.bfSize = dwDibSize;
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER)+ dwPaletteSize;
//写入位图文件头
DWORD dwWritten;//写入文件字节数
WriteFile(hFile, (LPSTR)&bf, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
//写入位图文件其余内容
WriteFile(hFile, (LPSTR)lpbi, dwDibSize, &dwWritten, NULL);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(hFile);
//没有压缩保存
/*BITMAP bmp;
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmp);//获得位图信息
FILE *fp = fopen("c:\\screen.bmp", "w+b");
BITMAPINFOHEADER bih = {0};//位图信息头
bih.biBitCount = bmp.bmBitsPixel;//每个像素字节大小
bih.biCompression = BI_RGB;
bih.biHeight = bmp.bmHeight;//高度
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight;//图像数据大小
bih.biWidth = bmp.bmWidth;//宽度
BITMAPFILEHEADER bfh = {0};//位图文件头
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//到位图数据的偏移量
bfh.bfSize = bfh.bfOffBits + bmp.bmWidthBytes * bmp.bmHeight;//文件总的大小
bfh.bfType = (WORD)0x4d42;
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);//写入位图文件头
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);//写入位图信息头
byte * p = new byte[bmp.bmWidthBytes * bmp.bmHeight];//申请内存保存位图数据
GetDIBits(memDC, hBitmap, 0, bmp.bmHeight, p, (LPBITMAPINFO) &bih, DIB_RGB_COLORS);//获取位图数据
fwrite(p, 1, bmp.bmWidthBytes * bmp.bmHeight, fp);//写入位图数据
delete [] p;
fclose(fp);*/
//DeleteObject(hBitmap);
//hBitmap=NULL;
SelectObject(memDC,hOldBitmap);
DeleteObject( hOldBitmap );
hOldBitmap=NULL;
DeleteDC( memDC );
ReleaseDC( NULL, dstDC );
ReleaseDC( hwnd, srcDC );
}
return nRetCode;
}
这样就能够截取到透明的窗体了