| ||
Examples are essential for mastering the art of programming. It is impossible to study programming only on the basis of theory. Programming is closer to art. It is a way of self-actualization.
The topic of resource usage in Windows programming is important; therefore, it deserves a separate chapter. Here, I provide three more sophisticated programs illustrating the use of resources and supply detailed explanations .
You probably have already noticed that in many programs, menus can dynamically change during program execution: Some items are added or removed, one menu appears as the built-in menu of another one, etc. An example illustrating the simplest manipulations with a menu is provided in Listing 10.1.
// The MENU2.RC file // Virtual key - <F5> #define VK_F5 0x74 //********* MENUP *********** MENUP MENU { POPUP "&First item" { MENUITEM "&First", 1 MENUITEM "S&econd", 2 } POPUP "&Second item" { MENUITEM "Thir&d", 3 MENUITEM "Fou&rth\tF5", 4 MENUITEM SEPARATOR POPUP "Another submen&u" { MENUITEM "Additional &item", 6 } } MENUITEM "E&xit", 5 } //********* MENUC *********** MENUC MENU { POPUP "Set 1" { MENUITEM "White", 101 MENUITEM "Gray", 102 MENUITEM "Black", 103 } POPUP "Set 2" { MENUITEM "Red", 104 MENUITEM "Blue", 105 MENUITEM "Green", 106 } } // Accelerator table // One accelerator is defined // for calling an item from the MENUP menu MENUP ACCELERATORS { VK_F5, 4, VIRTKEY, NOINVERT } ; The MENU2.INC file ; Constants ; The message arrives when the window is closed WM_DESTROY equ 2 ; The message arrives when the window is created WM_CREATE equ 1 ; The message arrives when the user clicks the ; left mouse button in the window WM_COMMAND equ 111h WM_MENUSELECT equ 11 Fh WM_SETTEXT equ 0Ch MIIM_TYPE equ 10h MF_STRING equ 0h MF_POPUP equ 10h ; Window properties CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_OVERLAPPEDWINDOW equ 000CF0000H STYLE equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS BS_DEFPUSHBUTTON equ 1h WS__VISIBLE equ l0000000h WS_CHILD equ 40000000h STYLBTN equ WS_CHILD+BS_DEFPUSHBUTTON+WS_VISIBLE ; Standard icon identifier IDI_APPLICATION equ 32512 ; Cursor identifier IDC_ARROW equ 32512 ; Window display mode --- Normal SW_SHOWNORMAL equ 1 SW_HIDE equ 0 SW_SHOWMINIMIZED equ 2 ; Prototypes of external procedures EXTERN wsprintfA:NEAR EXTERN GetMenuItemInfoA@16:NEAR EXTERN LoadMenuA@8:NEAR EXTERN SendMessageA@16:NEAR EXTERN MessageBoxA@16:NEAR EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR EXTERN TranslateAcceleratorA@12:NEAR EXTERN LoadAcceleratorsA@8:NEAR EXTERN GetMenu@4:NEAR EXTERN DestroyMenu@4:NEAR EXTERN SetMenu@8:NEAR ; Structures ; Message structure MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ;---- Window class structure WNDCLASS STRUC CLSSTYLE DD ? CLWNDPROC DD ? CLSCBCLSEX DD ? CLSCBWNDEX DD ? CLSHINST DD ? CLSHICON DD ? CLSHCURSOR DD ? CLBKGROUND DD ? CLMENNAME DD ? CLNAME DD ? WNDCLASS ENDS MENINFO STRUCT CbSize DD ? FMask DD ? FType DD ? FState DD ? WID DD ? HSubMenu DD ? HbmpChecked DD ? HbmpUnchecked DD ? DwItemData DD ? DwTypeData DD ? Cch DD ? MENINFO ENDS ; The MENU2.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall include menu2.inc ; INCLUDELIB directives for the linker to link libraries includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;-------------------- ; Data segment _DATA SEGMENT SPACE DB 30 dup(32), 0 MENI MENINFO <0> NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> HINST DD 0 ; Application descriptor CLASSNAME DB 'CLASS32', 0 CPBUT DB 'BUTTON', 0 ; Exit CLSBUTN DB 'BUTTON', 0 HWNDBTN DD 0 CAP DB 'Message', 0 MES DB 'Exit the program', 0 MEN DB 'MENUP', 0 MENC DB 'MENUC', 0 ACC DD ? HMENU DD ? PRIZN DD ? BUFER DB 100 DUP(0) _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Initialize the counter MOV PRIZN, 2 ; Get the application descriptor PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; Fill the window structure ; Style MOV [WC.CLSSTYLE], STYLE ; Message-handling procedure MOV [WC.CLWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEX], 0 MOV [WC.CLSCBWNDEX], 0 MOV EAX, [HINST] MOV [WC.CLSHINST], EAX ;----------Window icon PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;---------- Window cursor PUSH IDC_ARROW PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;---------- MOV [WC.CLBKGROUND], 17 ; Window color MOV DWORD PTR [WC.CLMENNAME], OFFSET MEN MOV DWORD PTR [WC.CLNAME], OFFSET CLASSNAME PUSH OFFSET WC CALL RegisterClassA@4 ; Create a window of the registered class PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH 400 ; DY Window height PUSH 400 ; DX Window width PUSH 100 ; Y PUSH 100 ; X PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET SPACE ; Window name PUSH OFFSET CLASSNAME ; Class name PUSH 0 CALL CreateWindowExA@48 ; Check for errors CMP EAX, 0 JZ _ERR MOV [NEWHWND], EAX ; Window descriptor ; Determine the menu identifier PUSH EAX CALL GetMenu@4 MOV HMENU, EAX ; Load the accelerators PUSH OFFSET MEN PUSH [HINST] CALL LoadAcceleratorsA@8 MOV ACC, EAX ;------------------------------ PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ; Show the newly-created window ;------------------------------ PUSH [NEWHWND] CALL UpdateWindow@4 ; Redraw the visible part of the ; window, the WM_PAINT message ; Message-processing loop MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP EAX, 0 JE END_LOOP PUSH OFFSET MSG PUSH [ACC] PUSH [NEWHWND] CALL TranslateAcceleratorA@12 CMP EAX, 0 JNE MSG_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG__LOOP END_LOOP: ; Exit the program (close the process) PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ;---------------------- ; Window procedure ; Position of parameters in the stack ; [EBP+014H] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI ; The WM_DESTROY message When closing the window CMP DWORD PTR [EBP+0CH], WM_DESTROY JE WMDESTROY ; The WM_CREATE message When creating the window CMP DWORD PTR [EBP+0CH], WM_CREATE JE WMCREATE ; The WM_COMMAND message In case events ; that happen to the window elements CMP DWORD PTR [EBP+0CH], WM_COMMAND JE WMCOMMND ; The WM_MENUSELECT message Menu-related events CMP DWORD PTR [EBP+0CH], WM_MENUSELECT JE WMMENUSELECT ; Returning all other messages JMP DEFWNDPROC WMMENUSELECT: ; Skipping the first event when accessing the menu CMP WORD PTR [EBP+14H], 0 JE FINISH ; Checking what has been activated --- menu item ; or popup menu header MOV EDX, 0 TEST WORD PTR [EBP+12H], MF_POPUP SETNE DL ; Filling the structure for function call ; GetMenuItemInfo MOVZX EAX, WORD PTR [EBP+10H] ; Identifier MOV MENI.cbSize, 48 MOV MENI.fMask, MIIM_TYPE MOV MENI.fType, MF_STRING MOV EBX, DWORD PTR [EBP+14H] MOV MENI.hSubMenu, EBX MOV MENI.dwTypeData, OFFSET BUFER MOV MENI.cch, 100 ; Get information about the chosen menu item PUSH OFFSET MENI PUSH EDX PUSH EAX PUSH DWORD PTR [EBP+14H] CALL GetMenuItemInfoA@16 ; Check the result of function execution CMP EAX, 0 JE FINISH ; Display the menu item name PUSH MENI.dwTypeData PUSH 0 PUSH WM_SETTEXT PUSH DWORD PTR [EBP+08H] CALL SendMessageA@16 MOV EAX, 0 JMP FINISH WMCOMMND: MOV EAX, HWNDBTN ; Check whether the button has been pressed CMP DWORD PTR [EBP+14H], EAX JE YES_BUT ; Check whether the Exit menu item of MENUC has been chosen CMP WORD PTR [EBP+10H], 5 JE WMDESTROY ; Check whether the menu item with the identifier 5 has been chosen CMP WORD PTR [EBP+10H], 4 JNE LOO JMP YES_BUT LOO: MOV EAX, 0 JMP FINISH YES_BUT: ; Button-click handling ; First, erase the name in the header PUSH OFFSET SPACE PUSH 0 PUSH WM_SETTEXT PUSH DWORD PTR [EBP+08H] CALL SendMessageA@16 ; Check whether the menu has been loaded CMP PRIZN, 0 JE L1 CMP RIZN, 1 JE L2 ; Load MENC PUSH OFFSET MENC PUSH [HINST] CALL LoadMenuA@8 ; Set the menu MOV HMENU, EAX PUSH EAX PUSH DWORD PTR [EBP+08H] CALL SetMenu@8 ; Set the identifier MOV PRIZN, 0 MOV EAX, 0 JMP FINISH L2: ; Load MENUP PUSH OFFSET MEN PUSH [HINST] CALL LoadMenuA@8 ; Set the menu MOV HMENU, EAX PUSH EAX PUSH DWORD PTR [EBP+08H] CALL SetMenu@8 ; Set the indicator MOV PRIZN, 2 MOV EAX, 0 JMP FINISH L1: ; Delete the menu PUSH HMENU CALL DestroyMenu@4 ; Refresh the window contents PUSH SW_SHOWMINIMIZED PUSH DWORD PTR [EBP+08H] CALL ShowWindow@8 PUSH SW_SHOWNORMAL PUSH DWORD PTR [EBP+08H] CALL ShowWindow@8 MOV PRIZN, 1 MOV EAX, 0 JMP FINISH WMCREATE: ; Create a window button PUSH 0 PUSH [HINST] PUSH 0 PUSH DWORD PTR [EBP+08H] PUSH 20 ; DY PUSH 60 ; DX PUSH 10 ; Y PUSH 10 ; X PUSH STYLBTN ; Window name (button label) PUSH OFFSET CPBUT PUSH OFFSET CLSBUTN ; Class name PUSH 0 CALL CreateWindowExA@48 MOV HWNDBTN, EAX ; Store the button descriptor MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: PUSH 0 ; MB_OK PUSH OFFSET CAP PUSH OFFSET MES PUSH DWORD PTR [EBP+08H] ; Window descriptor CALL MessageBoxA@16 PUSH 0 CALL PostQuitMessage@4 ; The WM_QUIT message MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START
The program opens a window with a menu and a command button. When the user clicks the button, another menu replaces the current one. If the button is clicked again, the menu disappears. Repeatedly clicking the button displays the first menu, the second menu, and so on, cyclically. Additionally, the first menu contains the item that, when chosen, produces the same result as clicking the button. Finally, for this menu item there is an accelerator key<F5>. When scrolling the menu, the titles of menu items and popup menus are displayed in the window header. This, briefly , is the way of the program's operation. The mechanisms according to which the program works are described in detail after Listing 10.1.
The program provided in Listing 10.1 implements several mechanisms that I need to describe. First, note that the program uses three resources: two menus and one accelerator table (see the resource file).
First, I'd like to draw your attention to the PRIZN variable. It stores the menu status: 2 means the MENUP menu has been loaded; 1 means no menu; and 0 means the initial menu, MENUC , has been loaded. The initial state is ensured by specifying the menu when registering the window class:
MOV DWORD PTR [WC.CLMENNAME], OFFSET MEN
The second aspect that deserves attention is the button. The mechanism used for detecting button-clicking events has already been considered , so I won't concentrate on it any more. One of the events that can take place as a result of the button-clicking events is menu removal. The menu is removed using the DestroyMENU function. After removing the menu, it is necessary to refresh the window by calling the ShowWindow function twice.
Another event that takes place when the button is clicked is menu change. The menu will be changed automatically if you load and install a new menu.
Choosing one of the items of the MENUP menu also changes the menu. Here, everything should be clear to you, because the same section of the program is accessed like in case of the button click.
An interesting situation arises with the accelerator. The accelerator key is <F5>. When this key is pressed, the same message is generated as when choosing the "fourth" item of the MENUP menu. The important point is that the same message will be generated when MENUC is loaded and when no menu is displayed. Because the procedure processes the message in either case, the <F5> key will also work in either case.
Now, consider how the name of the chosen menu item is determined. The central role in this mechanism is delegated to the WM_MENUSELECT message. This message arrives any time a menu item is selected. It is important to note that when you activate the menu, the WM_MENUSELECT message will be the first to arrive . This message has the LPARAM value that defines the menu identifier as zero. This is achieved by the following lines of code:
CMP WORD PTR [EBP+14H], 0 JE FINISH
When receiving the WM_MENUSELECT message, the least significant word of the WPARAM parameter can contain either the identifier of the menu item or the number of the header of the popup menu. This is the key point. You must know this information, because the header string of the popup menu item and the string of the menu item are obtained differently. To define what has been chosen, it is possible to use the most significant word of WPARAM. For this purpose, use the MF_POPUP constant: TEST WORD PTR [EBP+12H], MF_POPUP . Note how convenient the SETNE command is here.
Furthermore, to get the name string, use the GetMenuItemInfo function. The third parameter of this function can be either 0 or 1. If 0 is chosen, then the second parameter is the menu item identifier. If 1 is chosen, then the second parameter is the identifier of the popup menu header. The fourth parameter is the pointer to the structure that will be filled as a result of function execution. However, some fields of this structure must be filled beforehand. I draw your attention to the dwTypeData field, which must contain the pointer to the buffer containing the required string. The cch field must contain the length of that buffer. However, for the function to interpret the dwTypeData and cch fields exactly as the pointer to the buffer and the buffer length, the fields fMask and fType must be filled properly (see Listing 10.1). Finally, the cbsize field must contain the length of the entire structure.
Having received the required information (e.g., the menu item name string), send the WM_SETTEXT message using the SendMessage function. This message gives the command to set the window header.
| ||