对话框消息循环,其实也不复杂,其核心实现代码,也就下面几行代码:
while (<dialog still active> && GetMessage(&msg, NULL, 0, 0, 0)) { if (!IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }}但是,我们还是从头开始讲起吧。故事发生在DialogBoxIndirectParam调用,你应该还记得,我们之前讲解过,系统会将所有对DialogBoxXX的调用,统一转换为对DialogBoxIndirectParam的调用,代码如下:
INT_PTR WINAPI DialogBoxIndirectParam( HINSTANCE hinst, LPCDLGTEMPLATE lpTemplate, HWND hwndParent, DLGPROC lpDlgProc, LPARAM lParam){ /* * App hack! Some people pass GetDesktopWindow() * as the owner instead of NULL. Fix them so the * desktop doesn't get disabled! */ if (hwndParent == GetDesktopWindow()) hwndParent = NULL;没错,我们在代码中做了一次App Hack。 在前面的文章中,我们讨论了传递 GetDesktopWindow() 作为父窗口问题。 有很多开发者都犯了这个错误,我们不得不把这个App Hack 内置到核心操作系统代码中。 如果我们不这样做的话,则成百上千的上层应用程序都需要进行改动。
由于只有顶层窗口可以是窗口所有者,所以我们必须从hwndParent(可能是子窗口)出发,沿着窗口层次结构向上走,直到找到一个顶层窗口。
if (hwndParent) hwndParent = GetAncestor(hwndParent, GA_ROOT);完成了第二次App Hack之后,我们开始创建我们的对话框了:
HWND hdlg = CreateDialogIndirectParam(hinst, lpTemplate, hwndParent, lpDlgProc, lParam);注意:和以前一样,我将忽略错误检查和各种对话框,因为它只会分散本条目的重点。因为模态对话框会禁用它们的父窗口,所以在这里实现它:
BOOL fWasEnabled = EnableWindow(hwndParent, FALSE);然后我们进入对话框模态消息循环:
MSG msg; while (<dialog still active> && GetMessage(&msg, NULL, 0, 0)) { if (!IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }根据窗口退出消息的约定,我们重新投递我们可能收到的任何退出消息,以便下一个外部模态循环可以看到它。
if (msg.message == WM_QUIT) { PostQuitMessage((int)msg.wParam); }(聪明的读者可能已经注意到了看,上面的代码中有一个未初始化的变量错误:如果在 WM_INITDIALOG 处理期间调用了 EndDialog,则永远不会设置 msg.message。出于说明目的,我决定忽略这个错误。)
至此,我们的对话框完成使命了,我们需要清理一下。 请记住在销毁拥有的对话框之前启用所有者。
if (fWasEnabled) EnableWindow(hwndParent, TRUE);DestroyWindow(hdlg);最后,返回结果:
return <value passed to EndDialog>;}恭喜,你现在是对话框专家了。 明天,我们将看看,如何充分利用今天学习到的专业知识。
练习:想办法偷偷穿过两层上面代码中关于父窗口的App Hack,最终得到一个对话框,其所有者是桌面,并解释这种情况的可怕后果。
总结
好了,对话框专家,你看到了:代码之下,没有什么神秘的东西,有因就有果。这个世界,依然是唯物的。
最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。本文来自:《The dialog manager, part 4: The dialog loop》
留言与评论(共有 0 条评论) “” |