11.6.2 截图模块主窗体的设计与实现(2)
【代码解析】
第3行调用自定义函数GetSystemCursor()获取系统光标。第4行创建两个设备描述表,一个用于屏幕,另一个用于内存。第5、6行创建两个位图句柄,一个用于保存屏幕图像,一个用于保存设备位图。第7行定义4个变量用于保存选定区域的4个坐标。第10行创建屏幕设备描述表。第12行创建于屏幕设备描述表兼容的内存设备描述表。第13、14行判断当前选定区域的有效性。第16~19行获取选定区域的4个坐标。第21~30行使选中区域有效化。第32行创建一个与屏幕设备描述表兼容的位图,并在第34行将这个位图载入内存设备。如果当前调用此函数用于将截取的图像保存到剪切板,需要在第36~44行将截取的图像复制到内存设备上。
如果此函数仅仅将屏幕图像作为主窗体的背景,则在第47、48行将内存设备复制到屏幕上即可。第56、57行获取系统鼠标光标的坐标,第51行获取鼠标光标图像信息。第53~61行设置鼠标光标图像的显示信息。第63、64行将系统鼠标光标绘制到内存设备中相应的位置上。第65行恢复内存设备原位图资源,并将绘制好的位图保存到hBitmap变量中。第66、67行释放屏幕及内存设备描述表。如果需要保存图像到剪切板,则在第72行清空剪切板,第74行将准备好的位图hBitmap保存到剪切板上。第75行关闭剪切板。第78行返回绘制好的位图。
(3)自定义的函数GetSystemCursor()实现获取系统鼠标光标的功能,即使当前鼠标位于本实例以外的应用程序上,也可以正确地获取。实现如下:
- 01 void CCatchDlg::GetSystemCursor()
- 02 {
- 03 static HWND hTemp=0;
- 04 DWORD dwProcessID=0; //光标所在进程ID
- 05 POINT ptCursor; //光标位置
- 06 ::GetCursorPos(&ptCursor);
- 07 HWND hwnd=::WindowFromPoint(ptCursor); //获取光标所在窗体的句柄
- 08 if(hTemp!=hwnd) //光标所在窗体不为空
- 09 { //获取光标所在窗体的进程ID
- 10 DWORD dwThreadID=::GetWindowThreadProcessId(hwnd,&dwProcessID);
- 11 DWORD dwCurThreadID=::GetCurrentThreadId();
- 12 if(dwCurThreadID != dwThreadID) //光标不在本进程的窗体上
- 13 { //连接线程输入
- 14 ::AttachThreadInput(dwCurThreadID,dwThreadID,true);
- 15 m_capCursor =::GetCursor(); //获取光标
- 16 AttachThreadInput(dwCurThreadID, dwThreadID, FALSE);
- 17 }
- 18 else //光标就在本窗体上
- 19 { //获取光标
- 20 m_capCursor =GetCursor();
- 21 }
- 22 }
- 23 }
【代码解析】
第5、6行获取当前光标的位置。第7行根据光标所在位置,获取光标所在窗体的句柄。第8行判断窗体句柄是否获取成功,如是则在第10行获取光标所在窗体对应的进程ID。第11行获取当前进程的ID。第12行判断鼠标是否在当前进程中,如是则在第20行直接获取光标,否则在第14行将当前进程与光标所在进程进行资源共享,然后在第15行获取鼠标光标,最后在第16行关闭两线程的资源共享。第14行的AttachThreadInput()函数实现了两个线程资源共享的功能,共享的资源包括输入焦点、窗口激活、鼠标捕获、键盘状态及输入队列状态。函数原型如下:
- BOOL WINAPI AttachThreadInput(
- DWORD idAttach, //欲连接线程的标识符(ID)
- DWORD idAttachTo, //与idAttach线程连接的另一个线程的标识符
- BOOL fAttach //TRUE(非零)表示连接,FALSE表示撤销连接
- );
(4)重载窗体的OnEraseBkgnd()函数,将CopyScreenToBitmap()函数获取的屏幕图像作为窗体的背景。 - 01 BOOL CCatchDlg::OnEraseBkgnd(CDC* pDC)
- 02 {
- 03 CDC dcCompatible;
- 04 dcCompatible.CreateCompatibleDC(pDC); //创建与当前设备兼容的内存设备
- 05 dcCompatible.SelectObject(m_pBitmap); //将位图载入内存设备
- 06 CRect rect;
- 07 GetClientRect(&rect); //获取客户区大小
- 08 //将内存设备复制到当前设备
- 09 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,
- 0,0,SRCCOPY);
- 10 return TRUE;
- 11 }
【代码解析】
第3、4行创建一个与当前设备兼容的内存设备,第5行将绘制好的位图资源选入内存设备。第6、7行获取当前窗体客户区的大小。第9行将内存设备上的内容复制到窗体上。m_pBitmap即保存屏幕图像的位图。
(5)在窗体的初始化函数中设置窗体为全屏显示,并且位于屏幕的最上层。调用消息框设置函数,初始化消息框。实现如下:
- 01 BOOL CCatchDlg::OnInitDialog()
- 02 {
- 03 CDialog::OnInitDialog();
- 04 //把对化框设置成全屏顶层窗口
- 05 int m_xScreen = GetSystemMetrics(SM_CXSCREEN);
- 06 int m_yScreen = GetSystemMetrics(SM_CYSCREEN);
- 07 ::SetWindowPos(this->m_hWnd,HWND_TOPMOST,0,0,
- 08 m_xScreen,m_yScreen,SWP_SHOWWINDOW);
- 09 CRect rect;
- 10 m_eTip.GetWindowRect(&rect); //移动操作提示窗口
- 11 m_eTip.MoveWindow(10,10,rect.Width(),rect.Height());
- 12 SetTip(); //显示操作提示窗口文字
- 13 //捕获按键消息窗口,将对话框的句柄传递到CCatchScreenApp中
- 14 ((CScreenShotsApp *)AfxGetApp())->m_hwndDlg=m_hWnd;
- 15 return TRUE; //除非某个控件获取焦点,否则返回TRUE
- 16 }