2008-09-25 10 views
23

Ecco lo scenario:Come si recupera un elenco di utenti connessi/connessi in .NET?

Si dispone di un server Windows che gli utenti possono connettersi in remoto tramite RDP. Vuoi che il tuo programma (che viene eseguito come servizio) sappia chi è attualmente connesso. Questo può o non può includere una sessione di console interattiva.

Si prega di notare che questo è il non lo stesso come solo recuperare l'utente interattivo corrente.

Suppongo che ci sia una sorta di accesso API a Servizi terminal per ottenere queste informazioni?

risposta

25

Ecco il mio prendere sulla questione:

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

So che questo è un po 'di necomancy del thread ma se qualcuno lo usa c'è un porro di memoria. aggiungere 'WTSFreeMemory (userPtr); WTSFreeMemory (domainPtr); 'dopo la' Console.Writeline' per risolverlo. –

+1

Ciao Magnus, ho provato il tuo codice, ma ho appena restituito l'utente connesso al sistema client corrente nella mia LAN locale. C'è un modo per ottenere tutti gli utenti registrati con nome client dei sistemi in una LAN locale con il codice sopra? –

+0

@M_Mogharrabi, mi spiace che la tua domanda non abbia davvero senso per me. Questo codice è pensato per essere eseguito su un server, non so cosa intendi per "attuale sistema client" Puoi ripetere la frase? –

5

Ok, una soluzione alla mia domanda.

È possibile utilizzare WMI per recuperare un elenco di processi in esecuzione. Puoi anche guardare i proprietari di questi processi. Se si guardano i proprietari di "explorer.exe" (e si rimuovono i duplicati) si dovrebbe finire con un elenco di utenti registrati.

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

Questo ti dà alcuni utili ma non i nomi utente. – James

+0

Come lo chiamiamo? – Si8

19

Un'altra opzione, se non si vuole affrontare con la P te stesso/Richiama, sarebbe quella di utilizzare la libreria Cassia:

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

Sembra davvero facile da usare. Grazie per averlo aggiunto. – James

+0

Cassia è eccezionale, tranne che non è possibile ottenere l'indirizzo di rete di origine solo l'indirizzo IP del client che sarà il loro indirizzo di rete interno se si trova dietro un router. – DaddioNTS

Problemi correlati