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

如何终止由Windows服务启动的跨用户会话进程?

解决Windows服务跨会话关闭进程的问题

问题背景

Windows服务在用户登录/解锁/连接时,可正常在目标用户会话启动SessionFormRecorder.exe进程;但后续调用Process.CloseMainWindow()无法关闭该进程,此时进程ID仍存在,但Process.ProcessName返回Idle。

核心原因

  1. 进程ID复用:Windows系统会复用已终止进程的ID,保存的进程ID可能已被系统Idle进程或其他进程占用,导致操作对象错误。
  2. 跨会话窗口消息限制:服务默认运行在Session 0,用户会话为Session 1及以上,Session隔离机制导致服务无法向用户会话进程发送WM_CLOSE消息(CloseMainWindow()依赖此机制)。
  3. PID获取错误:代码中RunInUserSession.StartProcessAsCurrentUser调用未正确返回启动进程的实际ID,导致后续操作的PID无效。

解决方案

1. 确保PID正确获取

修改启动进程的代码,使用out参数确保拿到真实的进程ID:

case SessionChangeReason.SessionLogon:
case SessionChangeReason.RemoteConnect:
case SessionChangeReason.SessionUnlock:
    int processId = -1;
    // 用out参数接收真实PID,而非传入初始值-1
    bool isStarted = RunInUserSession.StartProcessAsCurrentUser(
        onRdpSession.sessionId, 
        "c:\\SessionFormRecorder.exe", 
        null, 
        null, 
        false, 
        out processId);
    
    if (isStarted && processId != -1)
    {
        onRdpSession.processId = processId;
        // 额外保存会话ID,用于后续验证
        onRdpSession.targetSessionId = sessionChangeDescription.SessionId;
        WriteToFile("SessionChange" + DateTime.Now.ToLongTimeString() + ", SessionUnlock|RemoteConnect|SessionLogon [" + sessionChangeDescription.SessionId.ToString() + "]" + ", User: " + userInfo.UserName + ", Connect state: " + userInfo.ConnectState.ToString() + ", Client address: " + ipAddress.ToString() + ", user: " + userInfo.UserName + ", WinStationName: " + userInfo.WinStationName.ToString());
    }
    else
    {
        WriteToFile($"启动SessionFormRecorder失败,会话ID:{sessionChangeDescription.SessionId}");
    }
    break;

2. 关闭前验证进程有效性

在关闭进程前,先验证PID对应的进程是否属于目标会话、是否为我们启动的进程,避免操作错误进程:

case SessionChangeReason.SessionLock:
case SessionChangeReason.SessionLogoff:
case SessionChangeReason.RemoteDisconnect:
    try
    {
        Process targetProcess = Process.GetProcessById(onRdpSession.processId);
        // 验证进程会话ID和名称,确保是目标进程
        if (targetProcess.SessionId == onRdpSession.targetSessionId 
            && targetProcess.ProcessName.Equals("SessionFormRecorder", StringComparison.OrdinalIgnoreCase))
        {
            // 先尝试优雅关闭主窗口
            bool closedGracefully = targetProcess.CloseMainWindow();
            if (closedGracefully)
            {
                WriteToFile($"进程{onRdpSession.processId}已优雅关闭");
            }
            else
            {
                // 优雅关闭失败,强制终止
                targetProcess.Kill();
                WriteToFile($"进程{onRdpSession.processId}无法优雅关闭,已强制终止");
            }
        }
        else
        {
            WriteToFile($"进程ID{onRdpSession.processId}不属于目标会话或不是SessionFormRecorder,跳过关闭");
        }
    }
    catch (ArgumentException)
    {
        WriteToFile($"进程ID{onRdpSession.processId}已不存在");
    }
    catch (InvalidOperationException ex)
    {
        WriteToFile($"关闭进程失败:{ex.Message}");
    }
    break;

3. 备选方案:使用进程句柄终止(更可靠)

如果跨会话权限问题依然存在,可在启动进程时保存进程句柄,通过Windows API直接终止:

// 引入P/Invoke方法
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);

// 启动进程时保存句柄(需要修改StartProcessAsCurrentUser,让它返回进程句柄)
IntPtr processHandle = IntPtr.Zero;
bool isStarted = RunInUserSession.StartProcessAsCurrentUser(
    onRdpSession.sessionId, 
    "c:\\SessionFormRecorder.exe", 
    null, 
    null, 
    false, 
    out processId, 
    out processHandle);

if (isStarted)
{
    onRdpSession.processHandle = processHandle;
}

// 关闭时使用句柄终止
if (onRdpSession.processHandle != IntPtr.Zero)
{
    TerminateProcess(onRdpSession.processHandle, 0);
    CloseHandle(onRdpSession.processHandle);
    onRdpSession.processHandle = IntPtr.Zero;
}

注意事项

  • CloseMainWindow()仅对带有图形界面的进程有效,如果SessionFormRecorder.exe是控制台程序或无主窗口,该方法会直接返回false,需直接使用Kill()或TerminateProcess。
  • 服务需要具备足够权限访问用户会话进程,可在服务安装时设置“允许服务与桌面交互”(但不推荐,更安全的方式是通过会话ID获取权限)。

内容的提问来源于stack exchange,提问作者H Aßdöµ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 06:03:19