Win32开发:如何在最后一个窗口销毁后退出应用程序
解决Win32应用最后一个窗口关闭时退出的问题
在Win32开发中,确实没有直接的原生API自动实现“所有窗口关闭后退出程序”的逻辑,但有两种可靠的方案可以实现需求——手动计数是行业内的常用方案,简单高效;另一种则是通过系统API动态检查窗口数量。
方案一:手动维护窗口计数(推荐)
这种方法简单、线程安全,是大多数Win32应用(包括你提到的Chromium)的实现方式:
- 定义一个全局的线程安全计数变量,初始值为0
- 每次成功创建窗口时,用
InterlockedIncrement原子递增计数 - 在每个窗口的
WM_DESTROY消息处理中,原子递减计数,当计数为0时调用PostQuitMessage(0)触发程序退出
代码示例:
#include <windows.h> // 全局线程安全计数,避免多线程创建/销毁窗口时的竞态问题 LONG g_windowCount = 0; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: // 原子递减计数,判断是否为最后一个窗口 if (InterlockedDecrement(&g_windowCount) == 0) { PostQuitMessage(0); } break; // 其他消息处理逻辑... default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // 创建窗口的示例函数 HWND CreateMyWindow(HINSTANCE hInstance) { WNDCLASSEX wcex = {0}; wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.lpszClassName = L"MyWindowClass"; RegisterClassEx(&wcex); HWND hWnd = CreateWindowEx(0, L"MyWindowClass", L"Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (hWnd) { // 窗口创建成功,递增计数 InterlockedIncrement(&g_windowCount); ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); } return hWnd; }
方案二:通过系统API动态检查窗口数量
如果你不想维护计数,可以通过GetGuiResources或EnumWindows来动态统计当前进程的顶层窗口数量:
方法A:使用GetGuiResources
GetGuiResources可以直接获取当前进程的顶层窗口数量,注意在WM_DESTROY消息中,当前窗口尚未完全销毁,所以需要将返回值减1后判断是否为0:
case WM_DESTROY: DWORD topLevelCount = GetGuiResources(GetCurrentProcess(), GR_GUIRESOURCES_TOPLEVELWINDOWS); // 当前窗口正在销毁,实际剩余窗口数为返回值减1 if (topLevelCount - 1 == 0) { PostQuitMessage(0); } break;
方法B:使用EnumWindows枚举窗口
通过枚举所有顶层窗口,筛选出属于当前进程的窗口并计数,同样需要减去当前正在销毁的窗口:
// 枚举窗口的回调函数 BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) { DWORD procId; GetWindowThreadProcessId(hWnd, &procId); if (procId == (DWORD)lParam) { // 找到当前进程的窗口,计数加1 *(LONG*)lParam += 1; } return TRUE; } // WM_DESTROY消息处理 case WM_DESTROY: LONG windowCount = 0; EnumWindows(EnumWindowsProc, (LPARAM)&windowCount); if (windowCount - 1 == 0) { PostQuitMessage(0); } break;
注意事项
- 方案一的原子操作确保了多线程环境下的计数准确性,适合有多个窗口线程的应用
- 方案二的
GetGuiResources在部分旧系统版本中可能存在小兼容性问题,EnumWindows更可靠但效率略低,适合窗口数量较少的应用 - Win32的消息循环本身不会自动退出,必须通过
PostQuitMessage触发,所以无论哪种方案,最终都需要调用这个函数来结束程序
内容的提问来源于stack exchange,提问作者Jason1923
相关产品推荐
相关产品推荐

