WM_CREATE
WM_CREATE
说明:以下内容主要来自腾讯元宝。
WM_CREATE会在什么时候触发?
WM_CREATE
消息是 Windows 消息机制中非常重要的一个消息,它在窗口或控件
创建完成并即将显示之前
被系统发送给窗口过程(
WindowProc
)。以下是
WM_CREATE
的具体触发时机、使用场景及注意事项:
1. WM_CREATE 消息的触发时机
**(1) 窗口首次创建时**
示例场景 :
当调用
CreateWindowEx
或CreateWindow
创建顶级窗口(如主对话框、应用程序窗口)或子窗口(如按钮、列表框等控件)时,系统在窗口对象分配完毕、窗口句柄(HWND
)生成后,立即发送WM_CREATE
消息。 关键点 :
-
发生在窗口的
CREATESTRUCT
结构体初始化之后 ,但窗口尚未显示(ShowWindow
未调用)。 - 适用于所有类型的窗口 (顶级窗口、对话框、控件)。
-
发生在窗口的
**(2) 窗口被销毁后重新创建**
场景 :
如果窗口被销毁(如调用
DestroyWindow
),随后再次创建同名窗口(例如重启应用程序的主窗口),系统会重新触发WM_CREATE
。
**(3) 动态创建控件时**
场景 :
在运行时动态创建控件(如列表框、按钮)时,例如通过
CreateWindowEx
在对话框或父窗口内创建控件, 每个新创建的控件 都会独立接收WM_CREATE
消息。 示例代码 :
HWND hListBox = CreateWindowEx( 0, L"ListBox", NULL, WS_CHILD | WS_VISIBLE | LBS_OWNERDRAWFIXED, 10, 10, 200, 300, hWndParent, (HMENU)IDC_MYLIST, hInstance, NULL); // 此时 hListBox 会自动收到 WM_CREATE 消息
**(4) 通过对话框模板创建控件**
场景 :
如果使用资源编辑器设计对话框(
.rc
文件),并在对话框中声明控件(如列表框),当对话框被创建(如DialogBox
或CreateDialog
调用)时, 对话框本身和其内部的所有控件 都会依次触发WM_CREATE
消息。
2. WM_CREATE 消息的典型用途
WM_CREATE
是
初始化窗口或控件
的黄金时机,常见用途包括:
- 初始化控件属性 :设置窗口样式、控件数据或关联资源(如图标、位图)。
-
注册子窗口类
:如果窗口包含自定义控件(如继承自
CWnd
的类),需在此处注册类。 - 分配内存 :为窗口或控件私有数据分配内存(如结构体、缓冲区)。
-
绑定事件处理
:为控件设置消息映射(如
ON_BN_CLICKED
)。
示例代码(初始化列表框) :
// 在窗口过程的 WM_CREATE 处理中
case WM_CREATE:
{
CREATESTRUCT* pCreateInfo = (CREATESTRUCT*)lParam;
// 创建自定义列表框(假设 LBS_OWNERDRAWFIXED)
HWND hListBox = CreateWindowEx(
0, L"ListBox", NULL,
WS_CHILD | WS_VISIBLE | LBS_OWNERDRAWFIXED,
10, 10, 200, 300,
pCreateInfo->hwndParent, (HMENU)IDC_MYLIST,
hInstance, NULL);
// 设置列表框项的高度(需响应 WM_MEASUREITEM)
SendMessage(hListBox, LB_SETITEMHEIGHT, 0, 30);
return TRUE; // 必须返回 TRUE,否则窗口创建失败
}
break;
3. 与 WM_INITDIALOG 的区别
**
WM_CREATE
**:适用于** 所有窗口和控件**(包括顶级窗口、对话框、控件),是通用的初始化消息。
**
WM_INITDIALOG
**:仅当窗口是** 对话框框体**(
DLGBOX
类型)时触发,专门用于初始化对话框中的控件和数据。
4. 注意事项
必须返回
TRUE
:在
WM_CREATE
处理函数中, 必须返回TRUE
,否则系统会认为窗口创建失败并终止创建流程。 避免过早操作 :
- 窗口尚未显示,但句柄(
HWND
)已有效,可以安全调用SendMessage
、SetWindowText
等函数。 -
不能直接调用
ShowWindow
或UpdateWindow
,因为窗口尚未进入显示状态。
- 窗口尚未显示,但句柄(
动态创建控件的层级关系 :
- 动态创建的控件(如列表框)会自动成为父窗口的子窗口,但其
WM_CREATE
消息 在父窗口的WM_CREATE
之后触发 。 -
消息队列顺序
:父窗口
WM_CREATE
→ 子窗口WM_CREATE
→ 父窗口WM_SIZE
(调整布局)。
- 动态创建的控件(如列表框)会自动成为父窗口的子窗口,但其
对话框中的控件创建 :
- 如果在对话框的
WM_INITDIALOG
中动态创建控件,需确保父窗口句柄(hWndDlg
)已正确传递。 -
推荐做法
:尽量在资源编辑器中声明控件,并通过
WM_INITDIALOG
初始化数据,而非动态创建。
- 如果在对话框的
5. 消息触发流程示例
步骤 1:创建主窗口
// 主窗口 WM_CREATE 处理
case WM_CREATE:
{
// 初始化代码(如创建控件、加载资源)
CreateMyCustomControl(hWnd);
return TRUE;
}
break;
步骤 2:创建对话框
// 对话框 WM_CREATE 处理(与 WM_INITDIALOG 区分)
case WM_CREATE:
{
// 初始化对话框控件(如设置文本、绑定数据)
SetDlgItemText(hWndDlg, IDC_EDIT1, _T("默认文本"));
return TRUE;
}
break;
case WM_INITDIALOG:
{
// 另一处初始化逻辑(如加载图标、设置焦点)
return TRUE;
}
break;
6. 常见误区
误用
WM_INIT
:WM_INIT
并非标准 Windows 消息,可能是对WM_CREATE
或WM_INITDIALOG
的笔误。 忘记返回
TRUE
:导致窗口创建失败,表现为窗口一闪即逝或无法响应。
在
WM_CREATE
中调用ShowWindow
:虽然句柄有效,但此时窗口尚未布局完成,可能导致显示异常。
总结
- **
WM_CREATE
是窗口生命周期的起点**,适用于所有窗口和控件。 - 核心用途 :初始化资源、设置控件属性、绑定数据。
-
返回值必须为
TRUE
,否则窗口创建失败。 - 区分
WM_CREATE
(通用初始化)和WM_INITDIALOG
(对话框专用初始化)。
同一个窗口的WM_CREATE会被重复触发吗?
case WM_CREATE:
hListBox = CreateWindow(_T("ListBox"), NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
20, 20, 60, 120,
hWnd, (HMENU)ID_MYLIST,
hInst, NULL);
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)_T("中国"));
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)_T("美国"));
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)_T("英国"));
break;
在您提供的代码中,
动态创建的列表框控件会触发自身的
WM_CREATE
消息
,但
不会再次触发父窗口的
WM_CREATE
消息
。以下是详细分析:
父窗口的
WM_CREATE
:当父窗口(如对话框或主窗口)被创建时,系统首先触发父窗口的
WM_CREATE
。 动态创建列表框 :
在父窗口的
WM_CREATE
处理中,调用CreateWindow
创建列表框控件。此时:- 系统为列表框分配
HWND
。 -
系统立即向列表框发送
WM_CREATE
消息 ( 列表框的窗口过程会处理此消息 )。 - 列表框创建完成后,父窗口的
WM_CREATE
继续执行后续代码(添加字符串)。
- 系统为列表框分配
2. 消息触发顺序
父窗口
WM_CREATE
→ 列表框WM_CREATE
→ 父窗口WM_CREATE
继续执行消息队列的顺序如下:
- 父窗口的
WM_CREATE
开始执行。 - 列表框被创建,触发列表框的
WM_CREATE
消息( 独立于父窗口 )。 - 列表框的
WM_CREATE
处理完成后,返回到父窗口的WM_CREATE
,继续执行LB_ADDSTRING
等代码。
- 父窗口的
3. 关键结论
**(1) 列表框会触发自己的 WM_CREATE
**
原因 :
每个
CreateWindow
调用都会为新窗口(或控件)生成一个HWND
,系统会立即发送WM_CREATE
消息到该窗口的窗口过程。 验证方法 :
如果列表框的类注册时绑定了自定义窗口过程(如
MyListBoxWndProc
),则在该过程中会收到WM_CREATE
消息。
**(2) 父窗口的 WM_CREATE
不会重复触发**
原因 :
WM_CREATE
是针对单个窗口的创建事件。父窗口的WM_CREATE
已经完成,后续动态创建的控件不会导致父窗口的WM_CREATE
再次触发。 例外情况 :
如果在父窗口的
WM_CREATE
中销毁并重新创建父窗口本身(如递归调用CreateWindow
),才会重新触发父窗口的WM_CREATE
。但您的代码中没有这种情况。
4. 潜在问题与改进
**(1) 必须返回 TRUE
**
父窗口的
WM_CREATE
必须返回TRUE
,否则窗口创建失败。case WM_CREATE: // ... 创建列表框和初始化代码 return TRUE; // 必须添加此行 break;
**(2) 列表框的 WM_CREATE
处理**
如果列表框是自定义控件(如继承自
CListBox
),需确保其窗口过程正确处理WM_CREATE
,例如初始化内部数据。// 自定义列表框窗口过程示例 LRESULT CALLBACK MyListBoxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: // 初始化列表框(例如分配内存) return TRUE; // ... 其他消息处理 } return DefListBoxProc(hWnd, uMsg, wParam, lParam); }
**(3) 避免在 WM_CREATE
中过早显示窗口**
列表框已设置
WS_VISIBLE
样式,会自动显示。但由于父窗口的布局尚未完成(如WM_SIZE
消息未处理),可能导致显示位置不准确。 建议 :
- 在父窗口的
WM_SIZE
中调整子窗口位置/大小。 - 或延迟控件创建(如在
WM_INITDIALOG
中创建对话框控件)。
- 在父窗口的
5. 完整消息流程图
[父窗口 WM_CREATE]
↓
创建列表框控件 →
↓
[列表框 WM_CREATE]
↓
返回到父窗口 WM_CREATE →
执行 LB_ADDSTRING →
父窗口 WM_CREATE 结束 →
父窗口继续处理其他消息(如 WM_SIZE)
总结
-
动态创建的列表框会触发自身的
WM_CREATE
,但这是独立于父窗口的。 -
父窗口的
WM_CREATE
仅触发一次 ,在其处理期间创建的控件会依次触发各自的WM_CREATE
。 - 确保父窗口和子窗口的
WM_CREATE
处理函数均返回TRUE
,否则创建流程会失败。
动态创建的列表框会触发自身的 WM_CREATE,如何处理?
动态创建的列表框会触发自身的
WM_CREATE
消息,但
是否需要处理取决于控件的类型和需求
:
1. 是否需要处理子窗口的 WM_CREATE
?
**(1) 标准控件(如 ListBox
/ Button
)**
通常不需要处理 :
Windows 系统已为标准控件实现了默认的窗口过程(如
DefWindowProc
或控件类专用过程),WM_CREATE
已被内部处理(例如初始化控件样式、关联资源)。 例外情况 :
如果需要在控件创建时执行 自定义初始化逻辑 (如分配内存、加载数据或绑定事件),则需要处理
WM_CREATE
。
**(2) 自定义控件**
必须处理 :
如果控件是自行实现的(如继承自
CWnd
的类),则需在WM_CREATE
中完成 类成员初始化 、 资源分配 或 消息映射绑定 等操作。
2. 如何处理子窗口的 WM_CREATE
?
**(1) 标准控件的处理方式**
无需手动处理 :
直接依赖系统默认逻辑即可。
间接扩展 :
若需修改标准控件的默认行为,可通过 子类化(Subclassing) 覆盖其窗口过程。
**(2) 自定义控件的处理方式**
-
步骤
:
注册窗口类 :
使用
RegisterClassEx
注册自定义控件的窗口类,并指定窗口过程 (如MyListBoxProc
)。 在
WM_CREATE
中初始化 :在自定义窗口过程中处理
WM_CREATE
,执行初始化代码。
3. 代码示例
**(1) 自定义控件类(C++/MFC)**
// 自定义列表框窗口过程
LRESULT CALLBACK MyListBoxProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: {
// 自定义初始化代码(例如分配内存)
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)new CMyData());
return TRUE;
}
// 其他消息处理...
}
return DefListBoxProc(hWnd, uMsg, wParam, lParam); // 调用默认处理
}
// 注册窗口类
WNDCLASSEX wc = {0};
wc.lpfnWndProc = MyListBoxProc;
wc.hInstance = hInst;
wc.lpszClassName = L"MyCustomListBox";
RegisterClassEx(&wc);
// 创建自定义列表框
hListBox = CreateWindowEx(
0, L"MyCustomListBox", NULL,
WS_CHILD | WS_VISIBLE,
20, 20, 60, 120,
hWndParent, (HMENU)ID_MYLIST,
hInst, NULL);
**(2) 标准控件的 WM_CREATE
处理(可选)**
即使不处理
WM_CREATE
,也可以通过以下方式在创建后立即操作控件:
// 在父窗口的 WM_CREATE 中创建列表框后
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)_T("中国")); // 添加字符串
4. 总结
场景 | 是否需要处理 WM_CREATE | 处理位置 |
---|---|---|
标准控件(如 ListBox ) | 通常不需要 | 系统默认窗口过程(无需手动处理) |
自定义控件 | 必须处理 | 自定义窗口过程(如 MyListBoxProc ) |
关键点
标准控件 :
依赖系统默认行为,若需扩展功能,建议通过 消息反射 (如 MFC 的
ON_CONTROL通知
)或 子类化 实现。 自定义控件 :
必须在
WM_CREATE
中完成初始化,否则可能导致控件行为异常。