测试(非当前)用户是否为管理员的更快方法

Faster method to test if (not current) user is an administrator

我需要测试哪些 Windows 用户拥有管理员权限。

好的,现在仔细阅读:不是当前用户。我查询所有本地用户帐户,然后测试其中一个具有管理员权限。假设我以 Joe 身份登录,我的应用程序在 Joe 用户的上下文中运行,但是这台 PC 上有一个用户 Timmy,他当前未登录。我需要测试 Timmy 在这台电脑上是否有管理员权限。所以,这个问题绝对不是关于当前用户权限的问题;)所以,这绝对不是关于确定当前用户权限的类似问题的重复。这个不一样 ;)

这是我的代码:

public static dynamic[] Users => WMI.Query("SELECT * FROM Win32_UserAccount WHERE Disabled = 0").Select<dynamic, dynamic>(d => {
    var machineContext = new PrincipalContext(ContextType.Machine);
    Principal principal = Principal.FindByIdentity(machineContext, d.SID);
    d.IsAdmin = principal.IsMemberOf(machineContext, IdentityType.Name, "Administrators");
    principal.Dispose();
    machineContext.Dispose();
    return d;
}).ToArray();

这行得通,但执行 IsMemberOf() 需要 超过 2 秒。 有没有更快的方法来做到这一点? 为什么这么慢?

如果您想知道 WMI.Query 在这里做了什么,它只是查询 WMI 和 returns 结果作为托管 dynamic 对象而不是 IDisposable 类型的数组。 IDisposable 类型在返回结果之前被释放。不过与问题无关。

为了澄清,我使用 System.DirectoryServices.AccountManagement 从 SID 获取实际用户帐户。我不知道 WindowsIdentity 是否可以从 SID 创建。 AFAIK 它不能。 WindowsIdentity 的用户需要登录(如果没有则抛出 SecurityException),我查询所有本地用户,而不仅仅是当前用户。

嗯,我找到了,但是还是很奇怪...

更新代码:(我将匹配组名称更改为匹配组 SID)。

public static dynamic[] Users => WMI.Query("SELECT * FROM Win32_UserAccount WHERE Disabled = 0").Select<dynamic, dynamic>(d => {
    using (var machineContext = new PrincipalContext(ContextType.Machine))
    using (Principal principal = Principal.FindByIdentity(machineContext, d.SID))
    d.IsAdmin = principal.GetGroups().Any(i => i.Sid.IsWellKnown(System.Security.Principal.WellKnownSidType.BuiltinAdministratorsSid));
    return d;
}).ToArray();

事实证明 GetGroups()IsMemberOf() 快得多。

更新: 实际上快了大约 135 倍。 GetGroups()Any() 花费了 17 毫秒而不是 2300 毫秒 IsMemberOf() 花费了。

作为奖励,我将与 WMI.Query 分享;)

/// <summary>
/// Safe, managed WMI queries support.
/// </summary>
static class WMI {

/// <summary>
/// Queries WMI and returns results as an array of dynamic objects.
/// </summary>
/// <param name="q"></param>
/// <returns></returns>
public static dynamic[] Query(string q) {
    using (var s = new ManagementObjectSearcher(q))
        return
            s
            .Get()
            .OfType<ManagementObject>()
            .Select(i => {
                var x = new ExpandoObject();
                using (i) foreach (var p in i.Properties) (x as IDictionary<string, object>).Add(p.Name, p.Value);
                return x;
            })
            .ToArray();
    }
}