当前位置: 源码素材网 » VC++教程 » 详情页

VC++ 屏幕抓图代码实例+实现讲解

  •   时间:2019-03-13
  • 概述:抓图 截屏

本屏幕抓图程序是利用VC++通用的热键管理DLL 实现的热键功能,在收到热键通知后截取屏幕的内容并保存到BMP 文件中。单击【浏览…】按钮可以改变保存文件的路径,而单击【设置】按钮则可以改变截取屏幕的热键。

实现方法:在一个DLL(热键管理DLL)中安装一个键盘钩子,用另一个程序(Capture)通过这个DLL 来增加、修改、删除热键。当用户按了抓取屏幕的热键后,热键管理DLL 通知Capture 抓取屏幕,并保存位图。程序代码:

1.制作热键管理DLL:这个DLL 实际上是一个键盘钩子,由它来监视系统的键盘事件。如果有和程序登记符合的按键和按键组合就通知该程序的窗口。为了应用方便,把它做成了一个标准的管理库来为其他的程序通过热键服务,它有两个输出函数AddHotkey 和DeleteHotkey,程序只需要调用这两个函数即可,如果编译之后不用改变热键,则只需要AddHotkey 即可。

(1)全局变量,所有的全局变量都放在一个共享段中,定义如下:

#pragma data_seg("shareddata")
HHOOK hHook =NULL; //钩子句柄
UINT nHookCount =0; //挂接的程序数目
static UCHAR HotKey[MAX_KEY] = {0}; //热键虚拟键码
static UCHAR HotKeyMask[MAX_KEY] = {0}; //组合掩码, control=4,alt=2,shift=1
static HWND hCallWnd[MAX_KEY] = {0}; //window handle associated with hotkey
static int KeyCount =0;
static UCHAR MaskBits=0;//00000 Ctrl=4&Alt=2&Shift=1
#pragma data_seg()

关于共享段,有几点重要的说明:

A. 第一种必须在链接选项里指定该段为共享, projectsettingslinkobject/library , 加上/section:shareddata,rws。
B.第二种指定共享段的方法:在DLF 文件的sections 里加上一句shareddata read write shared。
C. 另外还有第三种指定共享段的方法: 需要在程序里加上一句“ #pragma comment(linker,"section:shareddata,rws")”。
D.所有的变量必须初始化,否则链接程序会把它放到普通数据段。
E.如果不初始化变量,需要在段外用“__declspec(allocate("shareddata")) 变量类型 变量名”的方式定义。

(2)两个输出函数

//添加热键
BOOL __declspec(dllexport) __stdcall AddHotkey(HWND hWnd,UCHAR cKey,UCHAR cMask)
{
BOOL bAdded=FALSE;
for(int index=0;index<MAX_KEY;index++){
if(hCallWnd[index]==0){
hCallWnd[index]=hWnd;
HotKey[index]=cKey;
HotKeyMask[index]=cMask;
bAdded=TRUE;
KeyCount++;
break;
}
}
return bAdded;
}
//删除热键
BOOL __declspec(dllexport) __stdcall DeleteHotkey(HWND hWnd,UCHAR cKey,UCHAR cMask)
{
BOOL bRemoved=FALSE;
for(int index=0;index<MAX_KEY;index++){
if(hCallWnd[index]==hWnd){
if(HotKey[index]==cKey&&HotKeyMask[index]==cMask){
hCallWnd[index]=NULL;
HotKey[index]=0;
HotKeyMask[index]=0;
bRemoved=TRUE;
KeyCount--;
break;
}
}
}
return bRemoved;
}

钩子函数如下:

LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
BOOL bProcessed=FALSE;
if(HC_ACTION==nCode)
{
if((lParam&0xc0000000)==0xc0000000){// Key up
switch(wParam)
{
case VK_MENU:
MaskBits&=~ALTBIT;
break;
case VK_CONTROL:
MaskBits&=~CTRLBIT;
break;
case VK_SHIFT:
MaskBits&=~SHIFTBIT;
break;
default: //judge the key and send message
break;
}
for(int index=0;index<MAX_KEY;index++){
if(hCallWnd[index]==NULL)
continue;
if(IsWindow(hCallWnd[index])&&(HotKey[index]==wParam)&&(HotKeyMask[index]==MaskBits)){
SendMessage(hCallWnd[index],WM_HOTKEY,wParam,WM_KEYUP);
bProcessed=TRUE;
}
}
}
else if((lParam&0xc000ffff)==1){ //Key down
switch(wParam)
{
case VK_MENU:
MaskBits|=ALTBIT;
break;
case VK_CONTROL:
MaskBits|=CTRLBIT;
break;
case VK_SHIFT:
MaskBits|=SHIFTBIT;
break;
default: //judge the key and send message
break;
}
for(int index=0;index<MAX_KEY;index++){
if(hCallWnd[index]==NULL)
continue;
if(IsWindow(hCallWnd[index])&&(HotKey[index]==wParam)&&(HotKeyMask[index]==MaskBits)){
SendMessage(hCallWnd[index],WM_HOTKEY,wParam,WM_KEYDOWN);
bProcessed=TRUE;
}
}
}
if(!bProcessed){
for(int index=0;index<MAX_KEY;index++){
if(hCallWnd[index]==NULL)
continue;
if(IsWindow(hCallWnd[index])&&(HotKey[index]==0)&&(HotKeyMask[index]==0))
SendMessage(hCallWnd[index],WM_HOTKEY,WM_HOTKEY,lParam);
}
}
}
return CallNextHookEx( hHook, nCode, wParam, lParam );
}

这样,热键管理的DLL 就做好了,接下来可以在程序里调用AddHotkey。

2.抓图程序,抓图程序是一个基于对话框的程序,它在建立对话框的时候调用前面的DLL,登记热键。

(1)增加hook.lib 到工程里,在程序里给出两个DLL 函数的定义,也可以写个头文件,再包含进来。

BOOL __declspec(dllexport)__stdcall AddHotkey(HWND,UCHAR key,UCHAR mask);
BOOL __declspec(dllexport)__stdcall DeleteHotkey(HWND,UCHAR key,UCHAR mask);

(2)添加对热键通知事件的响应,程序中用的办法是重载WindowProc。

LRESULT CCaptureDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if(message==WM_HOTKEY&&lParam==WM_KEYDOWN){
SaveBmp();
return FALSE;
}
if(message==IDM_SHELL&&lParam==WM_RBUTTONUP){
CMenu pop;
pop.LoadMenu(MAKEINTRESOURCE(IDR_MENU1));
CMenu*pMenu=pop.GetSubMenu(0);
pMenu->SetDefaultItem(ID_EXITICON);
CPoint pt;
GetCursorPos(&pt);
int
id=pMenu->TrackPopupMenu(TPM_RIGHTALIGN|TPM_NONOTIFY|TPM_RETURNCMD|TPM_LEFTBUTTON,
pt.x,pt.y,this);
if(id==ID_EXITICON)
DeleteIcon();
else if(id==ID_EXIT)
OnCancel();
return FALSE;
}
LRESULT res= CDialog::WindowProc(message, wParam, lParam);
if(message==WM_SYSCOMMAND&&wParam==SC_MINIMIZE)
AddIcon();
return res;
}

(3)保存屏幕。保存屏幕由函数SaveBmp 完成,如下:

void CCaptureDlg::SaveBmp()
{
CDC dc;
dc.CreateDC("DISPLAY",NULL,NULL,NULL);
CBitmap bm;
int Width=GetSystemMetrics(SM_CXSCREEN);
int Height=GetSystemMetrics(SM_CYSCREEN);
bm.CreateCompatibleBitmap(&dc,Width,Height);
CDC tdc;
tdc.CreateCompatibleDC(&dc);
CBitmap*pOld=tdc.SelectObject(&bm);
tdc.BitBlt(0,0,Width,Height,&dc,0,0,SRCCOPY);
tdc.SelectObject(pOld);
BITMAP btm;
bm.GetBitmap(&btm);
DWORD size=btm.bmWidthBytes*btm.bmHeight;
LPSTR lpData=(LPSTR)GlobalAllocPtr(GPTR,size);
/////////////////////////////////////////////
BITMAPINFOHEADER bih;
bih.biBitCount=btm.bmBitsPixel;
bih.biClrImportant=0;
bih.biClrUsed=0;
bih.biCompression=0;
bih.biHeight=btm.bmHeight;
bih.biPlanes=1;
bih.biSize=sizeof(BITMAPINFOHEADER);
bih.biSizeImage=size;
bih.biWidth=btm.bmWidth;
bih.biXPelsPerMeter=0;
bih.biYPelsPerMeter=0;
///////////////////////////////////
GetDIBits(dc,bm,0,bih.biHeight,lpData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
// bm.GetBitmapBits(size,lpData); //此函数在处理5-5-5 模式的16 位色下会出现颜色混乱
//////////////////////////////
static int filecount=0;
CString name;
name.Format("pict%04d.bmp",filecount++);
name=m_Path+name;
BITMAPFILEHEADER bfh;
bfh.bfReserved1=bfh.bfReserved2=0;
bfh.bfType=((WORD)(’M’<< 8)|’B’);
bfh.bfSize=54+size;
bfh.bfOffBits=54;
CFile bf;
if(bf.Open(name,CFile::modeCreate|CFile::modeWrite)){
bf.WriteHuge(&bfh,sizeof(BITMAPFILEHEADER));
bf.WriteHuge(&bih,sizeof(BITMAPINFOHEADER));
bf.WriteHuge(lpData,size);
bf.Close();
nCount++;
}
GlobalFreePtr(lpData);
if(nCount==1)
m_Number.Format("%d picture captured.",nCount);
else
m_Number.Format("%d pictures captured.",nCount);
UpdateData(FALSE);
}
BOOL CCaptureDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg -> message == WM_KEYDOWN)
{
if(pMsg -> wParam == VK_ESCAPE)
return TRUE;
if(pMsg -> wParam == VK_RETURN)
return TRUE;
}
return CDialog::PreTranslateMessage(pMsg);
}

注意设置热键时,不要和其他程序冲突。

    相关声明:

      若“VC++ 屏幕抓图代码实例+实现讲解”有损您的权益,请告之我们删除内容。
      部分文章来源于网络,版权归原作者所有。