Rover12421's Blog

The End.

手把手教你写截图软件

其实写这个程序是受到尘封同学的启发,他写了个窗口截图程序,可是问题在于只能截窗口,似乎和我们常用的QQ截图不一样,由于我当时在网吧,身上没有U盘,没能拷回去研究,所以如果什么地方说错了敬请尘封同学原谅!
他在留言中给出了一个关键函数,里面首先用到了GetForegroundWindow,我回去查了下MSDN,结果如下:The GetForegroundWindow function returns a handle to the foreground window (the window with which the user is currently working). The system assigns a slightly higher priority to the thread that creates the foreground window than it does to other threads. 通俗的讲就是获取一个当前激活的窗口句柄
Return Values
The return value is a handle to the foreground window. The foreground window can be NULL in certain circumstances, such as when a window is losing activation
这里我发现一个问题,也就是函数可能返回null,在某种情况下。所以我想尘封的窗口截图不一定能每次成功
出于兴趣,我写一个类似QQ的截图程序(当时想了也觉得不怎么难,就是用几个GDI函数),现在就当给初学者点动力(你们完全有能力写),手把手教你们(大牛别笑我!)
首先,先思考截图原理是什么样的? 我做过这样一个实验,我先运行我的笑脸程序,然后启动浏览器的截图功能,这时笑脸静止了,而当取消截图时,笑脸又开始运动了。这说明在截图的过程中整个画面是静止的。
现在的问题是,如何使整个画面静止?方法很简单:先创建一个内存DC保存当前桌面背景,然后创建一个WS_POPUP风格的全屏窗口,并把这个窗口的背景图案设置为先前创建的内存DC。
好了,现在就等于在一个窗口上截图了,这个还不简单?只要记录拖放鼠标的起点位置和终点位置,不就知道要截的是哪块区域了吗? 这里还有个问题,大家记不记得再用QQ截图时,拖放鼠标会出现一个矩形,我们当然也要画一个矩形,这里用DrawFocusRect(具体用法参照MSDN)
接着就是把这个截图区域保存下来,然后让那个全屏窗口自动退出,最后把这个截图显示在我们的窗口中,好了大功告成!
关于我的程序,我有几点想说
1.菜单项中只有“截图”有效,其他菜单项消息我并没有处理
2.我本来也想保存为图片,但尘封用的那个函数我在MSDN中居然没查到,所以如果你有兴趣可以尝试着做
3.拖放矩形时如果选择了一点拖,就只能往这一点的斜下方拖(因为我预先把起点保存在Rect.top和Rect.left,也就是规定了起点是左上角)
4.不能截视频(以为截图原理不一样,你用QQ也是不能截的)
写完这个程序我突然想到了点事情,有时候一个有用有趣的软件,并不需要太多高深的技术(我这话不是否认算法的重要性),他有时候需要的是一种创意。你说QQ截图给我们日常生活中带来多少欢乐,多少便利!但是说到底,也就是那么几个GDI函数? 我们不是科学家,我们所做的也许更多在于方便用户,所以观众身边人,关注他们需要什么,也许灵感就会降落在你头上! 想想“搜狗拼音”,其实技术早就存在了,只是没人应用到这打字上来,但是终归有人是有心人,做了尝试(据说这人一开始到百度遭拒,后来被搜狐相中),后来搜狗就像那一夜春风,千树万树梨花开
至于细节下次再说吧,看看效果:

image

image

image

首先我要声明,当时我写这个程序的时候用的是WIN32汇编,不是c语言(这也是为什么我不贴代码的原因),但是在下面的叙述中,为了方便大家学习,我采用c语言描述,里面也许会出现语法问题(本人未用过c写windows程序),希望大家不要在意这些,抓住整体,把握主要思路就行了谢谢!
创建程序的主窗口如下图所示:

image

1.在注册主程序窗口类时我们要做下面一件事
;*********************************
;创建位图画刷(主要是为了美观)
;*
********************************
hPatternBrush = CreatePatternBrush(LoadBitmap,hInstance,IDB_BACK)
stWndClass.hbrBackground = hPatternBrush
2.在这个主窗口的WM_CREATE消息中,我们要做一下几件事情:
;*********************************
;获得屏幕大小
;*
********************************
dwWidth = GetSystemMetrics,SM_CXSCREEN  ;屏幕宽   
dwHeight = GetSystemMetrics,SM_CYSCREEN  ;屏幕高
dwCutWidth = WINDOW_SIZE   ;初始化截图宽和高(WINDOW_SIZE)为窗口大小
dwCutHeight = WINDOW_SIZE
;*********************************
;创建屏幕背景DC和截图DC
;*
********************************
hScreenDC = GetDC,NULL    ;获取屏幕DC
hBkDC = CreateCompatibleDC,hScreenDC  ;创建与屏幕DC相兼容的内存DC(用来保存整个桌面背景)
hCutDC = CreateCompatibleDC,hScreenDC  ;同上(不过是用来保存截图)
hBmp = CreateCompatibleBitmap,hScreenDC,dwWidth,dwHeight ;创建一副位图然后选入DC,这里要注意CreateCompatibleBitmap的第一个参数要用GetDC的返回
SelectObject,hBkDC,hBmp      ;值,而不能用CreateCompatibleDC的放回值
DeleteObject,hBmp
hBmp = CreateCompatibleBitmap,hScreenDC,dwWidth,dwHeight
SelectObject,hCutDC,hBmp
DeleteObject,hBmp
;*********************************
;使用位图画刷
;*
********************************
SelectObject,hCutDC,hPatternBrush
PatBlt,hCutDC,0,0,WINDOW_SIZE,WINDOW_SIZE,PATCOPY       ;因为在截图之前截图DC中什么图案也没,所以为了美观 ,我们先用画刷图案填充下
以上就是我们要在WM_CREATE中要做的事情,我们可以把他们统一写到Init函数中,然后调用
3.处理WM_PAINT消息:
hDc = BeginPaint,hWnd,&stPS
BitBlt,hDc,0,0,dwCutWidth,dwCutHeight,hCutDC,0,0,SRCCOPY  ;就是通过这个BitBlt,把图案显示到窗口上(其实还可以再处理下stPS结构,因为只要处理无效区
EndPaint,hWnd,&stPS
接下来就是响应窗口中截图选项:
1.通过另一个线程来创建全屏窗口:CreateThread,NULL,NULL,offset _CreateBk ,NULL,NULL,NULL   ;_CreateBk为线程函数(似乎高级语言中不建议直接用CreateThread)
在这个函数中我们要做下面几件事情:
;*********************************
;创建一个窗口用来静态保存桌面
;*
********************************
BitBlt,hBkDC,0,0,dwWidth,dwHeight,hScreenDC,0,0,SRCCOPY         ;把整个屏幕图像画到屏幕DC中
hWin = CreateWindowEx,WS_EX_TOPMOST,offset szScreenClass,NULL,WS_POPUP,0,0,dwWidth,dwHeight,NULL,NULL,hInstance,NULL ;创建全屏窗口
ShowWindow,hWin,SW_MAXIMIZE ;最大化显示
UpdateWindow,hWin
;*********************************
;消息循环
;*
********************************
这里略(因为和之前的没有任何区别)
2.全屏窗口消息的处理
这里最重要的有4个消息:WM_RBUTTONDOWN,WM_LBUTTONDOWN,WM_LBUTTONUP,WM_MOUSEMOVE
当选择截图项后,若我们立马点了鼠标右键(WM_RBUTTONDOWN),就说明要退出截图(也就是要退出全屏窗口),我们要这样子做:
SendMessage,hWin,WM_CLOSE,0,0    ;hWin为全屏窗口的句柄
dwFlag = 0      ;截图标志复位
当选择截图项后,若我们点了鼠标左键(WM_LBUTTONUP),就说明要开始截图了,我们要这样子做:
dwFlag = 1      ;截图标志置位
GetCursorPos,&stPoint     ;获取截图起点位置(并保存在左上角,这样导致就只能往“下”拖)
stRect.left = stPoint.x
stRect.top = stPoint.y
在拖动矩形框的时候我们要响应WM_MOUSEMOVE消息:(DrawFocusRect自己MSDN,但同一个矩形DrawFocusRect两次的话,原来线框会消息,因为它采用异或方式画虚线框)
if dwFlag      ;只有开始截图了(dwFlag置位时),才响应WM_MOUSEMOVE的消息
{
if dwFlag > 1
  DrawFocusRect,hScreenDC,&stRect  ;取消上次画的矩形
GetCursorPos,&stPoint    ;当前矩形的右下角
stRect.right = stPoint.x
stRect.bottom = stPoint.y
DrawFocusRect,hScreenDC,addr stRect  ;画当前矩形
dwFlag++
}
鼠标一直拖动拖动,当鼠标左键弹起时,说明截图结束,我们要响应WM_LBUTTONUP消息:
dwFlag = 0      ;复位截图标志,截图停止就不需要想要WM_MOUSEMOVE消息了
GetCursorPos,&stPoint     ;获取截图终点位置(右下角)
mov eax,@stPoint.x     ;获取截图的宽
sub eax,stRect.left   
mov dwCutWidth,eax
mov eax,@stPoint.y     ;获取截图的高(偷懒下,不转换为c语言了)
sub eax,stRect.top
mov dwCutHeight,eax
BitBlt,hCutDC,0,0,dwCutWidth,dwCutHeight,hBkDC,stRect.left,stRect.top,SRCCOPY  ;把截图图案画到截图DC中去
InvalidateRect,hWinMain,NULL,TRUE       ;强迫主窗口无效,为了显示截图图案
SendMessage,hWin,WM_CLOSE,0,0        ;退出全屏窗口
大体就是这样,其他还有些细节的工作,希望大家自己完成!

来源:http://www.rupeng.com/forum/thread-4243-1-1-uid47584.html

尘封的截图,主要源代码

void ScreenCatch(WCHAR *filename)
{
HWND hw= GetForegroundWindow(); //得到最前面的窗口句柄
HDC hdc=GetWindowDC(hw); //得到窗口的HDC
RECT rt;
GetClientRect(hw,&rt);//得到窗口的大小
rt.right+=6;
rt.bottom+=30;
HDC hBackdc= CreateCompatibleDC(hdc); 创建一个背面缓冲区
HBITMAP hbm= CreateCompatibleBitmap(hBackdc,rt.right-rt.left,rt.bottom-rt.top);
HBITMAP hbm1=CreateCompatibleBitmap(hdc,rt.right-rt.left,rt.bottom-rt.top);
SelectObject(hdc,hbm);
SelectObject(hBackdc,hbm1);//把bitmap 载入到DC中
 
BitBlt(hBackdc,0,0,rt.right,rt.bottom,hdc,0,0,SRCCOPY);
 
CLSID encoderClsid;
 
Bitmap bitmap (hbm1,NULL);//GDI+的对象保存成jpg图片很方便
 
GetEncoderClsid(L"image/jpeg",&encoderClsid); 
bitmap.Save(filename, &encoderClsid, NULL);
PlaySound(MAKEINTRESOURCE(IDI_OKSOUND),g_hIn,SND_RESOURCE);
DeleteObject(hbm);
DeleteObject(hbm1);
ReleaseDC(NULL,hBackdc);
ReleaseDC(NULL,hdc);
}

原帖:http://www.rupeng.com/forum/thread-2041-1-1-uid47584.html

   1:  void ScreenCatch(WCHAR *filename)
   2:  {
   3:  HWND hw= GetForegroundWindow(); //得到最前面的窗口句柄
   4:  HDC hdc=GetWindowDC(hw); //得到窗口的HDC
   5:  RECT rt;
   6:  GetClientRect(hw,&rt);//得到窗口的大小
   7:  rt.right+=6;
   8:  rt.bottom+=30;
   9:  HDC hBackdc= CreateCompatibleDC(hdc); 创建一个背面缓冲区
  10:  HBITMAP hbm= CreateCompatibleBitmap(hBackdc,rt.right-rt.left,rt.bottom-rt.top);
  11:  HBITMAP hbm1=CreateCompatibleBitmap(hdc,rt.right-rt.left,rt.bottom-rt.top);
  12:  SelectObject(hdc,hbm);
  13:  SelectObject(hBackdc,hbm1);//把bitmap 载入到DC中
  14:   
  15:  BitBlt(hBackdc,0,0,rt.right,rt.bottom,hdc,0,0,SRCCOPY);
  16:   
  17:  CLSID encoderClsid;
  18:   
  19:  Bitmap bitmap (hbm1,NULL);//GDI+的对象保存成jpg图片很方便
  20:   
  21:  GetEncoderClsid(L"image/jpeg",&encoderClsid); 
  22:  bitmap.Save(filename, &encoderClsid, NULL);
  23:  PlaySound(MAKEINTRESOURCE(IDI_OKSOUND),g_hIn,SND_RESOURCE);
  24:  DeleteObject(hbm);
  25:  DeleteObject(hbm1);
  26:  ReleaseDC(NULL,hBackdc);
  27:  ReleaseDC(NULL,hdc);
  28:  }

Comments