📄 tut32.html
字号:
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
;================================================
; Register the MDI child window class
;================================================
mov wc.lpfnWndProc,offset ChildProc
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszClassName,offset MDIChildClassName
invoke RegisterClassEx,addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW or WS_CLIPCHILDREN,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,0,\
hInst,NULL
mov hwndFrame,eax
<font color="#990033"> invoke LoadMenu,hInstance, IDR_CHILDMENU
mov hChildMenu,eax </font>
invoke ShowWindow,hwndFrame,SW_SHOWNORMAL
invoke UpdateWindow, hwndFrame
.while TRUE
invoke GetMessage,ADDR msg,NULL,0,0
.break .if (!eax)
invoke TranslateMDISysAccel,hwndClient,addr msg
.if !eax
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endif
.endw
invoke DestroyMenu, hChildMenu
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL ClientStruct:CLIENTCREATESTRUCT
.if uMsg==WM_CREATE
invoke GetMenu,hWnd
mov hMainMenu,eax
invoke GetSubMenu,hMainMenu,1
mov ClientStruct.hWindowMenu,eax
mov ClientStruct.idFirstChild,100
INVOKE CreateWindowEx,NULL,ADDR MDIClientName,NULL,\
WS_CHILD or WS_VISIBLE or WS_CLIPCHILDREN,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hWnd,NULL,\
hInstance,addr ClientStruct
mov hwndClient,eax
;=======================================
; Initialize the MDICREATESTRUCT
;=======================================
mov mdicreate.szClass,offset MDIChildClassName
mov mdicreate.szTitle,offset MDIChildTitle
push hInstance
pop mdicreate.hOwner
mov mdicreate.x,CW_USEDEFAULT
mov mdicreate.y,CW_USEDEFAULT
mov mdicreate.lx,CW_USEDEFAULT
mov mdicreate.ly,CW_USEDEFAULT
.elseif uMsg==WM_COMMAND
.if lParam==0
mov eax,wParam
.if ax==IDM_EXIT
invoke SendMessage,hWnd,WM_CLOSE,0,0
.elseif ax==IDM_TILEHORZ
invoke SendMessage,hwndClient,WM_MDITILE,MDITILE_HORIZONTAL,0
.elseif ax==IDM_TILEVERT
invoke SendMessage,hwndClient,WM_MDITILE,MDITILE_VERTICAL,0
.elseif ax==IDM_CASCADE
invoke SendMessage,hwndClient,WM_MDICASCADE,MDITILE_SKIPDISABLED,0
.elseif ax==IDM_NEW
invoke SendMessage,hwndClient,WM_MDICREATE,0,addr mdicreate
.elseif ax==IDM_CLOSE
invoke SendMessage,hwndClient,WM_MDIGETACTIVE,0,0
invoke SendMessage,eax,WM_CLOSE,0,0
.else
invoke DefFrameProc,hWnd,hwndClient,uMsg,wParam,lParam
ret
.endif
.endif
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefFrameProc,hWnd,hwndClient,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
ChildProc proc hChild:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
.if uMsg==WM_MDIACTIVATE
mov eax,lParam
.if eax==hChild
invoke GetSubMenu,hChildMenu,1
mov edx,eax
invoke SendMessage,hwndClient,WM_MDISETMENU,hChildMenu,edx
.else
invoke GetSubMenu,hMainMenu,1
mov edx,eax
invoke SendMessage,hwndClient,WM_MDISETMENU,hMainMenu,edx
.endif
invoke DrawMenuBar,hwndFrame
.elseif uMsg==WM_CLOSE
invoke MessageBox,hChild,addr ClosePromptMessage,addr AppName,MB_YESNO
.if eax==IDYES
invoke SendMessage,hwndClient,WM_MDIDESTROY,hChild,0
.endif
.else
invoke DefMDIChildProc,hChild,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
ChildProc endp
end start
</pre>
<h3><font face="MS Sans Serif" size="-1">Analysis:</font></h3>
<p><font face="MS Sans Serif" size="-1">The first thing the program does is to
register the window classes of the frame window and the MDI child window. After
that, it calls CreateWindowEx to create the frame window. Within the WM_CREATE
handler of the frame window, we create the client window:</font> <br>
</p>
<pre>
LOCAL ClientStruct:CLIENTCREATESTRUCT
.if uMsg==WM_CREATE
invoke GetMenu,hWnd
mov hMainMenu,eax
invoke GetSubMenu,hMainMenu,1
<font color="#990033"><b> mov ClientStruct.hWindowMenu,eax
mov ClientStruct.idFirstChild,100 </b></font>
invoke CreateWindowEx,NULL,ADDR MDIClientName,NULL,\
WS_CHILD or WS_VISIBLE or WS_CLIPCHILDREN,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hWnd,NULL,\
hInstance,<font color="#990033"><b>addr ClientStruct</b></font>
mov hwndClient,eax
</pre>
<p><font face="MS Sans Serif" size="-1">It calls <font color="#000099"><b>GetMenu</b></font>
to obtain the handle to the menu of the frame window, to be used in the <font color="#000066"><b>GetSubMenu</b></font>
call. Note that we pass the value 1 to <font color="#000099"><b>GetSubMenu</b></font>
because the submenu we want the window list to appear is the second submenu.
Then we fill the members of the CLIENTCREATESTRUCT structure.</font><br>
<font face="MS Sans Serif" size="-1">Next, we initialize the MDICLIENTSTRUCT
structure. Note that we don't need to do it here. It's only convenient to do
it in WM_CREATE.</font></p>
<pre> mov mdicreate.szClass,offset MDIChildClassName
mov mdicreate.szTitle,offset MDIChildTitle
push hInstance
pop mdicreate.hOwner
mov mdicreate.x,CW_USEDEFAULT
mov mdicreate.y,CW_USEDEFAULT
mov mdicreate.lx,CW_USEDEFAULT
mov mdicreate.ly,CW_USEDEFAULT
</pre>
<p><font face="MS Sans Serif" size="-1">After the frame window is created (and
also the client window), we call <font color="#000099"><b>LoadMenu</b></font>
to load the child window menu from the resource. We need to get this menu handle
so we can replace the menu of the frame window with it when an MDI child window
is present. Don't forget to call <font color="#000099"><b>DestroyMenu</b></font>
on the handle before the application exits to Windows. Normally Windows will
free the menu associated with a window automatically when the application exits
but in this case, the child window menu is not associated with any window thus
it will still occupy valuable memory even after the application exits.</font><br>
</p>
<pre> invoke LoadMenu,hInstance, IDR_CHILDMENU
mov hChildMenu,eax
........
invoke DestroyMenu, hChildMenu
</pre>
<p><font face="MS Sans Serif" size="-1">Within the message loop, we call <font color="#000099"><b>TranslateMDISysAccel</b></font>.</font></p>
<pre> .while TRUE
invoke GetMessage,ADDR msg,NULL,0,0
.break .if (!eax)
<font color="#990066"><b>invoke TranslateMDISysAccel,hwndClient,addr msg</b></font>
.if !eax
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endif
.endw </pre>
<p><font face="MS Sans Serif" size="-1">If <font color="#000099"><b>TranslateMDISysAccel</b></font>
returns a non-zero value, it means the message was already handled by Windows
itself so you don't need to do anything to the message. If it returns 0, the
message is not MDI-related and thus should be handled as usual.</font><br>
</p>
<pre>WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.....
.else
<font color="#990033"><b> invoke DefFrameProc,hWnd,hwndClient,uMsg,wParam,lParam
ret </b></font>
.endif
xor eax,eax
ret
WndProc endp</pre>
<p><font face="MS Sans Serif" size="-1">Note that within the window procedure
of the frame window, we call DefFrameProc to handle the messages we are not
interested in.</font></p>
<p><font face="MS Sans Serif" size="-1">The bulk of the window procedure is the
WM_COMMAND handler. When the user selects "New" from the File menu,
we create a new MDI child window.</font></p>
<pre> .elseif ax==IDM_NEW
invoke SendMessage,hwndClient,WM_MDICREATE,0,addr mdicreate
</pre>
<p><font face="MS Sans Serif" size="-1">In our example, we create the MDI child
window by sending WM_MDICREATE to the client window, passing the address of
the MDICREATESTRUCT structure in lParam.</font></p>
<pre>ChildProc proc hChild:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
.if uMsg==WM_MDIACTIVATE
mov eax,lParam
.if eax==hChild
invoke GetSubMenu,hChildMenu,1
mov edx,eax
invoke SendMessage,hwndClient,WM_MDISETMENU,hChildMenu,edx
.else
invoke GetSubMenu,hMainMenu,1
mov edx,eax
invoke SendMessage,hwndClient,WM_MDISETMENU,hMainMenu,edx
.endif
invoke DrawMenuBar,hwndFrame </pre>
<p><font face="MS Sans Serif" size="-1">When the MDI child window is created,
it monitors WM_MDIACTIVATE to see if it's the active window. It does this by
comparing the value of the lParam which contains the handle of the active child
window with its own handle. If they match, it's the active window and the next
step is to replace the menu of the frame window to its own. Since the original
menu will be replaced, you have to tell Windows again in which submenu the window
list should appear. That's why we must call <font color="#000099"><b>GetSubMenu</b></font>
again to retrieve the handle to the submenu. We send WM_MDISETMENU message to
the client window to achieve the desired result. wParam of WM_MDISETMENU contains
the handle of the menu you would like to replace the original menu. lParam contains
the handle of the submenu you want the window list to appear. Right after sending
WM_MDISETMENU, we call <font color="#000099"><b>DrawMenuBar</b></font> to refresh
the menu else your menu will be a mess.</font></p>
<pre> .else
invoke DefMDIChildProc,hChild,uMsg,wParam,lParam
ret
.endif </pre>
<pre><font face="MS Sans Serif" size="-1">Within the window procedure of the MDI child window, you must pass all unhandled messages to <font color="#000099"><b>DefMDIChildProc</b></font> instead of <font color="#003399"><b>DefWindowProc</b></font>.</font></pre>
<pre> .elseif ax==IDM_TILEHORZ
invoke SendMessage,hwndClient,WM_MDITILE,MDITILE_HORIZONTAL,0
.elseif ax==IDM_TILEVERT
invoke SendMessage,hwndClient,WM_MDITILE,MDITILE_VERTICAL,0
.elseif ax==IDM_CASCADE
invoke SendMessage,hwndClient,WM_MDICASCADE,MDITILE_SKIPDISABLED,0 </pre>
<p><font face="MS Sans Serif" size="-1">When the user selects one of the menuitems
in the window submenu, we send the corresponding message to the client window.
If the user chooses to tile the windows, we send WM_MDITILE to the client window,
specifying in wParam what kind of tiling we want. WM_CASCADE is similar.</font></p>
<pre> .elseif ax==IDM_CLOSE
invoke SendMessage,hwndClient,WM_MDIGETACTIVE,0,0
invoke SendMessage,eax,WM_CLOSE,0,0 </pre>
<pre><font face="MS Sans Serif" size="-1">If the user chooses "Close" menuitem, we must obtain the handle of the currently active MDI child window first by sending WM_MDIGETACTIVE to the client window. The return value in eax is the handle of the currently active MDI child window. After that, we send WM_CLOSE to that window.</font></pre>
<pre> .elseif uMsg==WM_CLOSE
invoke MessageBox,hChild,addr ClosePromptMessage,addr AppName,MB_YESNO
.if eax==IDYES
invoke SendMessage,hwndClient,WM_MDIDESTROY,hChild,0
.endif </pre>
<p><font face="MS Sans Serif" size="-1">Within the window procedure of the MDI
child, when WM_CLOSE is received, it displays a message box asking the user
if he really wants to close the window. If the answer is yes, we send WM_MDIDESTROY
to the client window. WM_MDIDESTROY closes the MDI child window and restores
the title of the frame window.</font></p>
<hr>
<p align="center"><font face="MS Sans Serif" size="-1"><b>[<a href="http://win32asm.cjb.net">Iczelion's
Win32 Assembly Homepage</a>]</b></font></p>
<p> </p>
<pre> </pre>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -