SetWindowPlacement的rcNormalPosition成员工作异常问题排查
我正在开发一款Win32 C++应用,尝试持久化窗口的边界以及最大化/最小化/正常状态。
在窗口的WM_DESTROY处理函数中,我获取窗口的WINDOWPLACEMENT结构并将其存储到注册表中。
在窗口的WM_CREATE处理函数中,我读取之前保存的WINDOWPLACEMENT结构并调用SetWindowPlacement()。若保存结构时窗口处于正常状态,一切符合预期。
但如果保存时窗口处于最小化或最大化状态,窗口虽能按预期显示为最小化或最大化,但无法恢复到正确位置;若保存时为最大化状态,点击还原按钮后窗口大小保持不变。
相关代码
函数声明
[[nodiscard]] std::optional<WINDOWPLACEMENT> LoadMainWindowPlacement(); void SaveMainWindowPlacement(const WINDOWPLACEMENT& placement);
这两个函数中,length成员已被设置:SaveMainWindowPlacement()的length由调用代码设置,LoadMainWindowPlacement()的length在函数内部设置。
WM_DESTROY处理
case WM_DESTROY: { WINDOWPLACEMENT placement = {}; placement.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hwnd, &placement); SaveMainWindowPlacement(placement); PostQuitMessage(0); return 0; }
WM_CREATE处理
case WM_CREATE: { auto placement = LoadMainWindowPlacement(); if (placement.has_value()) { SetWindowPlacement(hwnd, &placement.value()); } else { const auto screenWidth = GetSystemMetrics(SM_CXMAXIMIZED); const auto screenHeight = GetSystemMetrics(SM_CYMAXIMIZED); RECT windowRect; GetWindowRect(hwnd, &windowRect); const auto windowWidth = windowRect.right - windowRect.left; const auto windowHeight = windowRect.bottom - windowRect.top; const auto x = (screenWidth - windowWidth) / 2; const auto y = (screenHeight - windowHeight) / 2; SetWindowPos(hwnd, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); ShowWindow(hwnd, SW_SHOWDEFAULT); } return 0; }
持久化到注册表
HKEY hkSettings; if (RegCreateKeyEx(HKEY_CURRENT_USER, REG_SUB_PATH, 0, nullptr, 0, KEY_WRITE, nullptr, &hkSettings, nullptr) == ERROR_SUCCESS) { RegSetValueEx(hkSettings, L"MainWindowPlacement", 0, REG_BINARY, reinterpret_cast<const BYTE*>(&placement), sizeof(placement)); RegCloseKey(hkSettings); }
从注册表读取
WINDOWPLACEMENT placement = {}; HKEY hkSettings; if (RegOpenKeyEx(HKEY_CURRENT_USER, REG_SUB_PATH, 0, KEY_READ, &hkSettings) == ERROR_SUCCESS) { DWORD dwType; DWORD dwSize = sizeof(placement); if (RegQueryValueEx(hkSettings, L"MainWindowPlacement", 0, &dwType, reinterpret_cast<BYTE*>(&placement), &dwSize) == ERROR_SUCCESS) { if (dwType == REG_BINARY && dwSize == sizeof(placement)) { RegCloseKey(hkSettings); return placement; } } RegCloseKey(hkSettings); }
请问我是否遗漏或操作有误?
1. 错误的状态保存时机
当窗口收到WM_DESTROY时,系统已开始销毁窗口流程,此时GetWindowPlacement获取的结构可能丢失完整的还原位置信息(尤其是窗口处于最大化/最小化状态时)。正确时机是在窗口即将关闭但未销毁的WM_CLOSE阶段。
修改代码,将状态保存逻辑移至WM_CLOSE:
case WM_CLOSE: { WINDOWPLACEMENT placement = {}; placement.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hwnd, &placement); SaveMainWindowPlacement(placement); DestroyWindow(hwnd); // 触发WM_DESTROY return 0; }
同时移除WM_DESTROY中的保存代码,避免重复操作。
2. 重置读取后的length成员
SetWindowPlacement要求WINDOWPLACEMENT的length必须是当前系统下的结构大小,注册表中保存的旧值可能因版本变化失效。读取后必须手动重置该值:
修改读取代码:
WINDOWPLACEMENT placement = {}; HKEY hkSettings; if (RegOpenKeyEx(HKEY_CURRENT_USER, REG_SUB_PATH, 0, KEY_READ, &hkSettings) == ERROR_SUCCESS) { DWORD dwType; DWORD dwSize = sizeof(placement); if (RegQueryValueEx(hkSettings, L"MainWindowPlacement", 0, &dwType, reinterpret_cast<BYTE*>(&placement), &dwSize) == ERROR_SUCCESS) { if (dwType == REG_BINARY && dwSize == sizeof(placement)) { // 关键:重置为当前结构大小 placement.length = sizeof(WINDOWPLACEMENT); RegCloseKey(hkSettings); return placement; } } RegCloseKey(hkSettings); }
3. 确认SetWindowPlacement的显示逻辑
SetWindowPlacement会自动处理窗口的显示状态(包括最大化/最小化),无需额外调用ShowWindow,现有代码中加载状态的分支无需调整。
修复效果验证
- 最大化窗口关闭后重启,窗口保持最大化,还原后回到关闭前的正常位置和大小。
- 最小化窗口关闭后重启,窗口保持最小化,还原后位置正确。
内容的提问来源于stack exchange,提问作者Dave F

