2012-03-19 20:42:56|?次阅读|上传:wustguangh【已有?条评论】发表评论
关键词:C/C++, MFC, 操作系统, 图形/图像|来源:唯设编程网
在实际项目中,经常会用到截屏的功能,于是自己使用VC编写了一个实现截屏功能的MFC DLL,以备随时可以使用。该项目实现了屏幕指定区域截图、全屏用户交互式截图、保存截图结果到位图(BMP)文件等功能,下面分步骤详细介绍具体的设计方案和实现代码。
首先显示程序的运行效果:

1. 创建一个名称为ScreenSnap的MFC DLL项目到解决方案中,DLL类型选择MFC扩展DLL;
2. 在项目ScreenSnap中创建类ScreenShutter,用于提供用户调用屏幕截图操作的接口。该类主要实现屏幕截图的基本功能:保存屏幕截图到位图(BMP)文件、返回指定范围的截图、返回截图的完整区域位图。
示例:
class AFX_EXT_CLASS CScreenShutter
{
private:
CDC ScrDC; //显示器DC
public:
CBitmap* m_pBitmap;//存储背景图片(成员变量)
public:
CScreenShutter(void);
~CScreenShutter(void);
// 保存m_pBackBitmap中储存的位图数据到磁盘文件中
BOOL SaveBitmapToFile(LPCWSTR lpFileName);
// 截图屏幕指定区域
// 结果保存在成员变量m_pBackBitmap中
CScreenShutter* getScreenBitmap(POINT pt1,POINT pt2);
// 截图屏幕完整区域
// 结果保存在成员变量m_pBackBitmap中
CScreenShutter* getScreenBitmap();
};
备注:在CScreenShutter前面增加AFX_EXT_CLASS使该类为DLL的导出类。
示例:
CScreenShutter::CScreenShutter(void)
:m_pBitmap(NULL)
{
}
CScreenShutter::~CScreenShutter(void)
{
//清除位图资源
if (m_pBitmap != NULL)
{
delete m_pBitmap;
m_pBitmap = NULL;
}
}
示例:
// 把Cbitmap对象保存到文件中
// lpFileName 为位图文件名
BOOL CScreenShutter::SaveBitmapToFile(LPCWSTR lpFileName)
{
//设备描述表
HDC hDC;
//当前分辨率下每象素所占字节数
int iBits;
//位图中每象素所占字节数
WORD wBitCount;
//定义调色板大小, 位图中像素字节大小 ,位图文件大小 , 写入文件字节数
DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;
//位图属性结构
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 wBitCount = 24;
GetObject(*m_pBitmap, 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.biClrImportant = 0;
bi.biClrUsed = 0;
dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight;
//为位图内容分配内存
hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
if(hDib==NULL)
return false;
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, *m_pBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER)
+dwPaletteSize, (BITMAPINFO *)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 TRUE;
}
该函数将CBitmap类型的成员指针m_pBitmap所指的位图数据保存到磁盘文件,实现了CBitmap保存到磁盘文件的通用方法,实现过程比较麻烦,读者可以在其它项目直接重用该段代码。
// 截图屏幕指定区域
// 结果保存在成员变量m_pBackBitmap中
CScreenShutter* CScreenShutter::getScreenBitmap(POINT pt1,POINT pt2){
//NEW资源(调用一次重新拷贝一次)
if (m_pBitmap != NULL)
{
delete m_pBitmap;
m_pBitmap = NULL;
}
m_pBitmap = new CBitmap();
CDC ScrDC,MemDC;
ScrDC.CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
MemDC.CreateCompatibleDC(&ScrDC);
//创建位图
m_pBitmap->CreateCompatibleBitmap(&ScrDC,abs(pt1.x-pt2.x),abs(pt1.y-pt2.y));
MemDC.SelectObject(m_pBitmap);
//拷贝屏幕的区域到创建的位图中
int minX=pt1.x<pt2.x?pt1.x:pt2.x;
int minY=pt1.y<pt2.y?pt1.y:pt2.y;
MemDC.BitBlt(0, 0, abs(pt1.x-pt2.x), abs(pt1.y-pt2.y),&ScrDC,minX, minY,SRCCOPY);
ScrDC.DeleteDC();
MemDC.DeleteDC();
return this;
}
该函数使用设备描述表CDC实现屏幕截图,并将截图结果保存在pBitmap所指的位图存储空间中。调用CreateDC创建一个特定的设备描述表,调用CBitmap的成员函数CreateCompatibleBitmap初始化与对应设备描述表兼容的位图到m_pBitmap所指的位图空间。最后调用BitBlt实现将ScrDC设备描述表指定区域的位图拷贝到MemDC设备描述表。
// 交互式截图
// 结果保存在成员变量m_pBackBitmap中
CScreenShutter* CScreenShutter::getScreenBitmap(){
//显示位图
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CScreenSnapDlg dlg(m_pBitmap);
dlg.DoModal();
return this;
}
特别注意:由于在MFC扩展DLL中使用了自定义的对话框类,在调用前请添加语句
AFX_MANAGE_STATE(AfxGetStaticModuleState());
该类调用屏幕截图对话框CScreenSnapDlg实现全屏用户交互式截图,限于篇幅,CScreenSnapDlg类我们将在下一篇文章中进行介绍。