Frage WMI-Objekt, um aktuelle Sitzungen mit dem Kundennamen zu erhalten?


Ich möchte in der Lage sein, ein Powershell-Skript zu erstellen, das mir für alle RDP-Sitzungen, die derzeit auf einem Computer aktiv sind, sagt, wer der Benutzer ist und was sein Clientname (Computername) ist.

Ich kann eine Kombination von win32_loggedonnuser und win32_logonsession verwenden, um die Benutzernameninformationen zu erhalten, aber ich kann den Klientennamen in diesen Gegenständen nicht finden (Aufzählungen?).

PS C:\> $logons = gwmi win32_loggedonuser; $lstring = ""; foreach($l in $logons) { $lstring +=$l;} $lstring -match "cephalopod";
False
PS C:\> $sessions = gwmi win32_logonsession; $sstring = ""; foreach($s in $sessions) { $sstring +=$s;} $sstring -match "cephalopod";
False

(Kopffüßer ist mein Computername, der Rechner, der in der Serverbox angemeldet ist)

.

Ich kann sehen, dass HKCU:\Volatile Environment hat den Kundennamen und die temp Schlüssel hat den Benutzernamen darin, aber ich kann nicht von den Schlüsseln allein feststellen, wenn die Sitzung gerade aktiv ist.

Fehle ich einen API-Aufruf, der mir all diese Informationen an einem Ort bringt?

Grundlegende Voraussetzung: Geben Sie den Task-Manager> Benutzer für den Namen des Benutzers und des Clients aus, in dem der Status aktiv ist.


4
2017-12-19 22:07


Ursprung




Antworten:


Es gibt keine WMI-Schnittstelle dafür, die ich kenne.

Fehle ich einen API-Anruf, der mir alle diese Informationen in einem bekommt   Platz?

Ja. Sie können die Daten von der Win32-API abrufen. Aus wtsapi32.dll, um genau zu sein. Sie können ein C-Programm schreiben, oder Sie können P / Invoice von C # oder sogar PowerShell aufrufen.

Da du wahrscheinlich Powershell willst, habe ich dir heute Morgen folgendes geschrieben:

# QuerySessionInformation.ps1
# Written by Ryan Ries, Jan. 2013, with help from MSDN and Stackoverflow.

$Code = @'
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
public class RDPInfo
{
    [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
    }

    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;
            IntPtr clientNamePtr = IntPtr.Zero;
            Int32 sessionCount = 0;
            Int32 retVal = WTSEnumerateSessions(serverHandle, 0, 1, ref SessionInfoPtr, ref sessionCount);
            Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
            Int32 currentSession = (int)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);
                    WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSClientName, out clientNamePtr, out bytes);

                    if(Marshal.PtrToStringAnsi(domainPtr).Length > 0 && Marshal.PtrToStringAnsi(userPtr).Length > 0)
                    {
                        if(Marshal.PtrToStringAnsi(clientNamePtr).Length < 1)                       
                            Console.WriteLine(Marshal.PtrToStringAnsi(domainPtr) + "\\" + Marshal.PtrToStringAnsi(userPtr) + "\tSessionID: " + si.SessionID + "\tClientName: n/a");
                        else
                            Console.WriteLine(Marshal.PtrToStringAnsi(domainPtr) + "\\" + Marshal.PtrToStringAnsi(userPtr) + "\tSessionID: " + si.SessionID + "\tClientName: " + Marshal.PtrToStringAnsi(clientNamePtr));
                    }
                    WTSFreeMemory(clientNamePtr);
                    WTSFreeMemory(userPtr);
                    WTSFreeMemory(domainPtr);
                }
                WTSFreeMemory(SessionInfoPtr);
            }
        }
        catch(Exception ex)
        {
            Console.WriteLine("Exception: " + ex.Message);
        }
        finally
        {
            CloseServer(serverHandle);
        }
    }
}
'@

Add-Type $Code

Kopieren Sie alles in eine Datei namens QuerySessionInformation.ps1. Jetzt starten Sie die 32-Bit-Version von Powershell in C: \ Windows \ SysWOW64 \ WindowsPowershell \ v1.0. Der obige Code verwendet Zeiger, die in einer nativen 64-Bit-Umgebung nicht funktionieren.

Führen Sie das Skript jetzt aus. Wenn Sie die 32-Bit-Version von Powershell nie zuvor auf diesem Server ausgeführt haben, müssen Sie die Skriptausführungsrichtlinie mit Set-ExecutionPolicy ändern, da 32-Bit- und 64-Bit-Powershell separate Ausführungsrichtlinien haben. Beachten Sie, dass es keine Ausgabe vom Skript selbst geben sollte, da es lediglich den .NET-Code kompiliert und zur aktuellen Umgebung hinzufügt. Beachten Sie auch, dass Sie einen Typ mit Add-Type hinzufügen können, ohne die Powershell-Sitzung beenden zu müssen ... AFAIK. Es macht das Debuggen dieser Art von Dingen wirklich nervig, da Sie Powershell jedes Mal neu starten müssen, wenn Sie den Code ändern.

Nachdem der Code geladen wurde, geben Sie Folgendes ein:

PS C:\> [RDPInfo]::ListUsers("REMOTESERVER")

Wenn auf REMOTESERVER aktive Benutzersitzungen vorhanden sind, sieht die Ausgabe folgendermaßen aus:

DOMAIN\UserName  SessionID: 2    ClientName: RYAN-PC

Dies funktioniert sowohl auf Remote-Computern als auch auf dem lokalen Computer. Wenn der Benutzer jedoch keine ausreichenden Berechtigungen für den Remote-Computer besitzt, wird er automatisch fehlschlagen (keine Ausgabe).

Bearbeiten: Es gibt andere Informationen in WTS_INFO_CLASS, die für Sie von Interesse sein könnten, z. B. WTSConnectState und WTSClientAddress. Sie müssen nur nach ihnen suchen.

Bearbeiten: Ich habe diese Lösung auch in nativen Code (C) für die Verwendung in der Befehlszeile konvertiert:

http://www.myotherpcisacloud.com/post/2013/01/16/Usersexe-v1003.aspx


8
2018-01-13 19:58





Würde PS-Terminaldienste (für Powershell) den Trick machen? ich benutze das die ganze Zeit auf unseren 10 Terminalservern.

  1. Laden Sie es herunter und installieren Sie es über diesen Link
  2. PS > Import-Module PSTerminalServices
  3. PS > Get-tssession -computername {name} 

Dies ist ein wunderbares Dienstprogramm.


1
2018-01-17 23:49





Führen Sie quser / server aus: [Name des Servers]> [Pfad zur Textdatei] .txt

Es listet alle Informationen auf und leitet sie in eine Textdatei mit Leerzeichen ein, so dass sie leicht importiert und seziert werden kann. Funktioniert hervorragend und vermeidet jegliche Komplexität beim Aufruf systemeigener APIs, die 32 oder 64 Bit benötigen. Kann alles in verwaltetem Code erfolgen, wenn es sich um eine .Net-fokussierte App handelt.


1
2017-11-06 10:21





http://weblogs.asp.net/owscott/archive/2003/12/30/Managing-Terminal-Services-Sessions-Remotely.aspx

könnte dir nützlicher sein :)


0
2017-12-26 12:28



qwinsta gibt den Gerätenamen als aus rdp-tcp#0, usw; nicht der Client-Name oder der Client, der den Computernamen verbindet. Das hilft mir also nicht. - glasnt
Ja, es ist wirklich ein bisschen Gurke. Möglicherweise müssen Sie auf die Sicherheitsüberwachungsereignisse achten, auch wenn dies ein wenig mühsam sein kann. - Preflightsiren