Fasm--Win32汇编学习12

     Fasm--Win32汇编学习12

现在我们开始学习一些有关GUI编程的有趣的部分, 即:以对话框为主要界面的应用程序。我们将分两课来讲述这一过程

   理论:

    如果您仔细关注过前一个程序就会发现:您无法按TAB键从一个子窗口控件跳到另一个子窗口控件,要想转移的话只有用鼠标一下一下的去单击。对用户来说是不友好的。另一件事如果您象前一节课中那样把主窗口的北京从白色改为灰色,为了子窗口控件无缝地作相应的改变,您必须细分类所有子窗口。造成上述诸多不变的原因是子窗口控件本来是为对话框二设计的,象子窗口控件的背景色是灰色的,而对话框的背景色也是灰色的,这样它们本来就相互协调了,二无需程序员加入其他的处理,在我们深入讨论对话框前,我们必须知道why是对话框。一个对话框其实本来是有很多的子窗口控件的一个窗口,windows在对话框内部有一个管理程序,由其来处理象按下TAB键则输入焦点从一个子窗口控件跳到另一个子窗口控件、按下ENTER键等于在当前具有输入焦点的子窗口空间商点击了鼠标等等这些杂事,这样程序员就可以集中精力于他们的逻辑事务了。对话框主要左输入输出的接口,人们无需知道它内部的工作原理,而只要知道如何和他们进行交互就可以了。这也是面向对象设计中的所谓信息隐藏。只要这个黑盒子中的实现足够完美,我们就可以放心的使用。当然,我们必须强调的是这个“黑盒子”必须是完美的。WIN32 API内部就是一个“黑盒子”,噢,发现好像跑题了,那么我们回到正题。对话框的设计是为了减少程序员的工作量的,一般如果您在你的窗口上放一个子窗口控件,您必须处理它的按键逻辑以及分类它的窗口过程。如果您把它放在对话框中,则这些杂事对话框会自己处理,您只要知道如何获得用户输入的数据和如何把用户输入的数据放入到子窗口控件中就可以了。在程序中对话框和菜单同样是被定义成一种资源,您可以在脚本文件中写一个对话框模板,其中包含该对话框以及子窗口控件的特性,然后利用资源编辑器编辑。需要注意的是所有的资源必须放在同一个脚本文件中。虽然用文本编辑器可以编辑资源脚本,但是象调整子窗口控件位置时要牵扯到一些坐标值所以最好还是使用可视化的资源编辑器进行编辑,这样方便多了。一般编译器的开发包中都带有资源编辑器,您可以用它们来产生一个模板,然后自己增删一些子窗口控件。

    对话框有两种,模式对话框和无模式对话框。无模式对话框允许你把输入焦点切换到同一个程序的另一个窗口,而该对话框无须改变。比如MS WORD的FIND对话框。模式对话框又有两类:应用程序对话框和系统对话框。应用程序对话框不允许你在本应用程序中切换输入焦点,但可以切换到其他的程序中,而系统对话框则必须你对对话框做出响应否则不能切换到任何的应用程序中去。要创建一个无模式对话框掉要调用API函数CreateDialogParam,而创建一个模式对话框调用API DialogBoxParam。其中应用程序对话框和系统对话框的区别是style参数不同,要想创建一个系统模式对话框参数必须或上DS_SYSMODAL标志,在对话框中若要和子窗口通讯则调用函数SendDlgItemMessage。该函数的语法:

SendDlgItemMessage proto hwndDlg:DWORD,/
                                        idControl:DWORD,/
                                        uMsg:DWORD,/
                                        wParam:DWORD,/
                                        lParam:DWORD

该API对于向子窗口控件发送方面非常有用的。譬如你想获得一个子窗口控件中的字符串你可以这样做。

call SendDlgItemMessage, [hDlg], IDC_EDITBOX, WM_GETTEXT, 256, szBuffer

    具体要发送那些消息可以应该查询有关的WIN32 API手册。Windows还提供了几个快速存取控件数据的api函数。 譬如GetDlgItemText,CheckDlgButton等。这样一来,您就不需要去查每个消息的wparam和lparam参数获得相关讯息了。您应尽可能的使用这些API函数,这将使您的代码比较好维护。对话框管理器会把一些消息发送给一个特定的回调函数,对话框处理函数格式为:

  DlgProc proto hDlg:DWORD ,/
                            iMsg:DWORD ,/
                            wParam:DWORD ,/
                            lParam:DWORD

    该函数的格式非常类似窗口过程函数,除了返回值是true和false,而不是LRESULT,存在于windows内部的对话框管理器才是真正的窗口过程函数。对话框管理会首先调用我们自定义的过程,所以当我们的窗口过程函数处理这些消息时返回true,这表示我们已经处理了某条信息,返回false表示没有处理。这也意味着我们的窗口过程函数在接受到自己不处理的消息时并不会调用DefWindowProc函数,因为它本身不是一个真正的窗口过程函数。

  对于对话框有两种用法:

     一种是把它作为主窗口来用,一种是把它作为输入输出设备来使用。

本课中我们将示范第一种方法,“把对话框用作主窗口”有两种意思,

    1.您可以通过RegisterClassEx函数把对话框模板注册成一个窗口类,这样对话框的行为就成为一个普通的窗口了,它通过在注册时使用默认的窗口过程来处理所有的消息,通过这样的方法的好处是你不需要显示创建子窗口控件,windows对话框管理器会帮你创建,另外还会帮你创建按键逻辑,另外您还可以指定您窗口类中的光标和图标。

   2.您的应用程序创建没有父窗口的对话框窗口,这种方法中,没有必要需要一段处理消息循环的代码,因为所有的消息会windows对话管理器送到对话框过程函数中,这样您也不用注册窗口类。本课中我们将先使用第一种方法,然后使用第二种方法。

代码:

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


.data
        ;**************数据********************
        szClassName db 'DLGCLASS',0
        szWndName db 'My first program',0
        szMenuName db 'MyMenu',0
        szTestString db 'Wow! i am in an edit box now', 0
        szDlgName db 'Mydialog', 0
        szBuffer db 50 dup (?)
        hInstanse rd 1
        hIcon           rd 1
        hCursor           rd 1
        hDlg rd 1
        lpCommand rd 1
        hButton rd 1
        hEdit rd 1
        IDM_HELLO equ 32000
        IDM_CLEAR equ 32001
        IDM_EXIT equ 32003
        IDC_EDIT equ 3000
        IDC_BUTTON equ 3001
        IDC_EXIT equ 3002
.code


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
   
   
            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], DLGWINDOWEXTRA
            memmov @wc.hInstance, hInstance
            memmov @wc.hIcon, hIcon
            memmov @wc.hCursor, hCursor
            mov [@wc.hbrBackground], COLOR_WINDOW+1
            mov [@wc.lpszMenuName], szMenuName
            mov [@wc.lpszClassName], szClassName
            invoke RegisterClassEx, addr @wc
            invoke CreateDialogParam, [hInstanse], szDlgName, NULL, NULL, NULL
            mov [hDlg], eax
            invoke ShowWindow,[hDlg],SW_SHOWNORMAL
            invoke UpdateWindow,[hDlg]
            invoke GetDlgItem, [hDlg], IDC_EDIT
            invoke SetFocus, eax
       
        .GetMsg:   
            invoke GetMessage,addr @Msg,NULL, 0, 0
            or eax, eax
            je .QUIT
            invoke IsDialogMessage, [hDlg], addr @Msg
            or eax, eax
            jnz .GetMsg
            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
       
            invoke DefWindowProc,[hWnd],[uMsg],[wParam], [lParam]
            ret
       
   
    .command:
            mov eax ,[wParam]
            cmp ax, IDM_HELLO
            je _hello
            cmp ax, IDM_CLEAR
            je _clear
            cmp ax, IDM_EXIT
            je _exit
            jmp @f              
    _hello:
            invoke GetDlgItemText, [hWnd], IDC_EDIT, szBuffer, 50
            invoke MessageBox, NULL, szBuffer, 'test', MB_OK
            jmp Myend
       
    _clear:
            invoke SetDlgItemText, [hWnd], IDC_EDIT, NULL
            jmp Myend
    _exit:
        invoke DestroyWindow, [hWnd]
        jmp Myend
       
    @@:
            shr edx, 16
            cmp dx,BN_CLICKED
            jnz Myend
       
    _SetText:
            cmp ax, IDC_BUTTON
            jnz _Btexit
            invoke SetDlgItemText, [hWnd], IDC_EDIT, szTestString
            jmp Myend
    _Btexit:
    cmp ax, IDC_EXIT
    jnz Myend
    invoke SendMessage, [hWnd], WM_COMMAND, IDM_EXIT, NULL
    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 'resoucename.res'    

 

;/Rc//

 

#include "resource.h"

#define IDC_EDIT 3000
#define IDC_BUTTON 3001
#define IDC_EXIT 3002

#define IDM_HELLO 32000
#define IDM_CLEAR 32001
#define IDM_EXIT 32003

Mydialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our First Dialog Box"
CLASS "DLGCLASS"
BEGIN
    EDITTEXT         IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
    DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
    PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13, WS_GROUP

END




MyMenu MENU
BEGIN
    POPUP "popup&"
        BEGIN
        MENUITEM "&say hello", IDM_HELLO
        MENUITEM "&Clear Edit Box", IDM_CLEAR
        MENUITEM SEPARATOR
        MENUITEM "&Exit", IDM_EXIT
        END

END

分析:

    我们先来分析这个例子。

    该例子显示了如何把一个对话框模板注册为一个窗口类,然后创建一个由该窗口类派生的窗口。由于您没必要自己去创建子窗口控件,所以就简化了很多工作。

   Mydialog DIALOG 10, 10, 205, 60

    先是对话框的名字,然后是关键字“DIALOG” 。接下来的四个数字种,前两个是对话框的坐标,后两个是对话框的高和宽(注意:它们是对话框的单位而不一定是像素点)。

 

    STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK

  这里定义的是对话框的风格。

 

  CAPTION "Our First Dialog Box"

  这里是定义的对话框标题条上的标题。

 

   CLASS "DLGCLASS"

   这句是定义的类名,很重要。正是有了关键字CLASS 我们才能用它来声明把一个对话框当成一个窗口来用。跟在关键字后面的是“窗口类”的名称。

 

BEGIN
    EDITTEXT         IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
    DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
    PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13, WS_GROUP

END

  上面这一块定义的对话框子窗口控件,它们是声明在一头一位的两个关键字BEGIN和END之间。

control-type "text" ,controlID, x, y, width, height [,styles]


控件的类型是资源编辑器定义好了的常数,您可以查找有关的手册。 现在我们来看看汇编源代码。先看这部分:

       
        mov [@wc.cbWndExtra], DLGWINDOWEXTRA

        mov [@wc.lpszClassName], szClassName

通常cbWndExtra成员设置为NULL,但是我们想把一个对话框模板注册成一个窗口类我们必须将成员设置为DLGWINDOWEXTRA。这是MS的规定,你可以查下MSDN 上有对齐的详细解释。注意类名必须和资源脚本中CALLS关键字后面的类名保持一致。余下的成员变量和声明一般的窗口类保持相同。填写好窗口类变量后调用RegisterClassEx函数进行注册。看上起和注册一个普通的窗口类没什么不同。

  

invoke CreateDialogParam, [hInstanse], szDlgName, NULL, NULL, NULL

     注册完毕后,我们调用CreateDialogParam函数来产生一个无模式的对话框。这个函数共有5个参数,其中前两个参数是必须的:实例句柄和对话框模板名称的指针。注意:第二个参数是指向模板名称二不是类名称。这时windows将产生对话框和子控件窗口。同时您的应用程序将接受到windows传送的第一个消息WM_CREATE。

 

            invoke GetDlgItem, [hDlg], IDC_EDIT
            invoke SetFocus, eax

    在对话框产生后,我们把输入输出焦点设置到我们的编辑框中。如果在WM_CREATE消息处理那段设置焦点的代码GetDlgItem函数会返回失败,因为此时空间窗口还并没有产生。为了在对话框和所有的子窗口控件都产生后调用该函数我把它安排在了UpdataWindow函数后。GetDlgItem函数返回子窗口控件的句柄。

       

.GetMsg:   
            invoke GetMessage,addr @Msg,NULL, 0, 0
            or eax, eax
            je .QUIT
            invoke IsDialogMessage, [hDlg], addr @Msg
            or eax, eax
            jnz .GetMsg
            invoke TranslateMessage,addr @Msg
            invoke DispatchMessage,addr @Msg   
        jmp .GetMsg

     现在进入我们的消息循环,在我们翻译和派发消息前,IsDialogMessage该函数使得对话框内置的对话框管理程序来处理按键逻辑。如果该函数返回true,则表示消息已经被处理过了(也就表示消息是传递给对话框管理器的),所以我们进行判断如果等于true则直接跳到.GetMsg标号处来获得消息,就省略了我们的翻译和派发的过程。注意和前一课不同,当我们想得到控件的文本信息时调用GetDlgItemText函数而不是GetWindowText函数,前者接受的参数是一个控件的ID 号,而不是窗口的句柄,这使得在对话框中调用该函数更方便。

 

;//____________________________________________Dialog2____________________________________________________________________

    好我们现在使用第二种方法把一个对话框当成一个主窗口来使用。在接下来的例子中,我们将产生一个应用程序的模式对话框,您将会发现其中根本没有消息循环或窗口处理过程,因为它们根本没有必要!

        format PE GUI 4.0
       
        include 'win32ax.inc'
   
   

       


.data
        ;**************数据********************
        szTestString db 'Wow! i am in an edit box now', 0
        szDlgName db 'Mydialog', 0
        szBuffer db 50 dup (?)
        hInstanse rd 1
        IDM_HELLO equ 32000
        IDM_CLEAR equ 32001
        IDM_EXIT equ 32003
        IDC_EDIT equ 3000
        IDC_BUTTON equ 3001
        IDC_EXIT equ 3002
.code


entry $
        xor edi, edi
        invoke GetModuleHandle, edi
        mov [hInstanse], eax
        invoke DialogBoxParam, [hInstanse], szDlgName, edi, DlgProc, edi
        invoke GetLastError
        invoke ExitProcess,edi
       
       
proc DlgProc uses ebx esi edi, hwndDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
       
        cmp [uMsg], WM_INITDIALOG
        je Initial
        cmp [uMsg], WM_CLOSE
        je Finish
        cmp [uMsg], WM_COMMAND
        je Command
       
RetFalse:
        mov eax, FALSE
        ret       

Initial:
        invoke GetDlgItem, IDC_EDIT
        invoke SetFocus, eax
        jmp RetTrue
       
Finish:       
        invoke SendMessage, [hwndDlg], WM_COMMAND, IDM_EXIT, NULL
        jmp RetTrue
Command:
        mov eax, [wParam]
        cmp [lParam], 0
        jne BoxCommand
       
MenuCommand:
            cmp ax, IDM_HELLO
        je _hello
        cmp ax, IDM_CLEAR
        je _Clear
        cmp ax, IDM_EXIT
        je _Exit
        jmp RetTrue
BoxCommand:
        mov edx, [wParam]
        shr edx, 16
        cmp dx, BN_CLICKED
        jne RetTrue
        cmp ax, IDC_BUTTON
        je Button
        cmp ax, IDC_EXIT
        je _Exit
        jmp RetTrue
Button:
        invoke SetDlgItemText, [hwndDlg], IDC_EDIT, szTestString
        jmp RetTrue

           
_hello:
    invoke GetDlgItemText,[hwndDlg], IDC_EDIT, szBuffer, 50
    invoke MessageBox, NULL, szBuffer, 'test', MB_OK
    jmp RetTrue
_Clear:
        invoke SetDlgItemText,[hwndDlg], IDC_EDIT, NULL
        jmp RetTrue

_Exit:
        invoke EndDialog,[hwndDlg], NULL
RetTrue:
            mov eax, TRUE
        ret

endp

       
   
   
   
.import
       
library kernel32, 'kernel32.dll',/
            user32, 'user32.dll'
include 'api/kernel32.inc'
include 'api/user32.inc'   


section '.rsrc' data readable resource from 'test31.res'    

 

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>rc>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#define IDM_HELLO 32000
#define IDM_CLEAR 32001
#define IDM_EXIT 32003

Mydialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our First Dialog Box"
MENU MyMenu
BEGIN
    EDITTEXT         IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
    DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
    PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13, WS_GROUP

END




MyMenu MENU
BEGIN
    POPUP "popup&"
        BEGIN
        MENUITEM "&say hello", IDM_HELLO
        MENUITEM "&Clear Edit Box", IDM_CLEAR
        MENUITEM SEPARATOR
        MENUITEM "&Exit", IDM_EXIT
        END

END

 

分析:

invoke DialogBoxParam, [hInstanse], szDlgName, edi, DlgProc, edi

在上面我们已经说了创建模式对话框用的DialogBoxParam函数,该函数有五个参数,分别是:实例句柄、对话框模板的名字、父窗口的句柄、对话框过程函数的地址、和对话框相关的数据。该函数产生一个模式对话框。如果不显示地关闭该函数不会返回。

        cmp [uMsg], WM_INITDIALOG
        je Initial
        cmp [uMsg], WM_CLOSE
        je Finish

    除了不处理WM_CREATE消息外对话框的窗口过程函数和一般的窗口过程类似,该过程接受到的第一个消息是WM_INITDIALOG。通常把初始化的代码放在这里。请注意你处理该消息必须在eax中返回true 。内置的对话框函数不会把WM_DESTROY消息发送到对话框的消息处理过程中,所以我们如果想在对话框关闭时进行处理我们必须把它放在WM_CLOSE消息中处理。在我们的例子中发送WM_COMMAND消息并在wparam中放IDM_EXIT这和处理WM_CLOSE消息是一样的。在处理IDM_EXIT中我们调用EndDialog函数。如果我们想要销毁一个对话框必须调用EndDialog函数。该函数不会立即销毁,而是设置一个标志位,然后对话框管理器会接下来处理销毁的动作。好,现在我们来看看资源文件,在调用DialogBoxParam产生的对话框中挂接一个菜单必须这么做,注意在该对话框模板中,在该标识符前必须加MENU关键字,这两个例子中的显著不同是后者没有图标,这可以在处理WM_INITDIALOG中发送消息WM_SETICON消息,然后在该消息处理代码中作适当的处理即可。

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值