Получить идентификатор пользователя из идентификатора входа (Windows XP и выше)

У меня есть служба Windows, которая должна получить доступ к кустам реестра под HKEY_USERS, когда пользователи входят в систему, локально или через Terminal Server. Я использую WMI-запрос на win32_logonsession для получения событий при входе в систему, и одним из свойств, которые я получаю из этого запроса, является LogonId. Чтобы выяснить, какой куст реестра мне нужен, теперь мне нужен SID пользователей, который используется как имя ключа реестра под HKEY_USERS.

В большинстве случаев я могу получить это, выполнив функцию RelatedObjectQuery, например (на С#):

RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );

где "logonID" - это идентификатор сеанса входа в систему из запроса сеанса. Выполнение функции RelatedObjectQuery обычно дает мне свойство SID, которое содержит именно то, что мне нужно.

У меня есть две проблемы. Во-первых, и самое главное, связанный с этим объект не возвращает никаких результатов для пользователя домена, который входит в систему с кэшированными учетными данными, отключенными от домена. Во-вторых, я не доволен производительностью этого связанного объектаObjectQuery. Это может занять до нескольких секунд.

Здесь быстро и грязная программа командной строки, которую я собрал вместе, чтобы экспериментировать с запросами. Вместо того, чтобы настраивать прием событий, это просто перечисляет пользователей на локальном компьютере:

using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace EnumUsersTest
{
    class Program
    {
        static void Main( string[] args )
        {
            ManagementScope scope = new ManagementScope( "\\\\.\\root\\cimv2" );

            string queryString = "select * from win32_logonsession";                          // for all sessions
            //string queryString = "select * from win32_logonsession where logontype = 2";     // for local interactive sessions only

            ManagementObjectSearcher sessionQuery = new ManagementObjectSearcher( scope, new SelectQuery( queryString ) );
            ManagementObjectCollection logonSessions = sessionQuery.Get();
            foreach ( ManagementObject logonSession in logonSessions )
            {
                string logonID = logonSession["LogonId"].ToString();
                Console.WriteLine( "=== {0}, type {1} ===", logonID, logonSession["LogonType"].ToString() );
                RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );
                ManagementObjectSearcher userQuery = new ManagementObjectSearcher( scope, relatedQuery );
                ManagementObjectCollection users = userQuery.Get();
                foreach ( ManagementObject user in users )
                {
                    PrintProperties( user.Properties );
                }
            }

            Console.WriteLine( "\nDone! Press a key to exit..." );
            Console.ReadKey( true );
        }


        private static void PrintProperty( PropertyData pd )
        {
            string value = "null";
            string valueType = "n/a";
            if ( pd.Value != null )
            {
                value = pd.Value.ToString();
                valueType = pd.Value.GetType().ToString();
            }

            Console.WriteLine( "  \"{0}\" = ({1}) \"{2}\"", pd.Name, valueType, value );
        }


        private static void PrintProperties( PropertyDataCollection properties )
        {
            foreach ( PropertyData pd in properties )
            {
                PrintProperty( pd );
            }
        }
    }
}

Итак... есть способ быстро и надежно получить SID пользователя, учитывая информацию, которую я получаю от WMI, или я должен смотреть на использование чего-то вроде SENS?

Ответ 1

Я спросил очень похожий вопрос некоторое время назад и получил этот ответ: как получить SID из имени пользователя Windows.

Я планировал использовать SystemEvents для обнаружения, когда пользователь входит в Windows, а затем перебирает список зарегистрированных пользователей в этот момент, чтобы обнаружить всех зарегистрированных пользователей. (Здесь мой вопрос, обо всем этом, включая ссылки для обнаружения логинов и текущих пользователей.)

Если вы решите подход, опубликуйте обновление - мне было бы интересно услышать, что вы находите, работает хорошо.

Ответ 2

Еще один простой способ: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ПрофильList

Ответ 3

Powershell легче.

Function GetSIDfromAcctName()
{
$myacct = Get-WmiObject Win32_UserAccount -filter "Name = '$env:USERNAME " 
write-host Name: $myacct.name
Write-Host SID : $myacct.sid
}

Ответ 4

Другой рабочий ответ (код в VB.Net)

Public Function GetSIDfromAccName(ByVal strAccName As String) As String
        Debug.WriteLine("***WMI-GetSIDfromAccName***")
        Dim strSID As String = ""
        Try
            Dim wmiClass As System.Management.SelectQuery = New System.Management.SelectQuery(("Select * from Win32_UserAccount where Name='" _
              + (strAccName + "'")))
            Dim wmiSearcher As System.Management.ManagementObjectSearcher = New System.Management.ManagementObjectSearcher(wmiClass)
            For Each val As System.Management.ManagementBaseObject In wmiSearcher.Get
                strSID = val("SID").ToString
            Next
        Catch e As Exception
            Debug.WriteLine(e.ToString)
        End Try
        Return strSID
    End Function