Get User SID From Logon ID (Windows XP and Up)
I have a Windows service that needs to access registry hives under HKEY_USERS when users log on, either locally or via Terminal Server. I'm using a WMI query on win32_logonsession to receive events when users log on, and one of the properties I get from that query is a LogonId. To figure out which registry hive I need to access, now, I need the users's SID, which is used as a registry key name beneath HKEY_USERS.
In most cases, I can get this by doing a RelatedObjectQuery like so (in C#):
RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );
where "logonID" is the logon session ID from the session query. Running the RelatedObjectQuery will generally give me a SID property that contains exactly what I need.
There are two issues I have with this. First and most importantly, the RelatedObjectQuery will not return any results for a domain user that logs in with cached credentials, disconnected from the domain. Second, I'm not pleased with the performance of this RelatedObjectQuery --- it can take up to several seconds to execute.
Here's a quick and dirty command line program I threw together to experiment with the queries. Rather than setting up to receive events, this just enumerates the users on the local machine:
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.GetT开发者_C百科ype().ToString();
}
Console.WriteLine( " \"{0}\" = ({1}) \"{2}\"", pd.Name, valueType, value );
}
private static void PrintProperties( PropertyDataCollection properties )
{
foreach ( PropertyData pd in properties )
{
PrintProperty( pd );
}
}
}
}
So... is there way to quickly and reliably obtain the user SID given the information I retrieve from WMI, or should I be looking at using something like SENS instead?
I asked a very similar question a while back and got this answer: how to get a SID from a windows username.
I was planning on using SystemEvents to detect when a user logs on to windows, then looping through the logged on users list at that point to detect all the logged on users. (Here's my question, about all this including references for detecting logons and current users.)
If you decide on an approach please post an update - I'd be interested to hear what you find works well.
Another simple way: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion \ProfileList
Another working answer (code in 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
Powershell is easier.
Function GetSIDfromAcctName()
{
$myacct = Get-WmiObject Win32_UserAccount -filter "Name = '$env:USERNAME "
write-host Name: $myacct.name
Write-Host SID : $myacct.sid
}
精彩评论