在C#/.NET中将用户名转换为SID字符串

46

有一个问题涉及到从SID转换为帐户名,但没有相反的情况。

如何将用户名转换为SID字符串,例如找出与给定名称的用户相关联的HKEY_USERS子键?

4个回答

93

这个播客告诉我当SO上没有回答时,应该提问并回答问题。接下来就是我的提问。

.NET 2.0及以上版本的简单方法如下:

NTAccount f = new NTAccount("username");
SecurityIdentifier s = (SecurityIdentifier) f.Translate(typeof(SecurityIdentifier));
String sidString = s.ToString();

以下是一种较为麻烦但能在某些情况下运行且适用于.NET 1.1的方法:

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool LookupAccountName([In,MarshalAs(UnmanagedType.LPTStr)] string systemName, [In,MarshalAs(UnmanagedType.LPTStr)] string accountName, IntPtr sid, ref int cbSid, StringBuilder referencedDomainName, ref int cbReferencedDomainName, out int use);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
internal static extern bool ConvertSidToStringSid(IntPtr sid, [In,Out,MarshalAs(UnmanagedType.LPTStr)] ref string pStringSid);


/// <summary>The method converts object name (user, group) into SID string.</summary>
/// <param name="name">Object name in form domain\object_name.</param>
/// <returns>SID string.</returns>
public static string GetSid(string name) {
    IntPtr _sid = IntPtr.Zero; //pointer to binary form of SID string.
    int _sidLength = 0;   //size of SID buffer.
    int _domainLength = 0;  //size of domain name buffer.
    int _use;     //type of object.
    StringBuilder _domain = new StringBuilder(); //stringBuilder for domain name.
    int _error = 0;
    string _sidString = "";

    //first call of the function only returns the sizes of buffers (SDI, domain name)
    LookupAccountName(null, name, _sid, ref _sidLength, _domain, ref _domainLength, out _use);
    _error = Marshal.GetLastWin32Error();

    if (_error != 122) //error 122 (The data area passed to a system call is too small) - normal behaviour.
    {
        throw (new Exception(new Win32Exception(_error).Message));
    } else {
        _domain = new StringBuilder(_domainLength); //allocates memory for domain name
        _sid = Marshal.AllocHGlobal(_sidLength); //allocates memory for SID
        bool _rc = LookupAccountName(null, name, _sid, ref _sidLength, _domain, ref _domainLength, out _use);

        if (_rc == false) {
            _error = Marshal.GetLastWin32Error();
            Marshal.FreeHGlobal(_sid);
            throw (new Exception(new Win32Exception(_error).Message));
        } else {
            // converts binary SID into string
            _rc = ConvertSidToStringSid(_sid, ref _sidString);

            if (_rc == false) {
                _error = Marshal.GetLastWin32Error();
                Marshal.FreeHGlobal(_sid);
                throw (new Exception(new Win32Exception(_error).Message));
            } else {
                Marshal.FreeHGlobal(_sid);
                return _sidString;
            }
        }
    }
}

2
我很好奇为什么我选择“f”作为NTAccount的变量! - crb
2
“当简单方法行不通时,使用这个方法”...假设我有.NET 2.0,你有什么指导? - Rory
不好意思,我不记得了。我可能只是指早期的2.0版本;我认为它归结于相同的Win32 API调用。 - crb
我的一个客户报告了一个异常:“无法翻译一些或所有身份引用”,当我使用第一种方法时。我还没有尝试第二种方法,但我在这里留下我的评论,让任何人都知道。 - joe
我得到了 System.Security.Principal.IdentityNotMappedException - Yola

1
< p> LookupAccountName() 本地方法的优点在于可以在远程计算机上执行,而 .NET 方法无法在远程计算机上执行。

虽然示例没有显示,但 LookupAccountName(null) <- 这是要在远程系统上执行的命令。


1
using System.Security.Principal;

var curUser = WindowsIdentity.GetCurrent().User.Value;
var otherUser = new WindowsIdentity("kul@mycompany.com").User.Value;

3
虽然你的代码可能自我解释,但仅提供代码转储仍然是个不好的想法。没有任何解释和仅有代码的答案会被标记为低质量,并可能被删除。 - Nawed Nabi Zada
@NawedNabiZada 这是一个程序员向程序员提出的问题。我们不是作家。我自己也在寻找这个问题的答案,最终找到了并决定分享。在我的例子中,变量名和函数名可以清楚地表达它们的含义! - KUL
5
信不信由你,我只是评论让你了解 Stack Overflow 的规则。 - Nawed Nabi Zada

0
using System;
using System.Management;
using System.Windows.Forms;

namespace WMISample
{
public class MyWMIQuery
{
    public static void Main()
    {
        try
        {
            ManagementObjectSearcher searcher = 
                new ManagementObjectSearcher("root\\CIMV2", 
                "SELECT * FROM Win32_UserAccount where name='Galia'"); 

            foreach (ManagementObject queryObj in searcher.Get())
            {
                Console.WriteLine("-----------------------------------");
                Console.WriteLine("Win32_UserAccount instance");
                Console.WriteLine("-----------------------------------");
                Console.WriteLine("Name: {0}", queryObj["Name"]);
                Console.WriteLine("SID: {0}", queryObj["SID"]);
            }
        }
        catch (ManagementException e)
        {
            MessageBox.Show("An error occurred while querying for WMI 
            data: " + e.Message);
        }
      }
    }
}

/////////远程:

            ConnectionOptions connection = new ConnectionOptions();
            connection.Username = userNameBox.Text;
            connection.Password = passwordBox.Text;
            connection.Authority = "ntlmdomain:WORKGROUP";

            ManagementScope scope = new ManagementScope(
                "\\\\ASUS\\root\\CIMV2", connection);
            scope.Connect();

            ObjectQuery query= new ObjectQuery(
                "SELECT * FROM Win32_UserAccount"); 

            ManagementObjectSearcher searcher = 
                new ManagementObjectSearcher(scope, query);

            foreach (ManagementObject queryObj in searcher.Get())
            {
                Console.WriteLine("-----------------------------------");
                Console.WriteLine("Win32_UserAccount instance");
                Console.WriteLine("-----------------------------------");
            }

这个似乎只适用于本地用户账户? - Cocowalla
对于远程计算机来说,它几乎是一样的。 - Danil
哦,我不知道 WMI 可以用于域相关的事情,谢谢! - Cocowalla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接