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",0MenuName是资源文件中指定的菜单的名字。因为您可以在脚本文件中定义任意多个菜单,所以在使用前必须指定您要使用那一个,接下来的行是在选中菜单项时显示在相关对话框中的字符串。
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刚返回的菜单句柄就可以了。