.NET Core 2.1中获取管理员组成员SID的兼容方案求助
在.NET Core 2.1中获取管理员组成员SID的解决方案
你遇到的这个问题确实挺头疼的——.NET Core 2.1里的DirectoryEntry.Invoke方法不支持IDispatch相关操作,所以调用WinNT组的Members方法会抛出System.PlatformNotSupportedException: IDispatch and IDispatchEx are not supported异常,而这个问题直到.NET Core 3.0才被官方修复。如果没办法升级框架版本,咱们可以用P/Invoke调用Windows原生API的方式来实现需求,这是最可靠的替代方案。
先看看你原来的实现代码:
private IList<byte[]> GetAdministratorsMembersSIDs() { IList<byte[]> adminMembers = new List<byte[]>(); SecurityIdentifier id = new SecurityIdentifier(administratorsSid); string name = id.Translate(typeof(NTAccount)).Value.Split('\\')[1]; using (DirectoryEntry adminGroupEntry = new DirectoryEntry(string.Format("WinNT://./{0},group", name))) { foreach (object member in (IEnumerable)adminGroupEntry.Invoke("Members")) { using (DirectoryEntry memberEntry = new DirectoryEntry(member)) { adminMembers.Add((byte[])memberEntry.InvokeGet("objectSid")); } } } return adminMembers; }
替代方案:使用Windows原生API实现
我们可以调用Windows的NetLocalGroupGetMembersAPI来获取本地管理员组的成员SID,完全绕过.NET Core 2.1的DirectoryEntry限制,具体代码实现如下:
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Security.Principal; public class LocalGroupHelper { // Windows API常量定义 private const int MAX_PREFERRED_LENGTH = -1; private const int NERR_Success = 0; private const int LOCALGROUP_MEMBERS_INFO_3 = 3; // 存储成员SID信息的结构体 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct LOCALGROUP_MEMBERS_INFO_3 { public IntPtr lgrmi3_sid; public int lgrmi3_sidusage; } // P/Invoke声明:获取本地组成员 [DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int NetLocalGroupGetMembers( string servername, string groupname, int level, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, IntPtr resumehandle); // P/Invoke声明:释放API分配的内存 [DllImport("netapi32.dll", SetLastError = true)] private static extern int NetApiBufferFree(IntPtr buffer); public static IList<byte[]> GetAdministratorsMembersSIDs() { var adminSids = new List<byte[]>(); // 管理员组默认名称,也可以保留你原来通过SID转换名称的逻辑 string adminGroupName = "Administrators"; IntPtr bufPtr = IntPtr.Zero; try { int entriesRead; int totalEntries; // 调用API获取管理员组成员 int result = NetLocalGroupGetMembers( null, // 本地机器 adminGroupName, LOCALGROUP_MEMBERS_INFO_3, out bufPtr, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, IntPtr.Zero); if (result != NERR_Success) { throw new System.ComponentModel.Win32Exception(result); } // 遍历所有成员,提取SID并转换为字节数组 int structSize = Marshal.SizeOf(typeof(LOCALGROUP_MEMBERS_INFO_3)); IntPtr currentPtr = bufPtr; for (int i = 0; i < entriesRead; i++) { var memberInfo = (LOCALGROUP_MEMBERS_INFO_3)Marshal.PtrToStructure(currentPtr, typeof(LOCALGROUP_MEMBERS_INFO_3)); var sid = new SecurityIdentifier(memberInfo.lgrmi3_sid); byte[] sidBytes = new byte[sid.BinaryLength]; sid.GetBinaryForm(sidBytes, 0); adminSids.Add(sidBytes); currentPtr += structSize; } } finally { // 必须释放API分配的内存,避免泄漏 if (bufPtr != IntPtr.Zero) { NetApiBufferFree(bufPtr); } } return adminSids; } }
代码说明
- 这个方案直接调用Windows原生API,完全避开了.NET Core 2.1中
DirectoryEntry的平台支持限制; - 使用
LOCALGROUP_MEMBERS_INFO_3级别获取成员的SID指针,再通过SecurityIdentifier类转换为字节数组,和你原来的返回格式完全一致; - 最后一定要调用
NetApiBufferFree释放API分配的内存,避免内存泄漏。
另外,如果你的环境允许升级到.NET Core 3.0及以上版本,那直接使用你原来的代码就可以正常运行了,因为官方已经修复了这个平台支持问题。
内容的提问来源于stack exchange,提问作者Marco Ferrari
相关产品推荐
相关产品推荐

