当用户在本地或通过终端服务器登录时,我有一个 Windows 服务需要访问 HKEY_USERS 下的注册表配置单元。我在 win32_logonsession 上使用 WMI 查询来接收用户登录时的事件,我从该查询中获得的属性之一是 LogonId。为了弄清楚我需要访问哪个注册表配置单元,现在我需要用户的 SID,它用作 HKEY_USERS 下的注册表项名称。
在大多数情况下,我可以通过像这样(在 C# 中)执行 RelatedObjectQuery 来获得它:
RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );
其中“logonID”是会话查询中的登录会话 ID。运行 RelatedObjectQuery 通常会给我一个 SID 属性,其中包含我需要的内容。
我有两个问题。首先,也是最重要的,RelatedObjectQuery 不会为使用缓存凭据登录、与域断开连接的域用户返回任何结果。其次,我对这个 RelatedObjectQuery 的性能不满意 --- 它可能需要几秒钟的时间才能执行。
这是一个快速而肮脏的命令行程序,我拼凑在一起来试验查询。这不是设置接收事件,而是枚举本地机器上的用户:
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 );
}
}
}
}
那么......有没有办法根据我从 WMI 检索到的信息快速可靠地获取用户 SID,还是应该考虑使用类似 SENS 的东西?