FASM--Win32汇编学习10

                 FASM--Win32汇编学习10

本节课中,我们来学习下菜单。

译者注:

    菜单可以说是Windows下最重要的元素之一。windows用它来提供大部分的命令操作。我之前说过,我们在Windows下编程,就要遵循Windows下相应的规则,那么菜单也是一样的。 大家可以看下Windows本身自带的一些应用程序,它们的菜单的基本也是相同的。都是位于标题栏的下方。当然这是我们通过它的外形的综述,那么内在的。我们windows提供的菜单,必须是资源的形式。所以我们必须遵守这样的规则。

 

    有了它,用户可以方便地选择操作命令。用户只要细读一下所有的菜单项就可以明了程序所提供的大概功能,而且可以立即操作,无须去阅读手册。正因为菜单给了用户一种方便的方式,所以您在应用程序中加入菜单时要遵守一定的规则。譬如l一般头两个菜单项是“File”和“Edit”,最后是“Help”,您可以在这中间插入您要定义的菜单项。如果所运行的菜单命令会弹出一个对话框,我们一般在其菜单项后加入(......),菜单是一种资源,除资源外还有其他的像对话框,字符串,图标,位图资源等都是资源。在链接程序的时候将资源加入到可执行文件中,最后我们的执行程序既包括机器指令又包括了资源。您可以在任何文本编辑器中编写脚本文件,在文件中您可以以指令资源呈现出来的外观和其他的一些属性。当然更直观的方法是用资源编辑器,通常资源编辑器都打包在编译环境中,像Visual C++ ,Borland C++ 等。

我们可以按以下方式来定义一个菜单资源。

 

菜单ID  MENU  [DISCARDABLE]

BEGIN

     [Menu List here]

END

当然BEGIN END还可以替换成{}C语言的中的风格。我们一般是定义一个顶层菜单,然后在顶层菜单里定义子菜单和一些弹出式菜单等。

好我们按照如上的方式来定义一个菜单。

MyMenu  MENU

{

    [Menu List here]

}

这和C语言中的结构体定义非常相似。MyMenu类似被定义的变量,而MENU类似于关键字。当然您也可以用另外一种办法,那就是用BEGIN和END来代替花括号,这和Pascal语言风格相近。

在顶层菜单项中是一大串MENUITEM和POPUP语句。MENUITEM定义了一个菜单项,当选择后不会激活对话框。它的语法如下:

    MENUITEM  “&text”, ID [,option]

它由关键字MENUITEM开头,紧跟在MENUITEM后面的是菜单项的名称字符串,符号“&”后的第一个字符将会带下划线,它是由该单项的快捷键。ID的作用是当该菜单被选中的时,Windows的消息过程处理函数用来区分菜单项的。毫无疑问,    ID必须是唯一的。option有以下选项。

1.GRAYED 代表该菜单项处于非激活状态,即当选被选中时不会产生WM_COMMAND消息。该菜单项以灰色显示。

2.INACTIVE 代表该菜单项处于非激活状态,即当被选中时不会产生WM_COMMAND消息。该菜单项以正常颜色显示。

3.MENUBREAK 该菜单项和随后的菜单项会显示在新列中。(译者著:比较难描述,请做实验)

4.help 该菜单项和随后的菜单项右对齐。

 

你可以单独使用以上标志也可以把这些标志痛过 or 连接起来。当然INACTIVE 和 GRAYED是不能痛过or连接的。 POPUP的语法如下:

    POPUP  “&text”  [,option]

    {

          [MENU    LIST]

    }

 

   POPUP定义了一个菜单项当该菜单项被选中的时候,会弹出一个子菜单。另外有一种特别类型MENUITE语句,MENUITEM SEPARATOR,它表示在菜单项位置画一条分割线。定义完菜单后,您就可以在程序中使用这些菜单资源了。您可以在程序的两个地方使用它们。

1.在WNDCLASSEXE结构体的lpszMenuName成员。譬如你有一个菜单“First Window”,你可以用如下方法将它联系到您的创口。

szMenuName   db  'firs Window', 0

mov  [@wc.lpszMenuName], szMenuName

2.在CreateWindowEx函数中指明菜单的句柄:

    szMenuName  db  ‘first Window’,0

    hMenu       dd      ?

    invoke   LoadMenu, [hInstnace], szMenuName

    mov     [hMenu], eax

    invoke CreateWindowEx,NULL,OFFSET ClsName,/
        OFFSET Caption, WS_OVERLAPPEDWINDOW,/
        CW_USEDEFAULT,CW_USEDEFAULT,/
        CW_USEDEFAULT,CW_USEDEFAULT,/
        NULL,/
        hMenu,/
        hInst,/
        NULL

 

你也许会问这有什么不同?

当您使用第一种方法的时刻,由于是在窗口类中指定。故所有由该窗口类派生下来的窗口都将有相同的菜单。如果您想要从相同的类中派生的窗口有不同的菜单,则必须用第二种方法,该方法通过CreateWindowEx指定的菜单会覆盖WNDCLASSEX结构中指定的菜单。接下来我们看看当用户选择一个菜单项时,它是如何来通知windows窗口过程函数的,当用户选择一个窗口菜单项时,Windows窗口过程函数会接受到一个WM_COMMAND消息。传进来的参数wparam参数包含了菜单项的ID。 好了上面就是菜单的一切,那么下面我们来实践。

       format PE GUI 4.0
       
        include 'win32ax.inc'
   
   
        macro memmov [dst, src]
        {
            common
            push [src]
            pop [dst]
        }
       
        IDM_TEST equ 1
        IDM_HELLO equ 2
        IDM_GOODBYE equ 3
        IDM_EXIT equ 4

.text
        ;**************数据********************
        szClassName db 'first Window',0
        szWndName db 'My first program',0
        szMenuName db 'MyMenu',0
        szTestString db 'you selected test menu item', 0
        HelloString db 'you selected hello menu item', 0
        goodstring db 'you selected goodbyte menu item',0
        hInstanse rd 1
        hIcon         rd 1
        hCursor         rd 1
        hWndow rd 1
        lpCommand rd 1
       
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, NULL, szClassName, szWndName, WS_OVERLAPPEDWINDOW,/
                        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,/
                        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
            invoke DefWindowProc,[hWnd],[uMsg],[wParam], [lParam]
            ret
    .command:
            mov eax ,[wParam]
            and eax, 0FFFFh
            cmp eax, IDM_TEST
            je _test
            cmp eax, IDM_HELLO
            je _hello
            cmp eax, IDM_GOODBYE
            je _good
            cmp eax, IDM_EXIT
            je _exit
            jmp Myend
    _test:
            invoke MessageBox,NULL, szTestString, '提示', MB_OK
            jmp Myend
           
    _hello:
        invoke MessageBox,NULL, HelloString, '提示', MB_OK
            jmp Myend
    _good:
            invoke MessageBox,NULL, goodstring, '提示', MB_OK   
            jmp Myend
    _exit:
        invoke DestroyWindow, [hWnd]
        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 'resoursename.RES'    

;*********************************************************Menu.RC**********************************************

#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4


MyMenu MENU
{
    POPUP "popup&"
        {
        MENUITEM "&say hello", IDM_HELLO, GRAYED
        MENUITEM "&say Good Byte", IDM_GOODBYE
        MENUITEM SEPARATOR
        MENUITEM "&say Exit", IDM_EXIT
        }
    MENUITEM "&test", IDM_TEST
}

 

分析:

    首先我们来分析资源脚本文件。我们可以看到

#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4

这几行定义资源ID号。 由于我们资源文件用的是vc的资源编译器,所以我们定义的时候要用C语言的语法。

我们只要知道这些资源ID号必须是唯一的,因为我们必须通过它在窗口过程函数中来区分各个菜单项。

 

POPUP "popup&"
{
        MENUITEM "&say hello", IDM_HELLO, GRAYED
        MENUITEM "&say Good Byte", IDM_GOODBYE
        MENUITEM SEPARATOR
        MENUITEM "&say Exit", IDM_EXIT
}

定义了一个有4个菜单项的子菜单。这个菜单有4个子菜单。其中第三个菜单项是一个分割线。

MENUITEM "&test", IDM_TEST 定义主菜单的一项。

我们现在来看源代码:

        szMenuName db 'MyMenu',0

        szTestString db 'you selected test menu item', 0
        HelloString db 'you selected hello menu item', 0
        goodstring db 'you selected goodbyte menu item',0
        exitstring db 'you selected exit menu item',0

     szMenuName是资源文件中指定菜单的名字。因为你可以在脚本文件中定义多个菜单,所以在使用前你必须指定要使用哪一个。接下来的行是在选中时弹出消息框的内容。

 

        IDM_TEST equ 1
        IDM_HELLO equ 2
        IDM_GOODBYE equ 3
        IDM_EXIT equ 4

  定义的菜单ID号,要和资源脚本文件中一样。

  cmp [uMsg], WM_COMMAND
   je .command

.command:
            mov eax ,[wParam]
            and eax, 0FFFFh
            cmp eax, IDM_TEST
            je _test
            cmp eax, IDM_HELLO
            je _hello
            cmp eax, IDM_GOODBYE
            je _good
            cmp eax, IDM_EXIT
            je _exit
            jmp Myend

在本窗口过程中我们处理的 WM_COMMAND消息。当用户选择一个菜单项时,该菜单项的菜单ID放入参数wparam中被同时送到windows窗口过程处理函数。我们把它保存到eax寄存器中以便比较。由于wparam的高2个字节是通知码,低2个字节是菜单ID,所以我们通过and将高两个字节设置为0,从而进行判断。前三种情况下我们选择Test , HELLO, GOODBYTE菜单项的时候, 会弹出一个消息框显示相关字符串。但是当选择Exit菜单项的时候我们发送DestroyWindow函数来销毁我们的窗口。从而发送WM_DESTROY消息,然后调用PostQuitMessage消息,从而发送WM_QUIT消息,然后关闭消息循环,结束程序。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章