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
**************************************************************************************************************************
#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刚返回的菜单句柄就可以了。