C#获取RDC / RDP和“控制台”会话信息

9
我正在尝试通过C#编程以编程方式检索一些RDC/RDP和“控制台”登录信息。
我想开发一个简单的控制台应用程序(.EXE),这样我就可以从我们域中任何远程计算机的任务管理器->用户选项卡中检索信息(Windows Server 2003 x86或2008R2 x64)。
这显示一个人是直接登录到服务器上(即控制台)还是通过RDC/RDP(包括客户端,如果它仍然活动)或暂停状态(即他们没有退出登录,只是暂时关闭了RDC/RDP窗口)。
我对所有服务器都有管理员权限,并且可以配置需要启用/禁用的任何Windows服务/防火墙规则(如果需要)。
我认为我可能需要使用WMI(使用System.Management),但是我在谷歌上找到的示例仅检索现有用户。
//Method 1
var searcher = new ManagementObjectSearcher(
               "SELECT UserName FROM Win32_ComputerSystem");
var collection = Searcher.Get();
foreach(ManagementObject entry in collection)
{
  Console.WriteLine(entry["UserName"]);
}

//Method 2
string computer = "somecomputername";   
var searcher = new ManagementObjectSearcher(
               computer + @"root\CIMV2", "SELECT * FROM Win32_TerminalService");
var collection = Searcher.Get();
foreach(ManagementObject entry in collection)
{
  //Write them out (although usernames isnt listed from the example I found)
}
1个回答

30

这将为您提供所需的一切。只需调用ListSessions并传入服务器名称即可。要获取其他会话信息,请将ServerNameSessionId传递给GetSessionInfo

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

namespace TerminalTools
{
    public class TermServicesManager
    {

        [DllImport("wtsapi32.dll")]
        static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);

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

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

        [DllImport("wtsapi32.dll")]
        static extern Int32 WTSEnumerateSessions(IntPtr hServer, [MarshalAs(UnmanagedType.U4)] Int32 Reserved,
            [MarshalAs(UnmanagedType.U4)] Int32 Version, ref IntPtr ppSessionInfo, [MarshalAs(UnmanagedType.U4)] ref Int32 pCount);

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

        [StructLayout(LayoutKind.Sequential)]
        private struct WTS_SESSION_INFO
        {
            public Int32 SessionID;
            [MarshalAs(UnmanagedType.LPStr)]
            public String pWinStationName;
            public WTS_CONNECTSTATE_CLASS State;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WTS_CLIENT_ADDRESS
        {
            public uint AddressFamily;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] Address;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WTS_CLIENT_DISPLAY
        {
            public uint HorizontalResolution;
            public uint VerticalResolution;
            public uint ColorDepth;
        }

        public enum WTS_CONNECTSTATE_CLASS
        {
            Active,
            Connected,
            ConnectQuery,
            Shadow,
            Disconnected,
            Idle,
            Listen,
            Reset,
            Down,
            Init
        }

        public enum WTS_INFO_CLASS
        {
            InitialProgram = 0,
            ApplicationName = 1,
            WorkingDirectory = 2,
            OEMId = 3,
            SessionId = 4,
            UserName = 5,
            WinStationName = 6,
            DomainName = 7,
            ConnectState = 8,
            ClientBuildNumber = 9,
            ClientName = 10,
            ClientDirectory = 11,
            ClientProductId = 12,
            ClientHardwareId = 13,
            ClientAddress = 14,
            ClientDisplay = 15,
            ClientProtocolType = 16
        }

                        private static IntPtr OpenServer(string Name)
    {
        IntPtr server = WTSOpenServer(Name);
        return server;
    }

                    private static void CloseServer(IntPtr ServerHandle)
    {
        WTSCloseServer(ServerHandle);
    }

        public static List<TerminalSessionData> ListSessions(string ServerName)
        {
            IntPtr server = IntPtr.Zero;
            List<TerminalSessionData> ret = new List<TerminalSessionData>();
            server = OpenServer(ServerName);

            try
            {
                IntPtr ppSessionInfo = IntPtr.Zero;

                Int32 count = 0;
                Int32 retval = WTSEnumerateSessions(server, 0, 1, ref ppSessionInfo, ref count);
                Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));

                Int64 current = (int)ppSessionInfo;

                if (retval != 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
                        current += dataSize;

                        ret.Add(new TerminalSessionData(si.SessionID, si.State, si.pWinStationName));
                    }

                    WTSFreeMemory(ppSessionInfo);
                }
            }
            finally
            {
                CloseServer(server);
            }

            return ret;
        }

        public static TerminalSessionInfo GetSessionInfo(string ServerName, int SessionId)
        {
            IntPtr server = IntPtr.Zero;
            server = OpenServer(ServerName);
            System.IntPtr buffer = IntPtr.Zero;
            uint bytesReturned;
            TerminalSessionInfo data = new TerminalSessionInfo();

            try
            {
                bool worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ApplicationName, out buffer, out bytesReturned);

                if (!worked)
                    return data;

                string strData = Marshal.PtrToStringAnsi(buffer);
                data.ApplicationName = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientAddress, out buffer, out bytesReturned);

                if (!worked)
                    return data;

                WTS_CLIENT_ADDRESS si = (WTS_CLIENT_ADDRESS)Marshal.PtrToStructure((System.IntPtr)buffer, typeof(WTS_CLIENT_ADDRESS));
                data.ClientAddress = si;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientBuildNumber, out buffer, out bytesReturned);

                if (!worked)
                    return data;

                int lData = Marshal.ReadInt32(buffer);
                data.ClientBuildNumber = lData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientDirectory, out buffer, out bytesReturned);

                if (!worked)
                    return data;

                strData = Marshal.PtrToStringAnsi(buffer);
                data.ClientDirectory = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientDisplay, out buffer, out bytesReturned);

                if (!worked)
                    return data;

                WTS_CLIENT_DISPLAY cd = (WTS_CLIENT_DISPLAY)Marshal.PtrToStructure((System.IntPtr)buffer, typeof(WTS_CLIENT_DISPLAY));
                data.ClientDisplay = cd;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientHardwareId, out buffer, out bytesReturned);

                if (!worked)
                    return data;

                lData = Marshal.ReadInt32(buffer);
                data.ClientHardwareId = lData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientName, out buffer, out bytesReturned);
                strData = Marshal.PtrToStringAnsi(buffer);
                data.ClientName = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientProductId, out buffer, out bytesReturned);
                Int16 intData = Marshal.ReadInt16(buffer);
                data.ClientProductId = intData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ClientProtocolType, out buffer, out bytesReturned);
                intData = Marshal.ReadInt16(buffer);
                data.ClientProtocolType = intData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.ConnectState, out buffer, out bytesReturned);
                lData = Marshal.ReadInt32(buffer);
                data.ConnectState = (WTS_CONNECTSTATE_CLASS)Enum.ToObject(typeof(WTS_CONNECTSTATE_CLASS), lData);

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.DomainName, out buffer, out bytesReturned);
                strData = Marshal.PtrToStringAnsi(buffer);
                data.DomainName = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.InitialProgram, out buffer, out bytesReturned);
                strData = Marshal.PtrToStringAnsi(buffer);
                data.InitialProgram = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.OEMId, out buffer, out bytesReturned);
                strData = Marshal.PtrToStringAnsi(buffer);
                data.OEMId = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.SessionId, out buffer, out bytesReturned);
                lData = Marshal.ReadInt32(buffer);
                data.SessionId = lData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.UserName, out buffer, out bytesReturned);
                strData = Marshal.PtrToStringAnsi(buffer);
                data.UserName = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.WinStationName, out buffer, out bytesReturned);
                strData = Marshal.PtrToStringAnsi(buffer);
                data.WinStationName = strData;

                worked = WTSQuerySessionInformation(server, SessionId,
                    WTS_INFO_CLASS.WorkingDirectory, out buffer, out bytesReturned);
                strData = Marshal.PtrToStringAnsi(buffer);
                data.WorkingDirectory = strData;
            }
            finally
            {
                WTSFreeMemory(buffer);
                buffer = IntPtr.Zero;
                CloseServer(server);
            }

            return data;
        }

    }

    public class TerminalSessionData
    {
        public int SessionId;
        public TermServicesManager.WTS_CONNECTSTATE_CLASS ConnectionState;
        public string StationName;

                            public TerminalSessionData(int sessionId, TermServicesManager.WTS_CONNECTSTATE_CLASS connState, string stationName)
    {
        SessionId = sessionId;
        ConnectionState = connState;
        StationName = stationName;
    }

                    public override string ToString()
    {
        return String.Format("{0} {1} {2}", SessionId, ConnectionState, StationName);
    }
    }

    public class TerminalSessionInfo
    {
        public string InitialProgram;
        public string ApplicationName;
        public string WorkingDirectory;
        public string OEMId;
        public int SessionId;
        public string UserName;
        public string WinStationName;
        public string DomainName;
        public TermServicesManager.WTS_CONNECTSTATE_CLASS ConnectState;
        public int ClientBuildNumber;
        public string ClientName;
        public string ClientDirectory;
        public int ClientProductId;
        public int ClientHardwareId;
        public TermServicesManager.WTS_CLIENT_ADDRESS ClientAddress;
        public TermServicesManager.WTS_CLIENT_DISPLAY ClientDisplay;
        public int ClientProtocolType;
    }
}

1
嗨,Hovercrafty。这里从“用户”选项卡返回了正确的数据(连接数、会话 ID 和“连接类型”),但是缺少两个关键列(任务管理器中的“用户”选项卡中的 Username 和 ClientName 值)。我需要将 WTS_SESSION_INFO 和/或 TerminalSessionData.SessionId 传递到另一个方法中来检索这两列吗? - Kyle
@Kyle 我已经更新了答案,包括你需要获取额外会话数据的方法。 - Sean Kornish
1
很好的答案,这帮助我枚举并识别了本地存储应用程序中的会话,而不需要构建CMD参数。 - Bassie
这个功能为我构建一些非常有用的工具来确定远程用户状态提供了很好的开端。向原作者致敬。 - kerl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接