2008-09-25 15 views
23

Aquí está el escenario:¿Cómo recuperas una lista de usuarios conectados/conectados en .NET?

Tiene un servidor Windows al que los usuarios se conectan de forma remota a través de RDP. Desea que su programa (que se ejecuta como un servicio) sepa quién está conectado actualmente. Esto puede incluir o no una sesión de consola interactiva.

Tenga en cuenta que este es el no lo mismo que simplemente recuperar el usuario interactivo actual.

Supongo que hay algún tipo de acceso API a Servicios de Terminal Server para obtener esta información?

Respuesta

25

Esta es mi opinión sobre el tema:

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

namespace EnumerateRDUsers 
{ 
    class Program 
    { 
    [DllImport("wtsapi32.dll")] 
    static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName); 

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

    [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); 

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

    [StructLayout(LayoutKind.Sequential)] 
    private struct WTS_SESSION_INFO 
    { 
     public Int32 SessionID; 

     [MarshalAs(UnmanagedType.LPStr)] 
     public String pWinStationName; 

     public WTS_CONNECTSTATE_CLASS State; 
    } 

    public enum WTS_INFO_CLASS 
    { 
     WTSInitialProgram, 
     WTSApplicationName, 
     WTSWorkingDirectory, 
     WTSOEMId, 
     WTSSessionId, 
     WTSUserName, 
     WTSWinStationName, 
     WTSDomainName, 
     WTSConnectState, 
     WTSClientBuildNumber, 
     WTSClientName, 
     WTSClientDirectory, 
     WTSClientProductId, 
     WTSClientHardwareId, 
     WTSClientAddress, 
     WTSClientDisplay, 
     WTSClientProtocolType 
    } 
    public enum WTS_CONNECTSTATE_CLASS 
    { 
     WTSActive, 
     WTSConnected, 
     WTSConnectQuery, 
     WTSShadow, 
     WTSDisconnected, 
     WTSIdle, 
     WTSListen, 
     WTSReset, 
     WTSDown, 
     WTSInit 
    } 

    static void Main(string[] args) 
    { 
     ListUsers("<INSERT SERVERNAME HERE>"); 
    } 

    public static IntPtr OpenServer(String Name) 
    { 
     IntPtr server = WTSOpenServer(Name); 
     return server; 
    } 
    public static void CloseServer(IntPtr ServerHandle) 
    { 
     WTSCloseServer(ServerHandle); 
    } 
    public static void ListUsers(String ServerName) 
    { 
     IntPtr serverHandle = IntPtr.Zero; 
     List<String> resultList = new List<string>(); 
     serverHandle = OpenServer(ServerName); 

     try 
     { 
     IntPtr SessionInfoPtr = IntPtr.Zero; 
     IntPtr userPtr = IntPtr.Zero; 
     IntPtr domainPtr = IntPtr.Zero; 
     Int32 sessionCount = 0; 
     Int32 retVal = WTSEnumerateSessions(serverHandle, 0, 1, ref SessionInfoPtr, ref sessionCount); 
     Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO)); 
     IntPtr currentSession = SessionInfoPtr; 
     uint bytes = 0; 

     if (retVal != 0) 
     { 
      for (int i = 0; i < sessionCount; i++) 
      { 
      WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)currentSession, typeof(WTS_SESSION_INFO)); 
      currentSession += dataSize; 

      WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSUserName, out userPtr, out bytes); 
      WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSDomainName, out domainPtr, out bytes); 

      Console.WriteLine("Domain and User: " + Marshal.PtrToStringAnsi(domainPtr) + "\\" + Marshal.PtrToStringAnsi(userPtr)); 

      WTSFreeMemory(userPtr); 
      WTSFreeMemory(domainPtr); 
      } 

      WTSFreeMemory(SessionInfoPtr); 
     } 
     } 
     finally 
     { 
     CloseServer(serverHandle); 
     } 

    } 

    } 
} 
+11

Sé que esto es un poco necedad de hilo, pero si alguien usa esto hay un puerro de memoria. agregar 'WTSFreeMemory (userPtr); WTSFreeMemory (domainPtr); 'después de' Console.Writeline' para arreglarlo. –

+1

Hola Magnus, probé tu código, pero simplemente devolvió el usuario conectado al sistema cliente actual en mi LAN local. ¿Hay alguna manera de obtener todos los usuarios con nombre de sistema de clientes en una LAN local con el código anterior? –

+0

@M_Mogharrabi, siento que su pregunta realmente no tenga sentido para mí. Este código está destinado a ser ejecutado en un servidor, no sé a qué te refieres con "sistema cliente actual". ¿Puedes volver a redactar? –

5

Bien, una solución a mi propia pregunta.

Puede usar WMI para recuperar una lista de procesos en ejecución. También puede ver a los propietarios de estos procesos. Si observas a los propietarios de "explorer.exe" (y eliminas los duplicados) deberías terminar con una lista de usuarios conectados.

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

namespace TerminalServices 
{ 
    class TSManager 
    { 
    [DllImport("wtsapi32.dll")] 
    static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName); 

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

    [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; 
    } 

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

    public static IntPtr OpenServer(String Name) 
    { 
     IntPtr server = WTSOpenServer(Name); 
     return server; 
    } 
    public static void CloseServer(IntPtr ServerHandle) 
    { 
     WTSCloseServer(ServerHandle); 
    } 
    public static List<String> ListSessions(String ServerName) 
    { 
     IntPtr server = IntPtr.Zero; 
     List<String> ret = new List<string>(); 
     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)); 

     Int32 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(si.SessionID + " " + si.State + " " + si.pWinStationName); 
      } 

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

     return ret; 
    } 
    } 
} 
+0

Esto le da información útil pero no los nombres de usuario. – James

+0

¿Cómo lo llamamos? – Si8

19

Otra opción, si usted no quiere tratar con el P/Invoca a sí mismo, sería el uso de la Cassia biblioteca:

using System; 
using System.Security.Principal; 
using Cassia; 

namespace CassiaSample 
{ 
    public static class Program 
    { 
     public static void Main(string[] args) 
     { 
      ITerminalServicesManager manager = new TerminalServicesManager(); 
      using (ITerminalServer server = manager.GetRemoteServer("your-server-name")) 
      { 
       server.Open(); 
       foreach (ITerminalServicesSession session in server.GetSessions()) 
       { 
        NTAccount account = session.UserAccount; 
        if (account != null) 
        { 
         Console.WriteLine(account); 
        } 
       } 
      } 
     } 
    } 
} 
+1

Parece realmente fácil de usar. Gracias por agregar eso. – James

+0

Cassia es genial, excepto que no puede obtener la dirección de red de origen solo la dirección IP del cliente, que será su dirección de red interna si están detrás de un enrutador. – DaddioNTS

Cuestiones relacionadas