学习编程也有一段时间了,一开始想过抓屏是怎么做的。经过一段时间积累,确实有所长进呵。
首先抓屏我认为分两个部份:首先取得某个窗口的句柄进而取得该窗口DC内容;其次由该窗口设备DC内容生成BMP文件。
我觉得这两个部份都是很重要的,因为不了解BMP文件结构就不可能创建标准BMP文件,还有window的设备DC,windows绘图有关的操作使用DC实现。Device Context。
- int GetBmpPixels()
- {
- int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);//屏幕尺寸
- int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
- HWND hDesktopWnd = GetDesktopWindow();//获得桌面窗口句柄
- HDC hDesktopDC = GetDC(hDesktopWnd); //获得对应桌面窗口的设备DC
- HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);//创建内存设备兼容DC
- HBITMAP hCaptureBitmap =CreateCompatibleBitmap(hDesktopDC, nScreenWidth, nScreenHeight);//创建内存兼容位图
- SelectObject(hCaptureDC,hCaptureBitmap);将位图和设备DC相联
- BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,hDesktopDC,0,0,SRCCOPY);//拷贝桌面内容到内存DC中
- if (OpenClipboard(m_hWnd)) //m_hWnd为窗口句柄
- {
- //清空剪贴板
- EmptyClipboard();
- //把屏幕内容粘贴到剪贴板上,
- SetClipboardData(CF_BITMAP,hCaptureBitmap);
- //关闭剪贴板
- CloseClipboard();
- }
- //保存到文件
- SaveBitmapToFile(hCaptureBitmap,_T("src.bmp"));
- ::ReleaseDC(hDesktopWnd,hDesktopDC);
- DeleteDC(hCaptureDC);
- DeleteObject(hCaptureBitmap);
- }
- int SaveBitmapToFile(HBITMAP hBitmap ,LPCTSTR lpFileName) //hBitmap 为刚才的屏幕位图句柄
- { //lpFileName 为位图文件名
- HDC hDC;
- //设备描述表
- int iBits;
- //当前显示分辨率下每个像素所占字节数
- WORD wBitCount;
- //位图中每个像素所占字节数
- //定义调色板大小, 位图中像素字节大小 , 位图文件大小 , 写入文件字节数
- DWORD dwPaletteSize=0,dwBmBitsSize, dwDIBSize,dwWritten;
- BITMAP Bitmap;
- //位图属性结构
- BITMAPFILEHEADER bmfHdr;
- //位图文件头结构
- BITMAPINFOHEADER bi;
- //位图信息头结构
- LPBITMAPINFOHEADER lpbi;
- //指向位图信息头结构
- HANDLE fh, hDib, hPal,hOldPal=NULL;
- //定义文件,分配内存句柄,调色板句柄
- //计算位图文件每个像素所占字节数
- hDC = CreateDC(_T("DISPLAY"),NULL,NULL,NULL);
- iBits = GetDeviceCaps(hDC,BITSPIXEL)* GetDeviceCaps(hDC,PLANES);
- DeleteDC(hDC);
- if (iBits <= 1)
- wBitCount = 1;
- else if (iBits <= 4)
- wBitCount = 4;
- else if (iBits <= 8)
- wBitCount = 8;
- else if (iBits <= 24)
- wBitCount = 24;
- else
- wBitCount = 32;
- //计算调色板大小
- if (wBitCount <= 8)
- dwPaletteSize = (1<< wBitCount)*sizeof(RGBQUAD);
- //设置位图信息头结构
- GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
- bi.biSize = sizeof(BITMAPINFOHEADER);
- bi.biWidth = Bitmap.bmWidth;
- bi.biHeight = Bitmap.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;
- dwBmBitsSize = ((Bitmap.bmWidth*wBitCount+31)/32)*4*Bitmap.bmHeight;
- //为位图内容分配内存
- hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));
- lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
- *lpbi = bi;
- // 处理调色板
- hPal = GetStockObject(DEFAULT_PALETTE);
- if (hPal)
- {
- hDC = GetDC(NULL);
- hOldPal = SelectPalette(hDC,(HPALETTE)hPal,FALSE);
- RealizePalette(hDC);
- }
- // 获取该调色板下新的像素值
- GetDIBits(hDC,hBitmap,0,(UINT)Bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwPaletteSize, (LPBITMAPINFO)lpbi, DIB_RGB_COLORS);
- //恢复调色板
- if (hOldPal)
- {
- SelectPalette(hDC,(HPALETTE)hOldPal, TRUE);
- RealizePalette(hDC);
- ReleaseDC(NULL, hDC);
- }
- //创建位图文件
- fh = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
- if (fh == INVALID_HANDLE_VALUE)
- return FALSE;
- // 设置位图文件头
- bmfHdr.bfType = 0x4D42; // "BM"
- dwDIBSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize;
- bmfHdr.bfSize = dwDIBSize;
- bmfHdr.bfReserved1 = 0;
- bmfHdr.bfReserved2 = 0;
- bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwPaletteSize;
- // 写入位图文件头
- WriteFile(fh,(LPSTR)&bmfHdr,sizeof(BITMAPFILEHEADER),&dwWritten,NULL);
- // 写入位图文件其余内容
- WriteFile(fh,(LPSTR)lpbi,dwDIBSize,&dwWritten,NULL);
- //清除
- GlobalUnlock(hDib);
- GlobalFree(hDib);
- CloseHandle(fh);
- return 0;
- }
至此程序已经能够将桌面整个屏幕保存成一个位图文件了。总结一下:
1.获得桌面窗口句柄 :GetDesktopWindow()
2.取得桌面窗口设备DC :GetDC(hDesktopWnd)
3.创建与设备兼容DC和位图,并将DC与位图绑定,将窗口设备DC内容拷贝到桌面窗口兼容DC中:
CreateCompatibleDC(hDesktopDC);
CreateCompatibleBitmap(hDesktopDC, nScreenWidth, nScreenHeight);
SelectObject(hCaptureDC,hCaptureBitmap);
BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,hDesktopDC,0,0,SRCCOPY);
4.打开剪切板并将DC信息粘贴到剪切板中
OpenClipboard(m_hWnd) //hWnd为程序窗口句柄
//清空剪贴板
EmptyClipboard();
//把屏幕内容粘贴到剪贴板上,
SetClipboardData(CF_BITMAP,hCaptureBitmap);
//关闭剪贴板
CloseClipboard();
5. 保存到位图文件
SaveBitmapToFile(hCaptureBitmap,_T("src.bmp"));
6.释放资源
ReleaseDC(hDesktopWnd,hDesktopDC);
DeleteDC(hCaptureDC);
DeleteObject(hCaptureBitmap);
//
过程即此,剩下仍有很多要做,现在只是简单的桌面窗口抓屏,可以提供接口实现多种窗口的抓屏,这些也是很多抓屏软件已经实现的。还没有预览和文件保存接口,再进一步即多种格式图片文件的支持。