FASM -- Win32汇编学习11

来源:互联网 发布:知乎 acca 培训班 编辑:程序博客网 时间:2024/05/09 20:36

子窗口控件

本节课程中,我们将讨论控件。这些控件作为是我们输入输出的设备。

 

    理论:

        Windows提供了几个预定义的窗口类供我们窗口使用。大多数时间内我们把它们用在对话框上,所以我们把它们叫做子窗口控件。子窗口控件会自己处理消息,并在自己状态发生改变时通知父窗口。这样大大减轻了我们编程的工作,我们应尽可能的利用它们。本课中我们把这些控件放在窗口中简化程序。但是大多数情况下子窗口控件都是放在对话框中的。我们示例中演示的子窗口控件包括: 按钮、下拉菜单、检查框、单选按钮、编辑框等。使用子窗口控件时,先调用CreateWindow函数或CreateWindowEx函数。这里由于windows已经注册了这些子控件,所以我们无需在注册。当然我们也不能改变它们的类名称。譬如你如果想产生一个按钮控件,则必须指令类名为“Button”。其他必须制定的参数还有父窗口的句柄和将要产生子窗口的控件ID。子窗口的控件ID号是用来标示子控件的,故也必须是唯一的。子窗口控件产生后,当状态发生改变时会像其父窗口发送消息。一般我们应在WM_CREATE消息中产生子控件。子控件向父窗口发送的消息是WM_COMMAND消息,并传递的wparam的低二位字节是包括控件的ID,消息号在wparam高两位字节,lparam中则是子控件的窗口句柄。各类控件有不同的消息代码集,详情参见Win32 API参考手册。父窗口也可以通过调用函数SendMessage向子控件发送消息,其中第一个参数是子控件的窗口句柄,第二个参数是要发送的消息号。附加的参数可以在wparam和lparam中传递,其实只要知道,其实只要知道某个窗口的句柄就可以用该函数发送消息相关消息。所以产生子窗口后必须处理WM_COMMAND消息以便可以接受到子控件的消息。

 

  例子:

    我们将产生一个窗口,在该窗口中有一个编辑框和一个按钮。当你按下按钮时,会弹出一个消息框显示你在编辑框中输入的内容。另外该应用程序还有四个菜单项。

    1. say hello    把一段字符串输入到编辑框中。

    2. Clear Edit Box    清除编辑框中的字符串。

    3. Get Text     弹出对话框显示编辑空中的内容。

    4. Exit 退出程序

       

代码:

        format PE GUI 4.0
       
        include 'win32ax.inc'
   
   
        macro memmov [dst, src]
        {
            common
            push [src]
            pop [dst]
        }
       


.data
        ;**************数据********************
        szClassName db 'first Window',0
        szWndName db 'My first program',0
        szMenuName db 'MyMenu',0
        szTestString db 'Wow! i am in an edit box now', 0
        EditName db 'edit',0
        ButtonName db 'button',0
        ButtonText db 'My first button',0
        szBuffer db 50 dup (?)
        hInstanse rd 1
        hIcon          rd 1
        hCursor          rd 1
        hWndow rd 1
        lpCommand rd 1
        hButton rd 1
        hEdit rd 1
        IDM_TEXT equ 1
        IDM_HELLO equ 2
        IDM_CLEAR equ 3
        IDM_EXIT equ 4
        BUTTONID =    5
        EDITID   = 6
       
.text


entry $

       
    xor edi, edi
    invoke GetModuleHandle, edi
    or eax, eax
    jz .fail
    mov [hInstanse], eax
    invoke GetCommandLine,edi
    mov [lpCommand], eax
    stdcall _WndMain, hInstanse, edi, [lpCommand],SW_SHOWDEFAULT

.fail:
    invoke ExitProcess,NULL       

    proc _WndMain hInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
            local @wc:WNDCLASSEX
            local @Msg:MSG
    local @hMenu:DWORD
   
            invoke RtlZeroMemory, addr @wc, sizeof.WNDCLASSEX
            invoke LoadIcon, NULL,IDI_ASTERISK   
            mov [hIcon], eax
            invoke LoadCursor, NULL,IDC_ARROW
            mov [hCursor], eax
            mov [@wc.cbSize], sizeof.WNDCLASSEX
            mov [@wc.style], CS_HREDRAW or CS_VREDRAW
            mov [@wc.lpfnWndProc], _WndProc
            mov [@wc.cbClsExtra], NULL
            mov [@wc.cbWndExtra], NULL
            memmov @wc.hInstance, hInstance
            memmov @wc.hIcon, hIcon
            memmov @wc.hCursor, hCursor
            mov [@wc.hCursor], hCursor
            mov [@wc.hbrBackground], COLOR_WINDOW + 1
            mov [@wc.lpszMenuName], NULL
            mov [@wc.lpszClassName], szClassName
            invoke RegisterClassEx, addr @wc
            invoke LoadMenu,[hInstanse], szMenuName
            mov [@hMenu], eax
           
            invoke CreateWindowEx, WS_EX_CLIENTEDGE, addr szClassName, szWndName, WS_OVERLAPPEDWINDOW,/
                        CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,/
                        NULL, [@hMenu] , [hInstanse] , NULL
            mov [hWndow], eax
            invoke ShowWindow,[hWndow],SW_SHOWNORMAL
            invoke UpdateWindow,[hWndow]
       
        .GetMsg:   
            invoke GetMessage,addr @Msg,NULL, 0, 0
            or eax, eax
            je .QUIT
            invoke TranslateMessage,addr @Msg
            invoke DispatchMessage,addr @Msg
            jmp .GetMsg
   
        .QUIT:
            mov eax, [@Msg.wParam]
            ret

endp
       
   
   
    proc _WndProc uses ebx esi edi, hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
   
            cmp [uMsg], WM_DESTROY
            je finish
            cmp [uMsg], WM_COMMAND
            je .command
            cmp [uMsg], WM_CREATE
            je .create
            invoke DefWindowProc,[hWnd],[uMsg],[wParam], [lParam]
            ret
       
    .create:
            invoke CreateWindowEx,NULL, ButtonName, ButtonText, /
                        WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, /
                    75, 70, 140, 25, [hWnd] , BUTTONID, [hInstanse], NULL
            or eax, eax
            je faild
                   
            mov [hButton], eax
           
            invoke CreateWindowEx,WS_EX_CLIENTEDGE, EditName, NULL,/
                        WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, /
                        50, 35, 200, 25, [hWnd], EDITID, [hInstanse], NULL
            mov [hEdit], eax
    invoke SetFocus, [hEdit]
            jmp Myend
    .command:
            mov eax ,[wParam]
            and eax, 0FFFFh
            cmp eax, IDM_TEXT
            je _text
            cmp eax, IDM_HELLO
            je _hello
            cmp eax, IDM_CLEAR
            je _clear
            cmp eax, IDM_EXIT
            je _exit
            jmp Myend
    _text:
            invoke GetWindowText,[hEdit], szBuffer, 50
            invoke MessageBox, NULL, szBuffer, 'test', MB_OK
            jmp Myend
           
    _hello:
        invoke SetWindowText, [hEdit], szTestString
            jmp Myend
    _clear:
            invoke SetWindowText, [hEdit], NULL
            jmp Myend
    _exit:
        invoke DestroyWindow, [hWnd]
        jmp Myend
    faild:
            invoke MessageBox,NULL, 'faild', 'test', MB_OK
            jmp Myend
finish:

            invoke PostQuitMessage, NULL
    Myend:   
            xor eax, eax
        ret
    endp
   
   
.import
       
library kernel32, 'kernel32.dll',/
            user32, 'user32.dll'
include 'api/kernel32.inc'
include 'api/user32.inc'   


section '.rsrc' data readable resource from 'resourcename.RES'    

分析:

 

   .create:
            invoke CreateWindowEx,NULL, ButtonName, ButtonText, /
                        WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, /
                    75, 70, 140, 25, [hWnd] , BUTTONID, [hInstanse], NULL
            or eax, eax
            je faild
                   
            mov [hButton], eax
           
            invoke CreateWindowEx,WS_EX_CLIENTEDGE, EditName, NULL,/
                        WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, /
                        50, 35, 200, 25, [hWnd], EDITID, [hInstanse], NULL
            mov [hEdit], eax
    invoke SetFocus, [hEdit]
            jmp Myend

    我们在WM_CREATE消息中建立子窗口控件,在上面我也以及说了,我们一般都是在WM_CREATE消息中创建子窗口控件的。 我们通过CreatWindowEx函数来创建子窗口控件,因为这些子窗口控件都是windows预定义的类,这些类都是注册过的,且它们内部自己处理消息,当子窗口控件发生改变时向我们的父窗口发送WM_COMMADN消息。 创建子窗口控件没什么好讲的,我们在建立编辑框的时候给它附加了一个WS_EX_CLIENTEDGE外观,这使得看起来编辑下凹,具有立体感。每一个子控件的id都是预定义的,例如按钮的预定义类名是“button”,编辑框的预定义类名是“edit”。接下来的参数是窗口风格,除了通常的风格外,每一个控件也有自己的扩展风格,譬如按钮类的扩展风格前面加BS_,编辑框的类则是ES_。Win32 API参考中所有的风格描述,注意:您在CreateWindowsEx函数中本来要传递菜单句柄的地方传入子窗口空间的ID号不会有什么副作用,因为子窗口控件本身不能有菜单。产生控件后,我们保存它们的句柄,然后调用SetFocus把焦点设到编辑控件上以便用户立即可以输入。接下来的是如何处理控件发送的通知消息WM_COMMAND。

    .command:
            mov eax ,[wParam]
            and eax, 0FFFFh
            cmp eax, IDM_TEXT
            je _text
            cmp eax, IDM_HELLO
            je _hello
            cmp eax, IDM_CLEAR
            je _clear
            cmp eax, IDM_EXIT
            je _exit
            jmp Myend
    _text:
            invoke GetWindowText,[hEdit], szBuffer, 50
            invoke MessageBox, NULL, szBuffer, 'test', MB_OK
            jmp Myend
           
    _hello:
        invoke SetWindowText, [hEdit], szTestString
            jmp Myend
    _clear:
            invoke SetWindowText, [hEdit], NULL
            jmp Myend
    _exit:
        invoke DestroyWindow, [hWnd]
            jmp Myend
    faild:
            invoke MessageBox,NULL, 'faild', 'test', MB_OK
            jmp Myend

 

这里我们通过判断菜单项的ID号来进行处理。如果是选择say hello菜单,则通过调用SetWindowText设置编辑框一段字符串。 如果选择 Get Text 菜单则通过 GetWindowText函数获得编辑框中的内容并保存到缓冲区中。然后通过MessageBox显示缓冲区中的内容。其次如果选择的是Clear Edit Box消息,则调用SetWindowText设置编辑框的内容为NULL,(则此时编辑框的内容为空)。     如果选择exit菜单项,则调用DestroyWindow函数销毁窗口,从而结束程序。

cmp ax, BUTTONID
je _Button
_Button:
shr eax, 16
cmp eax, BN_CLICKED
jne Myend
invoke SendMessage,[hWnd] , WM_COMMAND, IDM_TEXT, NULL
jmp Myend

上面的片段是处理用户按钮事件的。他首先检查wParam的低字节看是否是按钮的ID 号,若是则检查高字节看发送的消息号是否BN_CLICKED,该消息是在按钮按下时发送的,如果一切都对,则转入处理该消息,我们可以从处理消息IDM_GETTEXT处复制全部的代码,但是更专业的办法是在发送一条IDM_GETTEXT消息让主窗口过程处理,这只要把传送的消息设置为WM_COMMAND,再把wParam的低字节中设置为IDM_GETTEXT即可。这样一来您的代码就简洁了许多,所以尽可能利用该技巧。最后,当然不是或有或无,必须在消息循环中调用函数TranslateMessage,因为您的应用程序需要在编辑框中输入可读的文字。如果省略了该函数,就不能在编辑框中输入任何东西。

原创粉丝点击