Why do we even have the DefWindowProc function? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20071105-00/?p=24583
Raymond Chen 2007年11月05日
为什么一定要使用 DefWindowProc 函数?
简要
文章讨论了为什么我们有
DefWindowProc
函数。它解释了如果窗口过程遵循对话框模型,简单地返回FALSE
以获得默认处理,那么DefWindowProc
函数就没有必要存在。然而,这种模型忽略了派生类使用基类作为子程序的关键模式。文章通过示例代码展示了如何使用DefWindowProc
来扩展而非完全覆盖基类行为,以及如何修改消息以自定义默认处理程序的行为。这在面向对象编程中是常见的,允许派生类大部分保持基类的行为。如果没有DefWindowProc
,实现这些功能将会更加困难。
正文
不久前,我探讨了重新实现对话框过程的两种方式(方法1,方法2)。 评论者“8”想知道我们为什么有DefWindowProc
函数。 难道窗口过程不能遵循对话框模型,简单地返回FALSE
以表示他们希望进行默认处理吗? 如果是这样,那就没有必要导出DefWindowProc
函数了。
这忽略了派生类的一个关键模式:将基类作为子程序来使用。 正是这种模式促使人们要求对像窗口过程那样的对话框过程。 如果你使用“返回FALSE
以获得默认行为”的模式, 窗口过程可能会是这样的:
BOOL DialogLikeWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
... 处理消息并返回TRUE ...
}
// 我们没有特别的处理;做默认的事情
return FALSE;
}
在这个假想的世界中,子类化可能会这样进行:
BOOL DialogLikeSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
... 处理消息并返回TRUE ...
}
// 我们没有特别的处理;让基类尝试
CallDialogLikeWindowProc(PrevDialogLikeWndProc, hwnd, uMsg, wParam, lParam);
}
这在你想要完全覆盖基类行为时是有效的。 但如果你只想增强它呢? 调用先前的窗口过程类似于从派生类调用基类实现, 在面向对象编程中这是相当常见的,你希望派生类“大体上”表现得像基类。 考虑这样一个案例,我们希望允许用户通过点击客户区域的任何地方来拖动窗口:
LRESULT CALLBACK CaptionDragWndProc(
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lres;
switch (uMsg) {
case WM_NCHITTEST:
lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (lres == HTCLIENT) lres = HTCAPTION;
return lres;
...
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
我们希望我们的命中测试表现得像正常的一样, 唯一的例外是客户区域中的点击应该被视为对标题的点击。 有了DefWindowProc
模型, 我们可以通过调用DefWindowProc
来进行默认处理,然后在后端修改结果。 如果我们使用了类似对话框的模型, 就没有一种方法可以将“默认处理器”作为一个子程序来调用,以便让它承担繁重的工作。 我们将被迫做所有的工作或者什么都不做。
另一个要明确的点是,DefWindowProc
函数提供可修改消息的机会, 在它们到达默认处理器之前。 例如,假设你有一个只读的编辑控件, 但你希望它看起来像一个正常的编辑控件,而不是得到静态控件的外观。 你可以通过修改你传递给DefWindowProc
的消息来做到这一点:
...
case WM_CTLCOLORSTATIC:
if (GET_WM_CTLCOLOR_HWND(wParam, lParam) == m_hwndEdit)
{
// 给它“编辑”的外观
return DefWindowProc(hwnd, WM_CTLCOLOREDIT, wParam, lParam);
}
...
还有一个常见操作是在保持其他属性不变的情况下更改编辑控件的一种颜色属性。 为此,你可以将DefWindowProc
作为一个子程序来使用, 然后微调你想要自定义的一个属性。
case WM_CTLCOLORSTATIC:
if (GET_WM_CTLCOLOR_HWND(wParam, lParam) == m_hwndDanger)
{
// 从默认颜色属性开始
LRESULT lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
// 将文字颜色改为红色;保持其他所有东西不变
SetTextColor(GET_WM_CTLCOLOR_HDC(wParam, lParam), RGB(255,0,0));
return lres;
}
...
使用对话框模型来实现这些类型的操作将是一项更为棘手的任务。