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消息,然後關閉消息循環,結束程序。。

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