如何使用WM_QUERYENDSESSION或WM_ENDSESSION消息阻止Windows关机重启
阻止Windows关机的正确实现方式
你的代码无法生效有两个核心原因:
- 你定义的
WindowProc窗口过程没有绑定到任何实际创建的顶层窗口,且程序没有运行Windows标准消息循环,系统永远不会向你的进程投递WM_QUERYENDSESSION/WM_ENDSESSION消息,你写的消息处理逻辑是完全不会执行的死代码。 - 从Windows Vista版本开始,仅响应
WM_QUERYENDSESSION返回FALSE无法稳定阻止关机,必须调用专用API向系统声明阻塞原因,否则系统会直接忽略你的拒绝响应,继续执行关机流程。
另外需要提前说明:该方案仅能拦截常规用户发起的关机、重启、注销操作,无法阻止高权限强制关机(比如shutdown /f命令、系统更新触发的强制重启、系统崩溃重启)。
实现步骤
- 注册窗口类,将自定义窗口过程绑定到窗口类
- 创建一个可隐藏的顶层窗口,作为接收系统消息的载体
- 启动标准Windows消息循环,保证系统消息能被正常派发到窗口过程
- 收到
WM_QUERYENDSESSION消息时,先调用ShutdownBlockReasonCreate注册阻塞提示文本,再返回FALSE拒绝关机请求 - 收到
WM_ENDSESSION且wParam为TRUE时,说明关机已无法阻止,立刻执行资源释放、数据保存等收尾操作 - 不需要继续阻塞关机时,调用
ShutdownBlockReasonDestroy释放资源
可运行修正代码
#include <Windows.h> LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_QUERYENDSESSION: { // 向系统注册关机阻塞原因,该文本会显示在系统关机拦截提示页 LPCWSTR blockReason = L"程序存在未完成任务,暂不允许关机"; ShutdownBlockReasonCreate(hwnd, blockReason); // 返回FALSE拒绝关机请求 return FALSE; } case WM_ENDSESSION: { if (wParam == TRUE) { // 关机已不可逆转,在此处执行紧急收尾逻辑 ShutdownBlockReasonDestroy(hwnd); } return 0; } case WM_DESTROY: ShutdownBlockReasonDestroy(hwnd); PostQuitMessage(0); return 0; default: // 未处理的消息必须交给系统默认过程处理,否则窗口会异常 return DefWindowProc(hwnd, uMsg, wParam, lParam); } } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { // 注册窗口类 const wchar_t CLASS_NAME[] = L"ShutdownBlockWindow"; WNDCLASSEXW wc = {0}; wc.cbSize = sizeof(WNDCLASSEXW); wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClassExW(&wc); // 创建隐藏的顶层窗口,不需要显示UI HWND hwnd = CreateWindowExW( 0, CLASS_NAME, L"ShutdownBlockWindow", 0, // 窗口样式置0,默认隐藏 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; // 启动消息循环,这是接收系统消息的必要前提 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
注意事项
- 不要在
WM_QUERYENDSESSION处理逻辑中弹出自定义模态框,新版本Windows会直接屏蔽这类弹窗,判定程序无响应后直接终止进程继续关机,阻塞提示统一通过ShutdownBlockReasonCreate传递即可。 - 消息循环中不要加入长时间阻塞的逻辑,否则窗口无法及时响应系统消息,会被系统判定为无响应,直接被终止。
- 运行在会话0的系统服务无法通过该方法拦截用户会话的关机流程,程序需要和当前登录用户运行在同一会话下才生效。
- 不要尝试在消息处理中做耗时操作,系统给每个程序响应关机消息的时间有严格限制,超时会被强制杀掉。
内容的提问来源于stack exchange,提问作者user17712360
相关产品推荐
相关产品推荐

