code it

Martins Tech Blog

Über RDP angemeldete Benutzer ermitteln

Was sich auf den ersten Blick als keine so einfach zu lösende Aufgabe anhört, verliert mit der Hilfe von WMI schon seinen Schrecken. In meinem Post Windows Management Instrumentation mit C# verwenden hab ich schon kurz erklärt, welche .NET-Objekte den Zugriff auf WMI erlauben und wie man dazu vorgeht. Daher hier nur kurz eine mögliche Lösung:

//create a local management scope
ManagementScope managementScope = new ManagementScope();
//create a query for all available sessions (LogonType 2 = Interactive; LogonType 10 = RemoteInteractive)
ObjectQuery sessionQuery = new ObjectQuery("Select * from Win32_LogonSession Where LogonType = 2 OR LogonType = 10");
// execute the query
ManagementObjectSearcher sessionObjectSearcher = new ManagementObjectSearcher(managementScope, sessionQuery);
ManagementObjectCollection sessionObjectCollection = sessionObjectSearcher.Get();

//iterate through all sessions
foreach (ManagementObject sessionObjectItem in sessionObjectCollection)
{
    //determine properties
    uint logonType = (uint)sessionObjectItem["LogonType"];
    string logonId = sessionObjectItem["LogonId"] as string ;

    if (logonType == 2)
    {
        Debug.WriteLine("Logon Type: Console");
    }
    else
    {
        Debug.WriteLine("Logon Type: RDP/Terminal Server");
    }

    //create a new query to get the user information for the selected session
    ObjectQuery associatorQuery = new ObjectQuery(string.Concat("Associators of {Win32_LogonSession.LogonId=", logonId, "} Where AssocClass=Win32_LoggedOnUser Role=Dependent"));
    // execute the query
    ManagementObjectSearcher associatorObjectSearcher = new ManagementObjectSearcher(managementScope, associatorQuery);
    ManagementObjectCollection associatorObjectCollection = associatorObjectSearcher.Get();

    //iterate through all users (should only be one result per session)
    foreach (ManagementObject associatorObjectItem in associatorObjectCollection)
    {
        //determine properties and output the result
        string user = associatorObjectItem["Name"] as string;
        string name = associatorObjectItem["FullName"] as string;
        string domain = associatorObjectItem["Domain"] as string;
        Debug.WriteLine(string.Concat("User: ", user));
        Debug.WriteLine(string.Concat("FullName: " , name));
        Debug.WriteLine(string.Concat("Domain: ", domain));
    }

    Debug.WriteLine(string.Concat("Session start time: ", sessionObjectItem["StartTime"]));
}

Zunächst werden mit Hilfe der Klasse Win32_LogonSession alle Sessions ermittelt, die die Kriterien erfüllen und im Anschluss daran mit Hilfe der ermittelten LogonId noch Informationen zum Benutzer ermittelt

Windows Management Instrumentation mit C# verwenden

Ich hab den Eindruck, für viele Entwickler ist Windows Management Instrumentation (WMI) etwas sehr Undurchschaubares. Und doch muss man gelegentlich auf Systeminformationen zugreifen. Dabei erweist es sich als sehr mächtiges Tool. Hat man einmal verstanden wie es funktioniert kommt man sehr schnell und einfach an die benötigten Informationen. Und genau da liegt eine große Hürde. Um WMI zu verwenden muss man die Objekte mittels WQL (WMI query language) abfragen. Und dafür muss man wissen wie die Syntax ist und welche Objekte man überhaupt verwenden kann. 

Erster Anlaufpunkt dafür ist die MSDN. Hier findet man eine WMI-Referenz, in der die möglichen Klassen mit den jeweiligen Eigenschaften und auch die Syntax der WQL erklärt werden. Möchte man beispielsweise den freien Plattenplatz, Größe und Namen aller Festplatten ermitteln, würde man folgende Query verwenden:

Select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3.

Das geht nun nicht nur auf dem lokalen System, sondern - entsprechende Berechtigungen vorausgesetzt - auch remote.

Um per .NET auf WMI zuzugreifen, steht der System.Management-Namespace zur Verfügung. Damit ist der Zugriff ähnlich simpel wie die Abfrage einer Datenbank. Die wichtigsten Klassen sind:

  • ConnectionOptions
  • ManagementScope
  • ObjectQuery
  • ManagementObjectSearcher
  • ManagementObjectCollection
  • ManagementObject

Das folgende Beispiel zeigt, wie man die oben erzeugte Query in .NET implementieren würde:

//create a local management scope
ManagementScope managementScope = new ManagementScope();
//get stats of fixed disk
ObjectQuery disksQuery = new ObjectQuery("select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3");
//execute the query 
ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher(managementScope, disksQuery);
//get the results
ManagementObjectCollection disksCollection = objectSearcher.Get();
//iterate through found drives and output the info
foreach (ManagementObject disksItem in disksCollection)
{
    // disk name
    Debug.WriteLine("Name : " + disksItem["Name"] as string);
    // free Space in bytes
    Debug.WriteLine("FreeSpace: " + disksItem["FreeSpace"] as string);
    // size in bytes
    Debug.WriteLine("Size: " + disksItem["Size"] as string);
}

Wie man gut erkennen kann, bekommt man keine typisierten Ergebnisse zurück, sondern muss über die Namen der Eigenschaften zugreifen, aber mit Hilfe der WMI-Klassenreferenz ist das recht unproblematisch.