注册 登录
编程论坛 汇编论坛

编写截屏工具遇到的问题

邋遢鬼 发布于 2012-07-29 13:48, 1446 次点击

.386
.Model Flat, StdCall
Option Casemap :None

Include D:\masm32\include\windows.inc
Include D:\masm32\include\user32.inc
Include D:\masm32\include\kernel32.inc
Include D:\masm32\include\gdi32.inc

includelib D:\masm32\lib\gdi32.lib
IncludeLib D:\masm32\lib\user32.lib
IncludeLib D:\masm32\lib\kernel32.lib
   
.DATA
    szClassName db "MASMPlus_Class",0
    szChildClassName db "Class",0
    hInstance    dd ?;实例句柄
    lpError        dd ?;如果使用GetLastError函数话,使用这个变量做字符串指针
    stRect        RECT <>
    hDCScreen    dd ?;内存DC句柄
    hBmpScreen    dd ?;内存位图句柄
    hChildWnd    dd ?;子窗口句柄
    point        dd ?,?;鼠标指针位置
    oldpoint    dd ?,?;同上
.CODE

_ChildProc proc uses ebx esi edi hWnd,uMsg,wParam,lParam
    local @hDC
    local @stPs:PAINTSTRUCT
    mov eax,uMsg
    .if eax == WM_PAINT
        invoke BeginPaint,hWnd,addr @stPs
        mov @hDC,eax
        invoke GetClientRect,hWnd,offset stRect
        mov eax,oldpoint
        mov ecx,oldpoint+4;取得子窗口原点相对父窗口原点的位移
        invoke BitBlt,@hDC,0,0,stRect.right,stRect.bottom,\
            hDCScreen,eax,ecx,NOTSRCCOPY
        invoke EndPaint,hWnd,addr @stPs
    .elseif eax == WM_SIZE
        mov esi,lParam
        test esi,esi
        jz @ret
        mov eax,point
        mov ecx,point+4
        mov edi,esi
        and esi,0fffh
        shr edi,16
        cmp eax,esi;小的放eax做子窗口的原点
        jb @f
        je @ret
            xchg eax,esi
        @@:
        sub esi,eax;得到x轴增量
        cmp ecx,edi
        jb @f
        je @ret
            xchg ecx,edi
        @@:
        sub edi,ecx;得到y轴增量
        mov oldpoint,eax
        mov oldpoint+4,ecx;临时原点坐标
        invoke SetWindowPos,hWnd,HWND_TOPMOST,eax,ecx,esi,edi,SWP_SHOWWINDOW;设置窗口大小和位置
        invoke InvalidateRect,hWnd,0,FALSE;刷新屏幕
    .elseif eax == WM_CLOSE||eax == WM_KEYDOWN
        invoke DestroyWindow,hWnd
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    @ret:
    xor eax,eax
    ret
_ChildProc endp
WndProc proc uses ebx esi edi hWnd:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
    local @stPs:PAINTSTRUCT
    local @hDC
    mov eax,uMsg
    .if eax == WM_PAINT;拷贝临时内存DC到自己的DC
        invoke BeginPaint,hWnd,addr @stPs
        mov @hDC,eax
        invoke GetClientRect,hWnd,offset stRect
        invoke BitBlt,@hDC,0,0,stRect.right,stRect.bottom,hDCScreen,0,0,SRCCOPY
        invoke EndPaint,hWnd,addr @stPs
    .elseif eax == WM_CREATE
        invoke GetDesktopWindow;得到桌面的窗口句柄
        push eax
        invoke GetClientRect,eax,offset stRect;获取它的客户区大小
        mov eax,[esp]
        invoke GetDC,eax;获取桌面DC
        mov @hDC,eax
        invoke CreateCompatibleDC,eax;创建内存DC
        mov hDCScreen,eax
        invoke CreateCompatibleBitmap,@hDC,stRect.right,stRect.bottom;创建内存位图
        mov hBmpScreen,eax
        invoke SelectObject,hDCScreen,hBmpScreen;装入位图

        invoke BitBlt,hDCScreen,0,0,stRect.right,stRect.bottom,@hDC,0,0,NOTSRCCOPY
        pop eax;从桌面DC拷贝到临时内存DC
        invoke ReleaseDC,eax,@hDC

    .elseif eax == WM_MOUSEMOVE
        .if wParam == MK_LBUTTON
            invoke SendMessage,hChildWnd,WM_SIZE,SIZE_RESTORED,lParam
        .endif;在WM_MOUSEMOVE消息中改变子窗口大小
    .elseif eax == WM_LBUTTONDOWN
        .if hChildWnd != 0;如果已经创建,就先关闭它
            invoke SendMessage,hChildWnd,WM_CLOSE,0,0
        .endif
        mov eax,lParam
        mov ecx,eax
        shr ecx,16
        and eax,0ffffh
        mov point,eax
        mov point+4,ecx
        mov oldpoint,eax
        mov oldpoint+4,ecx;计算子窗口的显示位置
        invoke CreateWindowEx,WS_EX_TOPMOST,offset szChildClassName,0,\
            WS_POPUP or WS_CHILD or WS_THICKFRAME,\
            eax,ecx,2,2,hWnd,NULL,hInstance,NULL
            
        mov hChildWnd,eax
        invoke ShowWindow,eax,SW_SHOWNORMAL;显示窗口
        invoke UpdateWindow,hChildWnd
    .elseif eax == WM_RBUTTONDOWN
        invoke SendMessage,hChildWnd,WM_CLOSE,0,0;右键关闭子窗口
    .elseif eax == WM_DESTROY||eax==WM_KEYDOWN
        invoke SendMessage,hChildWnd,WM_CLOSE,0,0
        invoke PostQuitMessage,NULL
        invoke DeleteDC,hDCScreen
        invoke DeleteObject,hBmpScreen
    .else
        invoke DefWindowProc,hWnd,eax,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
WndProc endp
WinMain proc
    LOCAL @WC   :WNDCLASSEX
    LOCAL @Msg  :MSG
    local @hWnd :HWND
    invoke RtlZeroMemory,addr @WC,sizeof @WC
    mov @WC.cbSize,sizeof WNDCLASSEX
    mov @WC.style,CS_HREDRAW or CS_VREDRAW
    mov @WC.lpfnWndProc,offset WndProc
    push hInstance
    pop @WC.hInstance
    mov @WC.lpszClassName,offset szClassName
    invoke LoadCursor,0,IDC_ARROW
    mov @WC.hCursor,eax
    invoke RegisterClassEx, ADDR @WC
    ;父窗口的窗口类
    mov @WC.lpszClassName,offset szChildClassName
    mov @WC.lpfnWndProc,offset _ChildProc
    invoke RegisterClassEx, ADDR @WC
    ;子窗口的窗口类
    invoke CreateWindowEx,0,ADDR szClassName,0,WS_POPUPWINDOW,\
        500,300,400,400,NULL,NULL,hInstance,NULL
        ;创建一个全屏无边框窗口
    mov @hWnd,eax
    invoke ShowWindow,@hWnd,SW_MAXIMIZE;最大化
    invoke UpdateWindow,@hWnd
    StartLoop:
        invoke GetMessage,ADDR @Msg,NULL,0,0
            or eax,eax
            je ExitLoop
                invoke DispatchMessage, ADDR @Msg
            jmp StartLoop
    ExitLoop:
   
mov eax,@Msg.wParam
ret
WinMain endp


START:

    invoke GetModuleHandle,NULL
    mov hInstance,eax
    invoke WinMain
    invoke ExitProcess,0

END START

错误表现为 我从某个点向右下截屏,却截到的点坐上的,令请指点WS_POPUPWINDOW风格的窗口如何在运行时设置大小。
24 回复
#2
信箱有效2012-07-29 15:28
看不懂
#3
zklhp2012-07-29 15:29
太长了 祝楼主早日解决问题。。
#4
邋遢鬼2012-07-29 19:03

不要这样嘛。我真心求帮助。为何系统发送的WM_SIZE消息的lParam会为0呢?还有,我判断以小的坐标做子窗口左上角的坐标,但是有时会反。不懂了,真心求指点的。
#5
邋遢鬼2012-07-29 23:27
#6
zklhp2012-07-29 23:45
看了看 没明白咋回事
#7
邋遢鬼2012-07-30 11:49
就是说,我创建一个窗口,大小是整个屏幕。然后把屏幕的dc 颜色取反拷给自己,然后在鼠标左击消息中,创建一个子窗口,并根据鼠标左击按下并移动消息,设置子窗口的大小。现在大小能设置,有两个坐标点存在。一个是窗口创建时那个坐标,一个是鼠标移动时的坐标。但是鼠标移动的坐标有时我计算的不对。唔,这个我想到办法了,那个,由于子窗口是WS_POPUPWINDOW风格,那么我在运行时如何拖动它改变窗口的大小?
#8
信箱有效2012-07-30 12:42
楼主好厉害 能写出这么长的代码。 我要拜你为师。
#9
邋遢鬼2012-07-30 13:35

代码不是越长越好的。
难道我需要定义4中cursor,自己判断鼠标是否在子窗口边界?
#10
zklhp2012-07-30 13:36
同拜师

感觉你这个实现方法不大好 虽然我也不知道怎么写好。。
#11
信箱有效2012-07-30 13:47
截屏呀。师傅说下这个截屏的思路呗。
#12
邋遢鬼2012-07-30 16:53
求思路。
#13
信箱有效2012-07-30 17:11
搜了半天 只找到实现方法
取屏幕图象
创建一个全屏窗口
把图象贴到全屏窗口中
选取目标区域
保存目标区域的位图到剪贴板或者到文件。
#14
zklhp2012-07-30 17:27
以下是引用信箱有效在2012-7-30 17:11:25的发言:

搜了半天 只找到实现方法
取屏幕图象
创建一个全屏窗口
把图象贴到全屏窗口中
选取目标区域
保存目标区域的位图到剪贴板或者到文件。

这个就很好嘛
#15
yuma2012-07-30 17:34
用高级语言写不是很简单吗?为什么要用汇编呢?
#16
zklhp2012-07-30 17:53
以下是引用yuma在2012-7-30 17:34:21的发言:

用高级语言写不是很简单吗?为什么要用汇编呢?

9494
#17
邋遢鬼2012-07-30 18:00
语言只是工具,不用太纠结。
选取目标区域
这步总得给用户修改大小和移动的机会吧?
#18
zklhp2012-07-30 18:06
你直接处理鼠标消息 不用什么小窗口 不能实现么
#19
yuma2012-07-30 18:09
别忘了,截屏分为:全屏 当前活动窗口  选取区域。

个人截屏经常用:键盘+画图

用高级语言要不了多少代码,就很容易实现。
#20
信箱有效2012-07-30 18:47
选取以后显示矩形,如果要修改和移动,直接再重新单击拖动左键就重新选取了。 如果像QQ那样的截图。。。太复杂了。
#21
zklhp2012-07-30 18:47
其实这里难有两个地方 一个是用汇编 比用高级语言难 但更难的是 这里是用windows提供的gdi函数来实现的 这些函数很原始 用起来也费劲 所以难

难主要是后者 前者 对于汇编熟手来说 我说的不是高手啊 熟手 熟练就可以 写汇编和写C的速度是差不多的 尤其是 可以用宏嘛


[ 本帖最后由 zklhp 于 2012-7-30 18:49 编辑 ]
#22
zklhp2012-07-30 18:51
以下是引用信箱有效在2012-7-30 18:47:23的发言:

选取以后显示矩形,如果要修改和移动,直接再重新单击拖动左键就重新选取了。 如果像QQ那样的截图。。。太复杂了。

你说的是拖动改大小 那个是在截图之后实现的罢

也就是 实现了一个鼠标画框后 再实现改大小

画框么 仔细想想不算难
#23
邋遢鬼2012-07-31 00:23
不只是画框呀,鼠标在边界还得改变鼠标形状呢。
#24
C_printf2012-07-31 11:41
看了下程序,我的建议是:既然你创建了全屏无边框,就不需要创建子窗口,把桌面dc的数据贴进全屏窗口,先保存全屏dc中的数据,直接在这个全屏窗口上来画你要截取的矩形就简单多了(并且可以让屏幕灰一点,视觉跟好),然后在事先保存的2维像素里面提出想要的数据块,然后用图片格式保存像素。至于鼠标也好说了,你有坐标临界点,还怕控制不了鼠标?
这完全在一个窗口绘制区操作了(虽然是无边框的),省的坐标系转换之类的问题。简单明了
如果你用我说的方法 就不需要改全屏窗口的大小了!
#25
邋遢鬼2012-07-31 18:03
全屏窗口的大小是不变的。那么要不就不创建子窗口了,在全屏窗口的目标区域画一个边框,并设置4种鼠标形状。在WM_MOUSEMOVE消息中判断当前光标形状来进行某种操作。我回去再试下。
本来创建子窗口的目的只是为了方便控制。如果只有一个全屏窗口的话,所有坐标都是相对于自己的坐标原点,或许操作更加方便。
1