程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 匯編教程之菜單

匯編教程之菜單

編輯:匯編語言

本課中我們將在我們的應用程序中加入一個菜單。

理論:

菜單可以說是WINDOWS最重要的元素之一。有了它,用戶可以方便地選擇操作命令.用戶只要細讀一下所有的菜單項就可以明了應用程序所提供的大概功能,而且可以立即操作,無須去閱讀手冊了.正因為菜單給了用戶一種方便的方式,所以您在應用程序中加入菜單時就要遵守一般的標准.譬如:一般頭兩個菜單項是"File"和"Edit",最後是"Help",您可以在這中間插入您要定義的菜單項.如果所運行的菜單命令會彈出一個對話框,那麼就要在該菜單項後加入省略符(...).菜單是一種資源,除菜單外還有其它像對話框,字符串,圖標,位圖資源等.在鏈接時鏈接程序將把資源加入到可執行程序中去,最後我們的執行程序中就既包括機器指令又包括了資源. 您可以在任何文本編輯器中編寫腳本文件,在文件中您可以指定資源呈現出來的外觀和其它的一些屬性.當然更直觀的方法是用資源編輯器,通常資源編輯器都打包在編譯環境中,像Visual C++, Borland C++等都帶了資源編輯器. 我們可以按以下方式來定義一個菜單資源:

MyMenu MENU
{
[menu list here]
}

這和C語言中的結構體的定義非常相似。 MyMenu類似於被定義的變量,而MENU則類似於關鍵字。當然您可以用另外一種辦法,那就是用BEGIN和END來代替花括號,這和PASCAL語言中的風格相同。

在菜單項的列表中是一大串的MENUITEM和POPUP語句。MENUITEM定義了一個菜單項,當選擇後不會激活對話框。它的語法如下:

MENUITEM "&text", ID [,options]

它由關鍵字MENUITEM開頭,緊跟在MENUITEM後的是指菜單項的名稱字符串,符號“&“後的第一個字符將會帶下畫線,它也是該菜單項的快捷鍵。ID的作用當該菜單被選中時,WINDOWS的消息處理過程用來區分菜單項用的。毫無疑問,ID號必須唯一。options有以下可供選擇的屬性:

GRAYED 代表該菜單項處於非激活狀態,即當其被選中時不會產生WM_COMMAND消息。該菜單以灰色顯示。

INACTIVE 代表該菜單項處於非激活狀態,即當其被選中時不會產生WM_COMMAND消息。該菜單以正常顏色顯示。

MENUBREAK 該菜單項和隨後的菜單項會顯示在新列中。(譯者注:比較難描述,請做實驗。)

HELP 該菜單項和隨後的菜單項右對齊。(譯者注:我在WINDOWS2000下編譯有該標志的菜單項,該標志好像沒起作用)

您可以單獨使用以上標志位,也可以把它們或在一起。當然INACTIVE和GRAYED不能同時使用。 POPUP的語法如下:

POPUP "&text" [,options]
{
[menu list]
}

POPUP定義了一個菜單項當該菜單項被選中時又會彈出一個子菜單。另外有一種特別類型的MENUITEM語句MENUITEM SEPARATOR,它表示在菜單項位置畫一條分隔線。定義完菜單後,您就可以在程序中使用腳本中定義的菜單資源了。您可以在程序的兩個地方(或叫做用兩種方式)使用它們:

在WNDCLASSEX結構體的成員lpszMenuName中。譬如,您有一個菜單“FirstMenu“,您可以按如下方法把它聯系到您的窗口:

.DATA
MenuName db "FirstMenu",0
...........................
...........................
.CODE
...........................
mov wc.lpszMenuName, OFFSET MenuName
...........................
在CreateWindowEx函數中指明菜單的句柄:
.DATA
MenuName db "FirstMenu",0
hMenu HMENU ?
...........................
...........................
.CODE
...........................
invoke LoadMenu, hInst, OFFSET MenuName
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號。好了,上面就是關於菜單項的一切,下面我們就來實踐。

例子:

第一個例子顯示了指定一個菜單項的第一種方法:

.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.const
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName ; Put our menu name here
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start

**************************************************************************************************************************

Menu.rc

**************************************************************************************************************************

#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
FirstMenu MENU
{
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
MENUITEM "&Test", IDM_TEST
}

分析:

我們先來分析資源文件:

#define IDM_TEST 1 /* equal to IDM_TEST equ 1*/
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4

上面的幾行定義了菜單項的ID號。只要注意菜單項ID號必須唯一外,您可以給ID號任何值。

FirstMenu MENU

用關鍵字MENU定義菜單。

POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}

定義一個有四個菜單項的子菜單,其中第三個菜單項是一個分隔線。

MENUITEM "&Test", IDM_TEST

定義主菜單中的一項。下面我們來看看源代碼。

MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0

MenuName是資源文件中指定的菜單的名字。因為您可以在腳本文件中定義任意多個菜單,所以在使用前必須指定您要使用那一個,接下來的行是在選中菜單項時顯示在相關對話框中的字符串。

IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4

定義用在WINDOWS窗口過程中的菜單項ID號。這些值必須和腳本文件中的相同。

.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF

在本窗口過程中我們處理WM_COMMAND消息。當用戶選擇了一個菜單項時,該菜單項的ID放入參數wParam中被同時送到WINDOWS的窗口過程,我們把它保存到eax寄存器中以便和預定義的菜單項ID比較用。前三種情況下,當我們選中Test、Say Hello、Say GoodBye菜單項時,會彈出一個對話框其中顯示一個相關的字符串,選擇Exit菜單項時,我們就調用函數DestroyWindow,其中的參數是我們窗口的句柄,這樣就銷毀了窗口。就像您所看到的,通過在一個窗口類中指定菜單名的方法來給一個應用程序生成一個菜單是簡單而直觀的。除此方法外您還可以用另一種方法,其中資源文件是一樣的,源文件中也只有少數的改動,這些改動如下:

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HMENU ? ; handle of our menu

定義了一個變量來保存我們的菜單的句柄,然後:

invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu,eax
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,\
hInst,NULL

調用LoadMenu函數,該函數需要實例句柄和菜單名的字符串,調用的結果返回指向菜單的句柄,然後傳給函數CreateWindowEx剛返回的菜單句柄就可以了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved