📄 lion-tut-c32.htm
字号:
<ol>
<li>获取你所希望显示窗口列表的子菜单的句柄. </li>
<li>将这个菜单句柄的值和你希望作为第一个MDI子窗口标识符的值一起传送给CLIENTCREATESTRUCT 结构. </li>
<li>调用 CreateWindowEx 用类名"MDICLIENT" ,lParam参数为CLIENTCREATESTRUCT结构的地址,
</li>
</ol>
<h3>创建MDI子窗口</h3>
<p>现在我们既有了框架窗口,也有了客户窗口. 下一阶段可以开始创建MDI子窗口了.有两种方法:
<ul>
<li>你可以发送 WM_MDICREATE消息给<b>客户窗口</b>,在wParam参数中传递类型MDICREATESTRUCT的结构的地址. 这是常用的也是最简单的MDI子窗口的创建方法.
<blockquote>
<pre><b>.data?
mdicreate MDICREATESTRUCT <>
....
.code
.....
[fill the members of mdicreate]
......
invoke SendMessage, hwndClient, WM_MDICREATE,addr mdicreate,0</b></pre>
</blockquote>
<p>假如创建成功的话, <b><font
color="#003399">SendMessage</font></b> 将会返回新创建的MDI子窗口的句柄. 你并不需要保存这个句柄. 如果你需要的话,
你可以通过其它的方法来获得它. MDICREATESTRUCT有如下定义.</p>
<blockquote>
<p><b>MDICREATESTRUCT STRUCT<br>
szClass DWORD ?<br>
szTitle DWORD ?<br>
hOwner DWORD ?<br>
x DWORD
?<br>
y DWORD
?<br>
lx DWORD
?<br>
ly DWORD
?<br>
style DWORD ?<br>
lParam DWORD ?<br>
MDICREATESTRUCT ENDS</b></p>
</blockquote>
</li>
</ul>
<blockquote>
<table border="1" cellPadding="3">
<TBODY>
<tr>
<th noWrap>szClass</th>
<td>你作为MDI自窗口模板的窗口类的地址</td>
</tr>
<tr>
<th noWrap>szTitle</th>
<td>你希望出现在子窗口的标题栏的文本的地址</td>
</tr>
<tr>
<th noWrap>hOwner</th>
<td>应用程序的例程句柄</td>
</tr>
<tr>
<th noWrap>x,y,lx,ly</th>
<td>子窗口的左上角的坐标以及宽度和高度</td>
</tr>
<tr>
<th noWrap>style</th>
<td>子窗口风格. 假若你用MDIS_ALLCHILDSTYLES创建子窗口的话,你可以使用任何窗口风格. </td>
</tr>
<tr>
<th noWrap>lParam</th>
<td>一个应用程序定义的32位值. 这是在MDI窗口中共享值的一种方法. 如果你不需要它, 将它设为NULL.</td>
</tr>
</TBODY>
</table>
</blockquote>
<ul>
<li>你可以调用 <font
color="#003399"><b>CreateMDIWindow</b></font>. 这一个功能具有下列语法: </li>
</ul>
<ul>
<blockquote>
<pre><b>CreateMDIWindow proto lpClassName:DWORD
lpWindowName:DWORD
dwStyle:DWORD
x:DWORD
y:DWORD
nWidth:DWORD
nHeight:DWORD
hWndParent:DWORD
hInstance:DWORD
lParam:DWORD</b></pre>
</blockquote>
</ul>
<blockquote>
<p>如果你仔细地看一下这些参数, 你将会发现它们和MDICREATESTRUCT结构的成员是相同的, 除了 <b>hWndParent</b>.以外.
本质上它和你用WM_MDICREATE传送的参数数目是相同的. MDICREATESTRUCT不需要<b>hWndParent</b> 域, 因为你必须用Sendmessage传送整个结构给正确的子窗口.
.</p>
</blockquote>
<p>在这里,你也许会有一些问题: 我应该使用哪一种方法? 在这两者之间有什么区别? 答案如下:</p>
<p> WM_MDICREATE方法创建的MDI子窗口作为调用代码是同一个线程.这意味这假如这个应用程序只有一个主线程的话, 所有的MDI子窗口都在这个主线程中运行.
这并不是一个大的问题. 但是如果一个或是多个你的MDI子窗口执行一些较长的操作的话, 问题就来了. 想象一下你的整个的应用程序突然之间停止了,对任何事情都没有反应,
一直持续到MDI子窗口的操作结束.</p>
<p>这个问题正是<b><font
color="#000099">CreateMDIWindow</font></b> 设计了所要解决的. <b><font color="#000099">CreateMDIWindow
</font></b>为每一个MDI子窗口创建了一个单独的线程. 这样假如一个MDI子窗口忙的话, 它不会拖累整个应用程序..</p>
<p>有关MDI子窗口的窗口过程还有一点需要说明的地方. 对于框架窗口, 你不能调用<b><font
color="#000099">DefWindowProc</font></b>来处理未处理的消息. 与之相反你必须在自窗口的窗口过程中使用<font color="#000099"><b>DefMDIChildProc</b></font>
. 这个函数具有和<b><font color="#000099">DefWindowProc</font></b>相同的参数.</p>
<p>除了WM_MDICREATE以外,还有其它的MDI相关的窗口消息. 列表如下:</p>
<table align="center" border="1" cellPadding="3" width="667">
<TBODY>
<tr>
<th noWrap width="177">WM_MDIACTIVATE</th>
<td width="466">这条消息由应用程序发送给客户窗口,告诉客户窗口激活所选择的MDI子窗口. 当客户窗口受到消息后, 它将激活所选择的MDI子窗口和发送WM_MDIACTIVATE消息给将被激活的子窗口和将变为非活动窗口的子窗口.
这条消息的用途是双方面的:应用程序可以用它来激活所希望的子窗口.同时它又可以被MDI子窗口本身用作活动/非活动窗口的指示器.举个例子,假如每一个MDI子窗口都有不同的菜单,
那么当它变为活动或是非活动窗口的时候,它可以利用这个机会来改变框架窗口的菜单</td>
</tr>
<tr>
<th noWrap width="177">WM_MDICASCADE<br>
WM_MDITILE<br>
WM_MDIICONARRANGE </th>
<td width="466">这些消息处理如何排列MDI子窗口. 举个例子, 假如你希望MDI子窗口排列成层叠的样式,发送WM_MDICASCADE消息给客户窗口.</td>
</tr>
<tr>
<th noWrap width="177">WM_MDIDESTROY</th>
<td width="466">发送这条消息给客户窗口来关闭一个MDI子窗口. 你应该使用这条消息而不是调用<font color="#000099"><b>DestroyWindow</b></font>
因为假如这个MDI子窗口最大化的话, th这条消息将会恢复框架窗口的标题. 假如你使用<font color="#000099"><b>DestroyWindow</b></font>,
框架窗口的标题将不会被恢复.</td>
</tr>
<tr>
<th noWrap width="177">WM_MDIGETACTIVE</th>
<td width="466">发送这条消息检索当前活动MDI子窗口的句柄.</td>
</tr>
<tr>
<th noWrap width="177">WM_MDIMAXIMIZE<br>
WM_MDIRESTORE </th>
<td width="466">发送 WM_MDIMAXIMIZE来最大化MDI子窗口和WM_MDIRESTORE来将它恢复成以前的状态. 对于这些操作总是使用这些消息.
假如你使用参数为SW_MAXIMIZE来调用ShowWindow时,MDI子窗口最大化并没有问题, 但是当你试图将它恢复成以前的状态时,问题就来了.
但是你可以用调用ShowWindow来最小化MDI子窗口.</td>
</tr>
<tr>
<th noWrap width="177">WM_MDINEXT</th>
<td width="466">发送这条消息给客户窗口,根据wParam和lParam里相应的值来激活下一个或是前一个MDI子窗口.</td>
</tr>
<tr>
<th noWrap width="177">WM_MDIREFRESHMENU</th>
<td width="466">发送这条消息给客户窗口来刷新框架窗口的菜单. 注意在发送了这条消息之后, 你必须调用<font color="#000099"><b>DrawMenuBar</b></font>
来更新菜单条.</td>
</tr>
<tr>
<th noWrap width="177">WM_MDISETMENU</th>
<td width="466">发送这条消息给客户窗口来取代框架窗口的整个菜单或是窗口子菜单. 你必须使用这条消息而不是用<font color="#000099"><b>SetMenu</b></font>.
在发送了这条消息之后, 你必须调用<font color="#000099"><b>DrawMenuBar</b></font>来更新菜单条.
正常情况下当活动的MDI子窗口有它自己的菜单而且你希望用这个活动的子窗口自身的菜单来取代框架窗口的菜单时,你将使用这条消息.</td>
</tr>
</TBODY>
</table>
<p>我们将创建一个MDI应用程序的步骤回顾一遍.
<ol>
<li>注册窗口类, 既有框架窗口类也有MDI子窗口类. </li>
<li>调用<font color="#000099"><b>CreateWindowEx</b></font>创建框架窗口. </li>
<li>在消息循环中调用<font
color="#003399"><b>TranslateMDISysAccel</b></font> 来处理MDI相关的加速键. </li>
<li>在框架窗口的窗口过程中, 调用<font
color="#000099"><b>DefFrameProc</b></font> 处理<b>所有</b>你的代码没有处理的消息. </li>
<li>用预选定义好的窗口类名 "MDICLIENT"调用<font color="#000099"><b>CreateWindowEx</b></font>来创建客户窗口,
在lParam参数中传递CLIENTCREATESTRUCT结构的地址. 正常情况下,你可以用框架窗口过程中的WM_CREATE句柄来创建一个客户窗口.</li>
<li>相应的要创建MDI子窗口,你可以通过调用<font color="#000099"><b>CreateMDIWindow</b></font>
来发送WM_MDICREATE消息给客户窗口.</li>
<li>在MDI子窗口的窗口过程中, 我们把所有未处理的消息发送给传递给<font color="#003366"><b>DefMDIChildProc</b></font>.
</li>
<li>假如某一条消息有它的MDI的版本,那我们就使用它的MDI版本. 举个例子, 我们使用WM_MDIDESTORY消息, 而不是使用<font color="#003366"><b>DestroyWindow</b></font>来关闭一个MDI子窗口.
</li>
</ol>
<h3>例子:</h3>
<pre>.386
.model flat,stdcall
option casemap:none
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
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
.const
IDR_MAINMENU equ 101
IDR_CHILDMENU equ 102
IDM_EXIT equ 40001
IDM_TILEHORZ equ 40002
IDM_TILEVERT equ 40003
IDM_CASCADE equ 40004
IDM_NEW equ 40005
IDM_CLOSE equ 40006
.data
ClassName db "MDIASMClass",0
MDIClientName db "MDICLIENT",0
MDIChildClassName db "Win32asmMDIChild",0
MDIChildTitle db "MDI Child",0
AppName db "Win32asm MDI Demo",0
ClosePromptMessage db "Are you sure you want to close this window?",0
.data?
hInstance dd ?
hMainMenu dd ?
hwndClient dd ?
hChildMenu dd ?
mdicreate MDICREATESTRUCT <>
hwndFrame dd ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
;=============================================
; 注册框架窗口类
;=============================================
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 hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_APPWORKSPACE
mov wc.lpszMenuName,IDR_MAINMENU
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
;================================================
; 注册MDI子窗口类
;================================================
mov wc.lpfnWndProc,offset ChildProc
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -