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

如何从Session 0获取Citrix等共享桌面中当前交互用户的Session ID?

从Session 0获取Citrix共享桌面的当前交互用户Session ID

核心问题分析

你遇到的两个核心问题:

  1. Citrix共享桌面环境中存在多个Active状态的会话,常规枚举会话找Active的方式无法定位到用户实际交互的会话;
  2. 代码运行在Session 0时,无法通过当前进程PID关联到用户交互会话的ID。

解决思路与实现方案

1. 区分Citrix环境下的目标交互会话

在Citrix这类多会话远程桌面环境中,仅通过WTSActive状态筛选会话不够,需要结合以下维度精准定位:

  • 会话存在已登录用户(WTSUserName非空);
  • 会话的客户端协议类型为ICA(Citrix专属协议,协议值为0x00000002);
  • 会话处于活跃连接状态(WTS_CONNECTSTATE_CLASS.WTSActive)。

2. 从Session 0中获取目标会话ID

Session 0是服务隔离会话,无法直接关联用户交互会话,需通过枚举所有会话并筛选的方式实现:

  • 调用WTSEnumerateSessionsEx枚举服务器上的所有会话(支持更详细的会话信息);
  • 对每个会话,调用WTSQuerySessionInformation获取会话的用户信息、客户端协议、连接状态;
  • 筛选出符合条件的交互会话,返回其Session ID。

代码实现示例

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

public static class SessionHelper
{
    // Windows API 声明
    [DllImport("wtsapi32.dll", SetLastError = true)]
    private static extern bool WTSEnumerateSessionsEx(IntPtr hServer, ref uint pLevel, uint Filter, ref IntPtr ppSessionInfo, ref uint pCount);

    [DllImport("wtsapi32.dll", SetLastError = true)]
    private static extern void WTSFreeMemoryEx(WTSTypeClass SessionInfoType, IntPtr pMemory, uint NumberOfEntries);

    [DllImport("wtsapi32.dll", SetLastError = true)]
    private static extern bool WTSQuerySessionInformation(IntPtr hServer, uint SessionId, WTS_INFO_CLASS WTSInfoClass, out IntPtr ppBuffer, out uint pBytesReturned);

    [DllImport("wtsapi32.dll")]
    private static extern void WTSFreeMemory(IntPtr pMemory);

    // 枚举会话的级别,使用1获取详细信息
    private const uint WTS_SESSION_INFO_LEVEL_1 = 1;
    // ICA协议类型值
    private const int PROTOCOL_TYPE_ICA = 2;

    // 会话信息结构(级别1)
    [StructLayout(LayoutKind.Sequential)]
    private struct WTS_SESSION_INFO_1
    {
        public uint SessionId;
        public WTS_CONNECTSTATE_CLASS State;
        public uint SessionFlags;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string WinStationName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string UserName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string DomainName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string FarmName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string FarmDomain;
    }

    private enum WTS_CONNECTSTATE_CLASS
    {
        WTSActive,
        WTSConnected,
        WTSConnectQuery,
        WTSShadow,
        WTSDisconnected,
        WTSIdle,
        WTSListen,
        WTSReset,
        WTSDown,
        WTSInit
    }

    private enum WTS_INFO_CLASS
    {
        WTSClientProtocolType = 16
    }

    private enum WTSTypeClass
    {
        WTSTypeSessionInfoLevel1 = 1
    }

    public static uint GetInteractiveSessionId()
    {
        IntPtr hServer = IntPtr.Zero; // 本地服务器用IntPtr.Zero
        uint level = WTS_SESSION_INFO_LEVEL_1;
        IntPtr sessionInfoPtr = IntPtr.Zero;
        uint sessionCount = 0;

        try
        {
            // 枚举所有会话
            if (!WTSEnumerateSessionsEx(hServer, ref level, 0, ref sessionInfoPtr, ref sessionCount))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            // 遍历每个会话
            for (int i = 0; i < sessionCount; i++)
            {
                IntPtr currentSessionPtr = IntPtr.Add(sessionInfoPtr, i * Marshal.SizeOf(typeof(WTS_SESSION_INFO_1)));
                WTS_SESSION_INFO_1 sessionInfo = Marshal.PtrToStructure<WTS_SESSION_INFO_1>(currentSessionPtr);

                // 筛选条件:活跃状态、有登录用户
                if (sessionInfo.State != WTS_CONNECTSTATE_CLASS.WTSActive || string.IsNullOrEmpty(sessionInfo.UserName))
                {
                    continue;
                }

                // 获取客户端协议类型,判断是否为Citrix ICA协议
                IntPtr protocolBuffer = IntPtr.Zero;
                uint bytesReturned = 0;
                if (WTSQuerySessionInformation(hServer, sessionInfo.SessionId, WTS_INFO_CLASS.WTSClientProtocolType, out protocolBuffer, out bytesReturned))
                {
                    int protocolType = Marshal.ReadInt32(protocolBuffer);
                    WTSFreeMemory(protocolBuffer);

                    // 如果是ICA协议,返回该会话ID
                    if (protocolType == PROTOCOL_TYPE_ICA)
                    {
                        return sessionInfo.SessionId;
                    }
                }
                // 非Citrix环境下,返回第一个符合条件的Active会话
                else
                {
                    return sessionInfo.SessionId;
                }
            }

            // 未找到交互会话
            throw new InvalidOperationException("未找到活跃的交互用户会话");
        }
        finally
        {
            // 释放内存
            if (sessionInfoPtr != IntPtr.Zero)
            {
                WTSFreeMemoryEx(WTSTypeClass.WTSTypeSessionInfoLevel1, sessionInfoPtr, sessionCount);
            }
        }
    }
}

额外说明

  • 代码需以管理员权限运行,因为Session 0的进程需要权限枚举其他会话信息;
  • 针对非Citrix的常规远程桌面环境,代码会自动返回第一个符合条件的Active会话;
  • 若存在多个Citrix交互会话,可根据业务需求扩展筛选逻辑(比如结合客户端名称WTSClientName)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 09:08:12