У меня есть служба 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?