第八课 菜单
.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 
    } 
      
  
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
定义主菜单中的一项。下面我们来看看源代码。
      
  
    .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,其中的参数是我们窗口的句柄,这样就销毁了窗口。
就像您所看到的,通过在一个窗口类中指定菜单名的方法来给一个应用程序生成一个菜单是简单而直观的。除此方法外您还可以用另一种方法,其中资源文件是一样的,源文件中也只有少数的改动,这些改动如下:
      
  
        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刚返回的菜单句柄就可以了。