我想在单击将成为主窗口子项的菜单项时使用CreateWindow()创建一个窗口。我知道我可以使用DialogBox()或CreateDialog(),但我想使用CreateWindow()。我使用此代码
resource.rc文件
#include "resource.h"
IDM_MENU MENU
{
POPUP "&Help"
{
MENUITEM "&About", IDM_HELP
}
}
关于窗口程序
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
主窗口程序
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_HELP:
{
WNDCLASSEX wc;
HWND hDlg;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = AboutProc;
wc.lpszClassName = TEXT("AboutClass");
if(!RegisterClassEx(&wc))
break;
hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);
ShowWindow(hDlg, SW_SHOWNORMAL);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(wc.lpszClassName, wc.hInstance);
}
break;
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
这是个好主意吗?你可以在同一个实例中注册多个课程吗?另外,最好将主窗口图标分配给这个子窗口,还是应该每次都加载它们?当我在IDM_HELP中调用UnregisterClass()时,是否会删除这些图标?我已经尝试过这个程序,一切正常,关闭这个子窗口后,图标仍会显示在主窗口中。但我仍然想知道将主窗口图标分配给此窗口是否可以,因为我在子窗口关闭后调用UnregisterClass()
答案 0 :(得分:2)
是的,您可以使用CreateWindow()
。您可以在事件处理程序中执行任何。使用DialogBox()
只会为您提供一个免费的模态循环(因此在关闭对话框之前,您的主窗口无法进行交互)。
是的,您可以注册多个窗口类。您可以提前注册所有窗口课程;每次有人点击您的菜单项时,您都不需要致电RegisterClass()
和UnregisterClass()
。
我不确定UnregisterClass()
是否释放了分配给您的窗口类的各种GDI资源;任何知道答案的人都可以自由发表评论。
答案 1 :(得分:2)
使用CreateWindow/Ex()
代替CreateDialog()
/ DialogBox()
并没有错。运行自己的模态消息循环没有任何问题,只要您正确地实现它 。例如,请注意这个警告:
Modality, part 3: The WM_QUIT message
关于模态的另一个重要的事情是WM_QUIT消息总是打破模态循环。在你自己的模态循环中记住这一点!如果您调用PeekMessage函数或GetMessage函数并获取WM_QUIT消息,则必须不仅退出模态循环,而且还必须重新生成WM_QUIT消息(通过PostQuitMessage消息),以便下一个外层将看到WM_QUIT消息并进行清理。如果您无法传播消息,则下一个外层将不知道它需要退出,并且该程序似乎会被卡住"在其关闭代码中,强制用户以艰难的方式终止进程。
您展示的示例不是这样做的,因此您需要添加它:
ShowWindow(hDlg, SW_SHOWNORMAL);
do
{
BOOL bRet = GetMessage(&msg, 0, 0, 0);
if (bRet > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if (bRet == 0)
PostQuitMessage(msg.wParam); // <-- add this!
break;
}
}
while (1);
UnregisterClass(wc.lpszClassName, wc.hInstance);
但是,你的模态窗口不应该仅使用WM_QUIT
来打破它的模态循环,因为这样做会退出整个应用程序!当窗口关闭时,使用不同的信号使模态循环中断。例如:
ShowWindow(hDlg, SW_SHOWNORMAL);
while (IsWindow(hDlg) && (GetMessage(&msg, 0, 0, 0) > 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
此外,模式窗口应该禁用其所有者窗口,然后在关闭时重新启用它。您的示例也没有这样做,所以也需要添加:
ShowWindow(hDlg, SW_SHOWNORMAL);
EnableWindow(hwnd, FALSE); // <-- add this
...
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
EnableWindow(GetWindow(hwnd, GW_OWNER), TRUE); // <-- add this
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Old New Thing博客有一系列关于如何使用模态窗口的帖子。
Modality, part 1: UI-modality vs code-modality
Modality, part 2: Code-modality vs UI-modality
Modality, part 3: The WM_QUIT message
Modality, part 4: The importance of setting the correct owner for modal UI
Modality, part 5: Setting the correct owner for modal UI
Modality, part 6: Interacting with a program that has gone modal
Modality, part 7: A timed MessageBox, the cheap version
Modality, part 8: A timed MessageBox, the better version
Modality, part 9: Setting the correct owner for modal UI, practical exam
The correct order for disabling and enabling windows
Make sure you disable the correct window for modal UI
更新:根据您的评论&#34;不,我不想要模态窗口&#34;,您可以忽略上述所有内容。所有这些仅适用于模态窗口。由于您不需要模态窗口,只需完全删除辅助循环并让主消息循环处理所有内容。此外,您无需致电UnregisterClass()
。它将在进程结束时自动取消注册。在程序启动时或至少在第一次显示“关于”窗口时,调用RegisterClass()
一次。您可以使用GetClassInfo/Ex()
来了解该类是否已注册,或自行跟踪。想想如果用户想要在流程的生命周期中多次显示“关于”窗口,会发生什么。因此,每次都要重复使用现有的课程注册。
试试这个:
LRESULT CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_HELP:
{
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = GetModuleHandle(0);
wc.lpszClassName = TEXT("AboutClass");
if (!GetClassInfoEx(wc.hInstance, wc.lpszClassName, &wc))
{
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
wc.hIconSm = (HICON)GetClassLong(hwnd, GCL_HICONSM);
wc.lpfnWndProc = AboutProc;
if (!RegisterClassEx(&wc))
break;
}
HWND hDlg = CreateWindowEx(0, wc.lpszClassName, TEXT("About"), WS_OVERLAPPEDWINDOW, 0, 0, 300, 200, hwnd, 0, wc.hInstance, 0);
if (hDlg)
ShowWindow(hDlg, SW_SHOWNORMAL);
}
break;
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}