Получить идентификатор пользователя из идентификатора входа (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