从登录ID获取用户SID(Windows XP及更高版本)

时间:2010-04-28 07:01:04

标签: c# .net windows wmi sid

我有一个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 );
            }
        }
    }
}

所以...有没有办法快速可靠地获取用户SID给出我从WMI检索的信息,或者我应该考虑使用像SENS这样的东西?

4 个答案:

答案 0 :(得分:2)

前一段时间我问a very similar question并得到了这个答案:how to get a SID from a windows username

我计划使用SystemEvents来检测用户何时登录到Windows,然后在此时循环登录用户列表以检测所有已登录的用户。 (Here's my question,关于所有这些,包括检测登录和当前用户的参考。)

如果您决定采用某种方法,请发布更新 - 我很想知道您认为哪些方法有效。

答案 1 :(得分:1)

另一种简单方法: HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ ProfileList

答案 2 :(得分:1)

Powershell更容易。

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

答案 3 :(得分:0)

另一个有效的答案(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