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

如何通过IP远程查询虚拟机当前连接用户(基于WTSQuerySessionInformation)

远程获取虚拟机终端服务连接用户的解决方案

看起来你已经搞定了本地获取终端服务用户的逻辑,现在要把这个能力扩展到远程虚拟机,不用每台都部署程序对吧?其实WTSAPI本身就支持远程调用,核心是先建立和远程服务器的连接,再查询会话信息。

核心思路调整

你当前的代码用的是本地服务器句柄(IntPtr.Zero),要远程访问的话,需要先通过WTSOpenServer函数获取目标虚拟机的服务器句柄,然后用这个句柄去查询会话信息。另外,只查当前会话可能不够,最好遍历所有活跃会话,才能拿到所有连接用户。

修改后的完整代码示例

先补充需要的API导入,然后实现远程查询的逻辑:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class RemoteSessionInfo
{
    // 添加远程服务器连接相关的API
    [DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr WTSOpenServer(string pServerName);

    [DllImport("Wtsapi32.dll")]
    private static extern void WTSCloseServer(IntPtr hServer);

    [DllImport("Wtsapi32.dll")]
    private static extern bool WTSEnumerateSessions(
        IntPtr hServer,
        int Reserved,
        int Version,
        out IntPtr ppSessionInfo,
        out int pCount);

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

    [DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode)]
    private static extern bool WTSQuerySessionInformation(
        IntPtr hServer,
        int sessionId,
        WTS_INFO_CLASS wtsInfoClass,
        out IntPtr ppBuffer,
        out uint pBytesReturned);

    public enum WTS_INFO_CLASS
    {
        WTSInitialProgram, WTSApplicationName, WTSWorkingDirectory, WTSOEMId, 
        WTSSessionId, WTSUserName, WTSWinStationName, WTSDomainName, WTSConnectState, 
        WTSClientBuildNumber, WTSClientName, WTSClientDirectory, WTSClientProductId, 
        WTSClientHardwareId, WTSClientAddress, WTSClientDisplay, WTSClientProtocolType
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct WTS_SESSION_INFO
    {
        public int SessionId;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pWinStationName;
        public WTS_CONNECTSTATE_CLASS State;
    }

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

    /// <summary>
    /// 获取远程虚拟机的所有活跃会话用户信息
    /// </summary>
    /// <param name="remoteServerIp">目标虚拟机IP</param>
    /// <returns>会话ID、用户名、客户端名称的字典</returns>
    public static Dictionary<int, (string UserName, string ClientName)> GetRemoteSessionUsers(string remoteServerIp)
    {
        var sessionUsers = new Dictionary<int, (string, string)>();
        IntPtr serverHandle = IntPtr.Zero;
        IntPtr sessionInfoPtr = IntPtr.Zero;

        try
        {
            // 打开远程服务器连接
            serverHandle = WTSOpenServer(remoteServerIp);
            if (serverHandle == IntPtr.Zero)
            {
                throw new System.ComponentModel.Win32Exception();
            }

            // 枚举所有会话
            int sessionCount;
            if (!WTSEnumerateSessions(serverHandle, 0, 1, out sessionInfoPtr, out sessionCount))
            {
                throw new System.ComponentModel.Win32Exception();
            }

            // 遍历每个会话
            var sessionInfoSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
            for (int i = 0; i < sessionCount; i++)
            {
                var currentSessionPtr = new IntPtr(sessionInfoPtr.ToInt64() + (i * sessionInfoSize));
                var sessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(currentSessionPtr, typeof(WTS_SESSION_INFO));

                // 只处理活跃/已连接的会话(根据需求调整状态)
                if (sessionInfo.State == WTS_CONNECTSTATE_CLASS.WTSActive || sessionInfo.State == WTS_CONNECTSTATE_CLASS.WTSConnected)
                {
                    // 获取用户名
                    string userName = GetSessionInformation(serverHandle, sessionInfo.SessionId, WTS_INFO_CLASS.WTSUserName);
                    // 获取客户端名称
                    string clientName = GetSessionInformation(serverHandle, sessionInfo.SessionId, WTS_INFO_CLASS.WTSClientName);

                    if (!string.IsNullOrEmpty(userName))
                    {
                        sessionUsers.Add(sessionInfo.SessionId, (userName, clientName));
                    }
                }
            }
        }
        finally
        {
            // 释放资源
            if (sessionInfoPtr != IntPtr.Zero)
            {
                WTSFreeMemory(sessionInfoPtr);
            }
            if (serverHandle != IntPtr.Zero)
            {
                WTSCloseServer(serverHandle);
            }
        }

        return sessionUsers;
    }

    /// <summary>
    /// 获取单个会话的指定信息
    /// </summary>
    private static string GetSessionInformation(IntPtr serverHandle, int sessionId, WTS_INFO_CLASS infoClass)
    {
        IntPtr buffer = IntPtr.Zero;
        uint bytesReturned;

        try
        {
            if (WTSQuerySessionInformation(serverHandle, sessionId, infoClass, out buffer, out bytesReturned) && buffer != IntPtr.Zero)
            {
                // 根据API字符集选择合适的转换方式,这里用Unicode
                return Marshal.PtrToStringUni(buffer);
            }
            return string.Empty;
        }
        finally
        {
            if (buffer != IntPtr.Zero)
            {
                WTSFreeMemory(buffer);
            }
        }
    }

    // 示例调用
    public static void Main()
    {
        try
        {
            var remoteUsers = GetRemoteSessionUsers("192.168.1.100");
            foreach (var session in remoteUsers)
            {
                Console.WriteLine($"会话ID: {session.Key}, 用户: {session.Value.UserName}, 客户端名称: {session.Value.ClientName}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"查询失败: {ex.Message}");
        }
    }
}

关键注意事项

  • 权限要求:运行这个程序的Windows账号必须拥有目标虚拟机的管理员权限,或者至少有终端服务的远程查询权限(比如加入Remote Desktop Users组,但管理员权限更稳妥)。
  • 网络配置:目标虚拟机的防火墙要允许终端服务(默认3389端口)的入站连接,同时终端服务(远程桌面服务)必须启用。
  • 错误处理:代码里加了Win32Exception捕获,方便排查连接失败的原因(比如权限不足、网络不通、远程服务未开启等)。
  • 字符集:原来的代码用了PtrToStringAnsi,远程调用建议用PtrToStringUni,避免中文等非ASCII字符乱码。

这样你就可以在一台机器上运行这个程序,通过IP直接查询所有远程虚拟机的连接用户,不用在每台VM上部署任何东西啦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 13:47:42