Windows SDK笔记
http://www.cppblog.com/Lee7/archive/2008/11/07/66226.html
Windows SDK 笔记 ( 一 ) : Windows 程序基本结构
一、概述
Windows 程序具有相对固定的结构,对编写者而言,不需要书写整个过程,大部分过程由系统完成。
程序中只要按一定的格式填写系统留给客户的那一小部分。
所需要完成的有:
窗口类的定义、窗口的建立、消息函数的书写、消息循环。
二、消息处理函数
Windows 程序是事件驱动的,对于一个窗口,它的大部分例行维护是由系统维护的。没个窗口都有一个消息处理函数。
在消息处理函数中,对传入的消息进行处理。系统内还有它自己的缺省消息处理函数。
客户写一个消息处理函数,在窗口建立前,将消息处理函数与窗口关联。这样,每当有消息产生时,就会去调用这个消息处理函数。
通常情况下,客户都不会处理全部的消息,而是只处理自己感兴趣的消息,其他的,则送回到系统的缺省消息处理函数中去。
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case ...
...
case ...
...
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
三、窗口的建立
客户需要自己建立窗口,建立后会得到系统返回的窗口句柄 (HWND), 后继的针对窗口的操作都针对句柄进行。
1. 注册窗口类
建立窗口前,需要制定好这个窗口的相关属性,最主要的就是将自己定义的消息处理函数与窗口关联,其他的属性还包括:菜单、图标等等。
这个属性指定步骤是通过指定 " 窗口类 " 来完成的。
对于自己建立的窗口,这个 " 窗口类 " 需要自己制定,也即自己填充一个 WNDCLASS 结构 , 然后向系统注册。
对于一些特殊窗口,如按钮等控件,他们的行为是系统制定好了的,所以不需要自己注册,直接使用对应的 “ 窗口类 ” 名称就行了。
2. 建立窗口
建立窗口时,注册的 " 窗口类 " 名称作为参数传入。
这样,当有针对该窗口的消息时,将调用 “ 窗口类 ” 中指定的消息处理函数,在其中得到处理。
四、消息循环
系统会将针对这个程序的消息依次放到程序的 “ 消息队列 ” 中,由程序自己依次取出消息,在分发到对应的窗口中去。
因此,建立窗口后,将进入一个循环。
在循环中,取出消息、派发消息,循环往复,直到取得的消息是退出消息。
循环退出后,程序即结束。
#include "stdafx.h"
#include <windows.h>
// 一、消息处理函数
// 参数: 窗口句柄,消息,消息参数,消息参数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// 处理感兴趣的消息
switch (message)
{
case WM_DESTROY:
// 当用户关闭窗口,窗口销毁,程序需结束,发退出消息,以退出消息循环
PostQuitMessage (0) ;
return 0 ;
}
// 其他消息交给由系统提供的缺省处理函数
return ::DefWindowProc (hwnd, message, wParam, lParam) ;
}
// 二、应用程序主函数
// 参数: 实例句柄、前一个实例的句柄、命令行参数、窗口显示方式
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
//1. 注册窗口类
static TCHAR szAppName[] = TEXT ("HelloWin" ) ; // 窗口类名称
// 定制" 窗口类" 结构
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ; // 关联消息处理函数
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ; // 实例句柄
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; // 图标
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; // 光标
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH); // 画刷
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName; // 类名称
// 注册
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("RegisterClass Fail!" ),
szAppName, MB_ICONERROR) ;
return 0 ;
}
// 建立窗口
HWND hwnd ;
hwnd = CreateWindow (szAppName, // 窗口类名称
TEXT ("The Hello Program" ), // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口风格
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance, // 实例句柄
NULL);
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
// 消息循环
MSG msg ;
while (GetMessage (&msg, NULL, 0, 0)) // 从消息队列中取消息
{
TranslateMessage (&msg) ; // 转换消息
DispatchMessage (&msg) ; // 派发消息
}
return msg.wParam ;
}
Windows SDK 笔记 ( 二 ) :在窗口上建立控件
一、概述
控件是子窗口,它们是系统已经定义好的窗口类,因此不需要注册、
也不需要写消息处理函数。
在主窗口得到 WM_CREATE 消息时,建立子窗口即可。
二、实例
// 参数: 窗口句柄,消息,消息参数,消息参数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// 处理感兴趣的消息
switch (message)
{
case WM_CREATE:
CreateWindow(TEXT("BUTTON" ), // 控件" 类名称"
TEXT(" 按钮(&A)" ),
WS_CHILD | WS_VISIBLE |BS_PUSHBUTTON,
10,
10,
100,
100,
hwnd,
(HMENU)1000, // 控件ID
((LPCREATESTRUCT) lParam)->hInstance, // 实例句柄
NULL);
return 0;
case WM_DESTROY:
// 当用户关闭窗口,窗口销毁,程序需结束,发退出消息,以退出消息循环
PostQuitMessage (0) ;
return 0 ;
}
// 其他消息交给由系统提供的缺省处理函数
return ::DefWindowProc (hwnd, message, wParam, lParam) ;
}
三、关于 WM_CREATE 消息
WM_CREATE 的 lParam 参数将会传入一个建立时信息结构指针 (LPCREATESTRUCT) 。
结构中包含了一些有用信息 ( 窗口建立时的参数 ) 。
typedef struct tagCREATESTRUCT {
LPVOID lpCreateParams;
HINSTANCE hInstance; // 实例句柄
HMENU hMenu;
HWND hwndParent;
int cy;
int cx;
int y;
int x;
LONG style;
LPCTSTR lpszName;
LPCTSTR lpszClass;
DWORD dwExStyle;
} CREATESTRUCT, *LPCREATESTRUCT;
四、控件与父窗口的协作
1. 控件上发生动作时,将向父窗口发送通知消息 WM_COMMAND 。
WM_COMMAND:
HIWORD(wParam): 通知码 (notification code)
LOWORD(wParam): 控件 ID
(HWND)lParam: 控件句柄
除了 WM_COMMAND 外,每种控件还有可能有其他的通知消息 ( 如 WM_DRAWITEM) 。
2. 父窗口需要控制控件时,向控件发控件消息。
事先应记录下控件句柄,或由 ID 获取控件句柄
3. 备注 :
各种控件的通知消码和控制消息可由
MSDN-> Platform SDK-> User Interface Services->Windows User Interface->Controls
查得。
五、控件 " 类名称 "
1. 标准控件
BUTTON : 按钮
COMBOBOX : 复合框
EDIT : 编辑
LISTBOX : 列表
RichEdit :Rich Edit version 1.0
RICHEDIT_CLASS :Rich Edit version 2.0
SCROLLBAR : 滚动条
STATIC : 静态
2. 外壳附带的公用控件
注 : 建立前需要用 InitCommonControlsEx 进行初始化
INITCOMMONCONTROLSEX icex;// Ensure that the common control DLL is loaded.
icex.dwSize = sizeof (INITCOMMONCONTROLSEX);
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
HWND hWndListView =CreateWindowEx(0,WC_LISTVIEW, //WC_LISTVIEW 不需要加引号
TEXT("" ),
WS_CHILD | WS_VISIBLE|WS_BORDER | LVS_ICON | LVS_EDITLABELS | WS_EX_CLIENTEDGE ,
10,
10,
100,
100,
hwnd,
(HMENU)1000, // 控件ID
((LPCREATESTRUCT) lParam)->hInstance, // 实例句柄
NULL);
}
ANIMATE_CLASS
DATETIMEPICK_CLASS
HOTKEY_CLASS
MONTHCAL_CLASS
PROGRESS_CLASS
REBARCLASSNAME
STATUSCLASSNAME
TOOLBARCLASSNAME
TOOLTIPS_CLASS
TRACKBAR_CLASS
UPDOWN_CLASS
WC_COMBOBOXEX
WC_HEADER
WC_IPADDRESS
WC_LISTVIEW
WC_PAGESCROLLER
WC_TABCONTROL
WC_TREEVIEW
3. 特殊窗口
MDIClient :MDI 客户区窗口
ComboLBox :The class for the list box contained in a combo box.
DDEMLEvent :Windows NT/2000: The class for DDEML events.
Message :Windows 2000: The class for a message-only window.
#32768 :The class for a menu.
#32769 :The class for the desktop window.
#32770 :The class for a dialog box.
#32771 :The class for the task switch window.
#32772 :Windows NT/2000: The class for icon titles.
Windows SDK 笔记 ( 三 ) :定制控件消息处理函数
一、概述
控件的消息处理函数是由系统定义好了的,通常情况下,不需要自己提供。
但当需要对控件进行特殊控制时,可以提供一个消息处理函数,替换原来的消息处理函数。
自己的处理完成后,再调用控件的缺省消息处理。
二、相关函数
1. 窗口类的属性可以通过 GetWindowLong 和 SetWindowLong 进行读取和设置
LONG GetWindowLong(
HWND hWnd, // handle to window
int nIndex // offset of value to retrieve
);
LONG SetWindowLong(
HWND hWnd, // handle to window
int nIndex, // offset of value to set
LONG dwNewLong // new value
);
可以返回或设置以下内容 :
nIndex 值 意义
GWL_EXSTYLE 扩展风格
GWL_STYLE 风格
GWL_WNDPROC 消息处理函数
GWL_HINSTANCE 实例
GWL_ID 窗口 ID
GWL_USERDATA 用户数据
DWL_DLGPROC 对话框消息处理函数
DWL_MSGRESULT
DWL_USER
所以使用
OldMsgProc = (WNDPROC)SetWindowLong (hControlWnd, GWL_WNDPROC, (LONG)MyMsgProc);
将控件消息处理函数替换成 MyMsgProc, 原处理函数被 OldMsgProc 记录。
2. 调用消息处理函数
LRESULT CallWindowProc(
WNDPROC lpPrevWndFunc, // pointer to previous procedure
HWND hWnd, // handle to window
UINT Msg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
三、示例
1. 提供新处理函数
// 记录原来处理函数的全局变量
WNDPROC OldMsgProc;
// 新消息处理函数
LRESULT MyMsgProc(HWND hwnd,UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
::MessageBox(NULL,"click!" ,"" ,MB_OK);
}
// 调用控件原来的消息处理函数
return CallWindowProc(OldMsgProc,hwnd,message,wParam,lParam);
}
2. 建立窗口后,更改消息处理函数
case WM_CREATE:
{
HWND hControlWnd = CreateWindowEx(0,"BUTTON" ,
TEXT(" 按钮(&A)" ),
WS_CHILD | WS_VISIBLE|BS_PUSHBUTTON,
10,
10,
100,
100,
hwnd,
(HMENU)1000, // 控件ID
((LPCREATESTRUCT) lParam)->hInstance, // 实例句柄
NULL);
// 嵌入新的消息处理函数
OldMsgProc = (WNDPROC) SetWindowLong (hControlWnd, GWL_WNDPROC, (LONG)MyMsgProc);
}
return 0;
Windows SDK 笔记 ( 四 ) :模式对话框
一、概述
对话框是一种特殊的窗口,它依据对话框模板资源而建立。
它与一般的窗口有些不同,很多过程由系统完成了,虽然用户还是要提供一个消息处理函数,但在此消息处理函数中,不需要将不关心的消息交由缺省消息处理函数。
实际上,调用缺省处理的过程又系统完成。
二、对话框消息处理函数
对话框也需要用户提供一个消息处理函数,但这个处理函数没有普通窗口的消息处理函数" 权利大" 。
对话框是一种系统定义的“ 窗口类” ,它已经定义好了对应的消息处理函数。客户所作的消息处理函数,并不是直接与窗口连接,而是对对话框消息处理函数的一种补充,或者说“ 嵌入” 。
因此,对话框处理函数不需要调用“ 缺省消息处理函数” 。
当有消息被处理时,返回TRUE, 没有消息需要处理时,返回FALSE ,此时退出用户消息处理函数后,系统会去调缺省消息处理函数。
// 对话框消息处理函数
// 返回值类型为BOOL, 与普通窗口处理函数不同。
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG :
return TRUE ; // 返回真,表示消息被处理了。
case WM_COMMAND :
switch (LOWORD (wParam))
{
case IDOK :
case IDCANCEL :
EndDialog (hDlg, 0) ; // 使用EndDialog 关闭对话框
return TRUE ; // 返回真,表示消息被处理了。
}
break ;
}
return FALSE ; 返回假,表示消息未被用户处理, 又缺省消息处理函数去处理。
}
三、模式对话框建立
使用DialogBox 。
INT_PTR DialogBox(
HINSTANCE hInstance, // handle to module
LPCTSTR lpTemplate, // dialog box template
HWND hWndParent, // handle to owner window
DLGPROC lpDialogFunc // dialog box procedure
);
例:
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_ABOUT:
DialogBox (hinst, MAKEINTRESOURCE(IDD_ABOUT), hwnd, AboutDlgProc) ;
break ;
}
return 0;
四、模式对话框与程序的交互
模式对话框中,可以对程序中的数据进行更改。
结束对话框时,在EndDialog 第二个参数中传入退出参数
这个参数将被DialogBox 作为返回值,然后对话框的用户根据此返回值作相应的操作。
1. 初始化
对话框消息处理函数中,在接到WM_INITDIALOG 消息时,作一些初始化工作。
如从全局变量读取初始值来设置各控件状态。
2. 退出时
若退出时,更改需要生效,( 如按了“ 确定”), 则根据控件状态设置全局变量,并相应的在EndDialg 中使用一个表示成功的值( 如TRUE) 。
若更改不需要生效( 如按了“ 取消”) ,则不保存结果,并相应的在EndDialg 中使用一个表示取消的值( 如FALSE) 。
3. 对话框用户作出反应
根据DialogBox 的返回值不同,而进行不同的操作
如,返回TRUE 时,重绘窗口:
if (DialogBox (hInstance, TEXT ("AboutBox" ), hwnd, AboutDlgProc))
InvalidateRect (hwnd, NULL, TRUE) ;
Windows SDK 笔记 ( 五 ) :非模式对话框
一、概述
使用DialgBox 建立的对话框是“ 模式对话框” ,只有关闭对话框后,程序的其他窗口才能进行操作。
与此相对应,存在“ 非模式对话框” ,对话框建立后,并不强制要求用户立即反应,而是与其他窗口同时接受用户操作。
二、建立
非模式对话框使用CreateDialg 建立。
可以在WinMain 中建立主窗口后建立,对话框句柄保存备用。
hDlgModeless = CreateDialog (
hInstance,
TEXT ("ColorScrDlg" ), // 对话框模板
hwnd,
ColorScrDlg // 对话框消息处理函数
);
三、消息循环添加针对非模式对话框的处理
“ 非模式对话框” 与“ 模式对话框” 不同,模式对话框工作的时候,有其内部的消息泵机制。
而非模式对话框则象普通窗口一样,由WinMain 中书写的消息循环驱动 。
但由于是对话框,它对一些消息有特殊的处理,例如用于在对话框中各子控件间导航的"TAB" 键、"ENTER" 键等等。
因此,在消息循环中,需要先给对话框提供截获消息的机会。
while (GetMessage (&msg, NULL, 0, 0))
{
if (hDlgModeless == 0 || !IsDialogMessage (hDlgModeless, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
如果当前取得的消息是对话框消息的话,IsDialgMessage 将它交由对话消息处理函数处理,并返回TRUE 。
不需要在派发了。
四、非模式对话框的销毁
使用:
DestroyWindow (hDlg);
用户关闭对话框时,对话框消息处理函数将收到WM_CLOSE 消息,接到后调用DestroyWindow 以销毁非模式对话框。
Windows SDK 笔记 ( 六 ) :使用对话框资源建立窗口
一、概述
在Charles Petzold 的书中,介绍了一种直接利用对话框资源建立主窗口的方法。
使用这种方法,可以方便的在主窗口中安排子控件,而代码的其他部分跟用普通窗口时一样。
我们知道,对话框是系统预先定义的“ 窗口类” ,它有自己的窗口处理函数,我们自己写的对话框消息处理函数并不是真正的窗口消息处理函数。
但我们可以在对话框模板脚本中,指定这个对话框使用我们自己定义的窗口类,而不是系统的对话框类,这样,就将对话框的消息处理函数“ 嫁接” 成我们自己定义的消息处理函数了。
二、书写一个“ 真正的” 窗口消息处理函数
按照普通窗口的方式书写好消息处理函数。
( 不要漏掉了DefWindowProc)
三、注册窗口类
用书写的消息处理函数注册一个窗口类。
四、建立对话框资源,指定窗口类为自定的窗口类。
手工书写一个对话框资源,存为单独文件,然后包含到资源文件中去。
( 使用菜单View->Resource Includes 弹出对话框,将文件名填入到Compile-time derective 一栏, 这将在rc 文件中添加一行:"#include ""Some.dlg"" ")
例:
建立文件Some.dlg
书写:
HexCalc DIALOG -1, -1, 102, 122
STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
CLASS "HexCalc" // 填写上自己注册的类名称
CAPTION "Hex Calculator"
{
PUSHBUTTON "D" , 68, 8, 24, 14, 14
PUSHBUTTON "A" , 65, 8, 40, 14, 14
// 各种控件
}
五、使用非模式对话框方式建立主窗口
建立主窗口的时候,使用CreateDialog 。
hwnd = CreateDialog (
hInstance,
szAppName, // 对话框模板
0,
NULL) ;
ShowWindow (hwnd, iCmdShow) ;
其他各部分,都与普通窗口时相同( 注册窗口类、消息循环等) 。
Ⅱ. 在对话框中建立自定义子窗口
可以自己定义控件,然后在对话框模板中使用
一、定义" 窗口类" 与消息处理函数
在WinMain 中
除了注册主窗口类外,
另外注册用于对话框的类,指明类对应的消息处理函数
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = SomeWndProc ; // 对应的消息处理函数
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = NULL ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = TEXT ("SomeControl" ) ;
ReGISterClass (&wndclass) ;
同时,还要书写好消息处理函数SomeWndProc 。
二、在对话框模板中添加自定义控件窗口
在对话框模板上放上"Custom Control" ,然后设置属性,并填写自己定义的类名称SomeControl 。
Windows SDK 笔记 ( 七 ) :创建 MDI 窗口
一、概述
MDI 窗口包含一个框架窗口和若干子窗口。
实际上,框架窗口本身是一个普通主窗口,不过它的客户去被一个特殊窗口覆盖。
这个特殊窗口是系统预定义的“ 窗口类” ,类名称为:"MDICLIENT" 。它负责各个MDI 子窗口的管理。
二、窗口建立
1. 注册一个MDI 框架窗口类,提供MDI 框架窗口消息处理函数
MDI 框架窗口消息处理函数中,将未处理消息交由DefFrameProc 处理
//MDI 框架窗口消息处理函数
LRESULT CALLBACK MDIFrameWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//...
// 其他消息交给由系统提供的缺省框架处理函数DefFrameProc
// 其中,第二个参数是客户区窗口句柄
return ::DefFrameProc (hwnd,hwndClient, message, wParam, lParam) ;
}
2. 注册多个MDI 子窗口类、对应提供各MDI 子窗口的消息处理函数
子窗口消息处理函数中,将未处理消息交由MDIDefMDIChildProc 处理
//MDI 子窗口消息处理函数
LRESULT CALLBACK MDIChildWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//...
//...
// 其他消息交给由系统提供的缺省MDI 子窗口处理函数
return ::DefMDIChildProc (hwnd, message, wParam, lParam) ;
}
3. 在框架窗口的客户区建立MDI 管理子窗口
MDI 子窗口的管理实际上是由框架窗口客户区的"MDILIENT" 窗口完成的。
这是一个系统预定义的窗口。
在主窗口收到WM_CREATE 消息后:
case WM_CREATE:
{
hinst=((LPCREATESTRUCT) lParam)->hInstance;
// 填充CLIENTCREATESTRUCT 结构
CLIENTCREATESTRUCT clientcreate ;
clientcreate.hWindowMenu = hMenuInitWindow ; // 用于添加窗口列表的菜单句柄
clientcreate.idFirstChild = 50000 ; // 起始ID
hwndClient =CreateWindowEx(0,
"MDICLIENT" , // 类名称为"MDICLIENT"
NULL,
WS_CHILD |WS_CLIPCHILDREN| WS_VISIBLE,
0,
0,
0,
0,
hwnd,
(HMENU)1,//ID
hinst, // 实例句柄
&clientcreate); // 参数
}
return 0;
窗口的大小没有关系,缺省的框架窗口消息处理函数为让它覆盖整个客户区。
MDI 客户区窗口建立后,通过向它发送消息管理子窗口的建立、销毁、排列等等。
4.MDI 子窗口的建立
可以在菜单中添加命令项,以建立子窗口。
框架窗口的消息处理函数收到命令后,向MDI 客户区窗口发建立命令。
case ID_NEW:
{
MDICREATESTRUCT mdicreate;
mdicreate.szClass = szMDIChildName ; //MDI 子窗口的类名称
mdicreate.szTitle = TEXT ("Hello" ) ;
mdicreate.hOwner = hinst ;
mdicreate.x = CW_USEDEFAULT ;
mdicreate.y = CW_USEDEFAULT ;
mdicreate.cx = CW_USEDEFAULT ;
mdicreate.cy = CW_USEDEFAULT ;
mdicreate.style = 0 ;
mdicreate.lParam = 0 ;
SendMessage (
hwndClient, //MDI 客户区窗口句柄
WM_MDICREATE, // 创建MDI 子窗口
0,
(LPARAM) (LPMDICREATESTRUCT) &mdicreate // 创建参数
) ;
}
break ;
三、消息循环中处理针对MDI 的热键
在消息循环中,用TranslateMDISysAccel 处理针对MDI 的热键。
while (GetMessage (&msg, NULL, 0, 0))
{
if (!TranslateMDISysAccel (hwndClient, &msg) &&
!TranslateAccelerator (hwndFrame, hAccel, &msg))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
四、命令的流向
框架窗口在收到WM_COMMAND 等通知消息后,应该给当前激活的MDI 窗口提供处理机会。
case WM_COMMAND:
switch (LOWORD (wParam))
{
// 针对框架的命令
case ID_ONE:
//...
return 0;
// 针对MDI 子窗口管理的命令
case IDM_WINDOW_TILE:
SendMessage (hwndClient, WM_MDITILE, 0, 0) ;
return 0 ;
// 针对子窗口的命令又子窗口去处理
default :
hwndChild = (HWND) SendMessage (hwndClient,
WM_MDIGETACTIVE, 0, 0) ;
if (IsWindow (hwndChild))
SendMessage (hwndChild, WM_COMMAND, wParam, lParam) ;
break ; //..and then to DefFrameProc
}
break ; // 跳出针对WM_COMMAND 的case 分支,又DefFrameProc 处理剩下的命令
五、子窗口的管理
1. 概述
给MDI 客户区窗口发控制消息即可
如:
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_WINDOW_TILE:
SendMessage (hwndClient, WM_MDITILE, 0, 0) ;
return 0 ;
case IDM_WINDOW_CASCADE:
SendMessage (hwndClient, WM_MDICASCADE, 0, 0) ;
return 0 ;
case IDM_WINDOW_ARRANGE:
SendMessage (hwndClient, WM_MDIICONARRANGE, 0, 0) ;
return 0;
//...
//...
}
break ;
2. 当前子窗口的关闭
关闭当前激活窗口时,先向该窗口发送查询消息:WM_QUERYENDSESSION 。
子窗口的消息处理循环中响应此消息,作关闭前的一些处理,若能关闭,返回真
否则返回假。
框架窗口中根据此返回值决定是否关闭窗口。
如果用户直接按下子窗口的关闭按钮,则WM_CLOSE 消息直接发送到了子窗口消息处理函数。
例如:
框架窗口命令处理中:
case IDM_FILE_CLOSE:
// 获得当前激活窗口
hwndChild = (HWND) SendMessage (hwndClient, WM_MDIGETACTIVE, 0, 0);
// 询问通过后,销毁窗口
if (SendMessage (hwndChild, WM_QUERYENDSESSION, 0, 0))
SendMessage (hwndClient, WM_MDIDESTROY, (WPARAM) hwndChild, 0);
return 0;
子窗口的消息处理函数中:
LRESULT CALLBACK HelloWndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message)
{
//...
//...
case WM_QUERYENDSESSION:
case WM_CLOSE:
if (IDOK != MessageBox (hwnd, TEXT ("OK to close window?" ),
TEXT ("Hello" ),
MB_ICONQUESTION | MB_OKCANCEL))
return 0 ;
break ; // i.e., call DefMDIChildProc
}
return DefMDIChildProc (hwnd, message, wParam, lParam) ;
}
3. 关闭所有子窗口
当使用命令方式关闭所有子窗口时,需要枚举所有子窗口进行关闭。
例:
框架窗口响应命令:
case IDM_WINDOW_CLOSEALL:
// 针对所有子窗口执行CloseEnumProc
EnumChildWindows (hwndClient, CloseEnumProc, 0) ;
return 0 ;
枚举函数:
BOOL CALLBACK CloseEnumProc (HWND hwnd, LPARAM lParam)
{
if (GetWindow (hwnd, GW_OWNER)) // Check for icon title
return TRUE ;
SendMessage (GetParent (hwnd), WM_MDIRESTORE, (WPARAM) hwnd, 0) ;
if (!SendMessage (hwnd, WM_QUERYENDSESSION, 0, 0))
return TRUE ;
SendMessage (GetParent (hwnd), WM_MDIDESTROY, (WPARAM) hwnd, 0) ;
return TRUE ;
}
六、菜单控制
在MDI 程序中,可以根据激活的子窗口而切换框架窗口的菜单。
并且,可以将窗口列表添加到菜单中去。所添加的菜单项命令是又框架对应的缺省消息处理函数完成的。
1. 为每种窗口类准备一套菜单资源
2. 装载菜单,得到菜单句柄
3. 框架在建立时,使用框架菜单的句柄作为参数。
4. 子窗口在激活时,加载自己菜单到框架窗口
失去焦点时,还原框架菜单。
使用向MDI 客户区窗口发送WM_MDISETMENU 或WM_MDISETMENU 消息。
wParam 为菜单句柄,lParam 为欲添加窗口列表的子菜单句柄
case WM_MDIACTIVATE:
// 激活时,设置框架菜单
if (lParam == (LPARAM) hwnd)
SendMessage (hwndClient, WM_MDISETMENU,
(WPARAM) hMenuHello, (LPARAM) hMenuHelloWindow) ;
// 失去焦点时,将框架菜单还原
if (lParam != (LPARAM) hwnd)
SendMessage (hwndClient, WM_MDISETMENU, (WPARAM) hMenuInit,
(LPARAM) hMenuInitWindow) ;
DrawMenuBar (hwndFrame) ;
// 注: hwndFrame 的得到方法:
//hwndClient = GetParent (hwnd) ;
//hwndFrame = GetParent (hwndClient) ;
return 0 ;
( 全文完)