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

寻求用户登录且桌面初始化完成的Windows事件触发方案

解决方案:等待桌面初始化完成后触发启动屏

你的核心问题是WTS_SESSION_LOGON事件触发时机过早——此时用户会话的桌面环境(由explorer.exe负责)尚未完成初始化,直接启动的启动屏进程因缺少有效桌面上下文或依赖组件提前退出。以下是几种可靠的实现方式:

方法1:监听Explorer.exe启动并等待就绪

桌面的核心进程是explorer.exe,只有当它完全启动并初始化完成后,桌面才真正可用。服务中可通过以下步骤实现:

  • 收到WTS_SESSION_LOGON事件后,持续枚举目标会话的进程,直到检测到explorer.exe启动
  • 调用WaitForInputIdle等待explorer.exe进入空闲状态(确认初始化完成)
  • 再通过CreateProcessAsUser在用户会话中启动启动屏进程

关键代码片段

// 枚举会话内进程的辅助函数
BOOL IsExplorerRunning(DWORD sessionId) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE;

    PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
    if (!Process32First(hSnapshot, &pe)) {
        CloseHandle(hSnapshot);
        return FALSE;
    }

    do {
        DWORD procSessionId;
        if (ProcessIdToSessionId(pe.th32ProcessID, &procSessionId) && procSessionId == sessionId) {
            if (_wcsicmp(pe.szExeFile, L"explorer.exe") == 0) {
                CloseHandle(hSnapshot);
                return TRUE;
            }
        }
    } while (Process32Next(hSnapshot, &pe));

    CloseHandle(hSnapshot);
    return FALSE;
}

// 服务中处理SESSION_LOGON事件的逻辑
void OnSessionLogon(DWORD sessionId) {
    // 等待explorer.exe启动
    while (!IsExplorerRunning(sessionId)) {
        Sleep(200);
    }

    // 获取用户令牌,准备启动会话内进程
    HANDLE hToken;
    if (WTSQueryUserToken(sessionId, &hToken)) {
        // 提升权限(需服务配置对应权限)
        HANDLE hTokenDup;
        if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup)) {
            STARTUPINFO si = { sizeof(si) };
            PROCESS_INFORMATION pi;
            si.lpDesktop = L"winsta0\\default"; // 指定用户默认桌面

            // 启动启动屏进程
            if (CreateProcessAsUser(hTokenDup, L"C:\\YourPath\\SplashScreen.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
                // 等待启动屏进程运行(可选)
                WaitForSingleObject(pi.hProcess, 3000);
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);
            }
            CloseHandle(hTokenDup);
        }
        CloseHandle(hToken);
    }
}

方法2:在用户会话中启动监控进程

服务在收到登录事件后,先启动一个轻量监控进程到用户会话,由该进程等待桌面就绪后再启动启动屏:

  • 监控进程通过GetShellWindow()检查桌面窗口是否存在且可见
  • 或监听WM_SETTINGCHANGE消息(桌面初始化完成时会发送该消息)
  • 确认就绪后启动启动屏并退出

监控进程核心代码

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // 等待桌面窗口就绪
    while (true) {
        HWND hShellWnd = GetShellWindow();
        if (hShellWnd && IsWindowVisible(hShellWnd)) {
            // 启动启动屏
            ShellExecute(NULL, L"open", L"C:\\YourPath\\SplashScreen.exe", NULL, NULL, SW_SHOW);
            break;
        }
        Sleep(100);
    }
    return 0;
}

注意事项

  • 服务需拥有SeAssignPrimaryTokenPrivilege和SeIncreaseQuotaPrivilege权限,否则CreateProcessAsUser会失败
  • 启动屏进程需确保在用户桌面运行,指定lpDesktop = L"winsta0\\default"避免会话0隔离问题
  • 可根据实际情况调整等待间隔,避免占用过多CPU资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 04:32:09