You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试RegisterApplicationRestart()以应对更新导致的程序关闭?

MFC托盘程序更新关闭后的重启解决方案及测试方法

一、集成RegisterApplicationRestart()实现自动重启

在程序初始化阶段(比如CWinApp::InitInstance())调用RegisterApplicationRestart(),指定合适的重启条件,确保只有系统更新导致的关闭才触发重启:

#include <windows.h>
#include <restartmanager.h>

BOOL CYourTrayApp::InitInstance()
{
    // 常规初始化代码...

    // 注册重启逻辑:仅在系统更新/补丁安装导致关闭时重启,忽略崩溃、挂起场景
    HRESULT hr = RegisterApplicationRestart(NULL, RESTART_NO_CRASH | RESTART_NO_HANG | RESTART_NO_PATCH);
    if (FAILED(hr))
    {
        TRACE(_T("RegisterApplicationRestart 调用失败,错误码: 0x%X\n"), hr);
        // 可写入日志记录失败信息
    }

    return TRUE;
}

参数说明:

  • RESTART_NO_CRASH:程序崩溃时不重启
  • RESTART_NO_HANG:程序挂起时不重启
  • RESTART_NO_PATCH:仅当系统安装更新/补丁触发关闭时才重启,完全匹配你的场景

二、测试RegisterApplicationRestart()的有效性

1. 模拟系统更新的关闭消息

编写工具程序,向你的托盘程序发送WM_QUERYENDSESSION和WM_ENDSESSION消息(带ENDSESSION_CLOSEAPP标记,对应更新场景):

#include <windows.h>
#include <tlhelp32.h>

HWND FindTrayAppMainWindow(const TCHAR* exeName)
{
    HWND targetWnd = NULL;
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE) return NULL;

    PROCESSENTRY32 pe32{sizeof(PROCESSENTRY32)};
    if (Process32First(hProcessSnap, &pe32))
    {
        do
        {
            if (_tcscmp(pe32.szExeFile, exeName) == 0)
            {
                EnumWindows([](HWND hWnd, LPARAM lParam) -> BOOL {
                    DWORD procId;
                    GetWindowThreadProcessId(hWnd, &procId);
                    if (procId == (DWORD)lParam && IsWindowVisible(hWnd))
                    {
                        *(HWND*)lParam = hWnd;
                        return FALSE;
                    }
                    return TRUE;
                }, (LPARAM)&targetWnd);
                break;
            }
        } while (Process32Next(hProcessSnap, &pe32));
    }
    CloseHandle(hProcessSnap);
    return targetWnd;
}

int _tmain(int argc, TCHAR* argv[])
{
    HWND trayWnd = FindTrayAppMainWindow(_T("YourTrayApp.exe"));
    if (!trayWnd) return 1;

    // 发送WM_QUERYENDSESSION,告知程序系统即将因更新关闭
    BOOL canClose = SendMessage(trayWnd, WM_QUERYENDSESSION, 0, ENDSESSION_CLOSEAPP);
    if (canClose)
    {
        // 发送WM_ENDSESSION,确认关闭
        SendMessage(trayWnd, WM_ENDSESSION, TRUE, ENDSESSION_CLOSEAPP);
    }
    return 0;
}

编译运行该工具后,观察托盘程序是否关闭并自动重启,可通过日志记录启动/关闭时间、进程ID来验证。

2. 使用shutdown命令模拟重启提示

以管理员身份打开命令提示符,执行:

shutdown /r /t 30 /c "模拟系统更新重启" /d p:4:1

该命令会触发系统重启流程,发送WM_QUERYENDSESSION和WM_ENDSESSION消息给所有程序。此时点击重启提示的“取消”,但程序已收到关闭消息,若注册生效,程序会被关闭并重启。

三、模拟真实更新场景

1. 安装Windows可选预览更新

手动安装Windows可选预览更新(部分更新无需重启,但会关闭占用系统资源的程序),观察托盘程序是否被关闭后自动重启。

2. 用Restart Manager API模拟更新流程

直接调用Restart Manager的API,模拟系统更新时关闭程序并重启的逻辑:

#include <windows.h>
#include <restartmanager.h>

int _tmain()
{
    DWORD sessionHandle;
    WCHAR sessionKey[CCH_RM_SESSION_KEY + 1];
    HRESULT hr = RmStartSession(&sessionHandle, 0, sessionKey);
    if (FAILED(hr)) return 1;

    // 替换为你的托盘程序进程ID
    DWORD targetProcId = 1234;
    RM_PROCESS_INFO procInfo{0};
    procInfo.dwProcessId = targetProcId;

    hr = RmRegisterResources(sessionHandle, 0, NULL, 1, &procInfo, 0, NULL);
    if (SUCCEEDED(hr))
    {
        DWORD rebootReasons;
        // 关闭注册的程序
        hr = RmShutdown(sessionHandle, RM_SHUTDOWN_REASON_INSTALLATION, &rebootReasons);
        if (SUCCEEDED(hr))
        {
            // 重启程序
            RmRestart(sessionHandle, 0, NULL);
        }
    }

    RmEndSession(sessionHandle);
    return 0;
}

此方法完全复刻Windows更新时的处理逻辑,测试结果最贴近真实场景。

关键注意事项

  • 程序需以对应权限运行:若托盘程序是管理员权限启动,Restart Manager会以相同权限重启它。
  • 处理关闭逻辑:在WM_QUERYENDSESSION消息中返回TRUE,表示程序可以安全关闭;若返回FALSE,Restart Manager可能会强制终止程序,但仍会触发重启。
  • 系统版本限制:RegisterApplicationRestart()仅支持Windows Vista及以上系统,XP及以下无法使用。

内容的提问来源于stack exchange,提问作者KokoCa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 11:43:24