.NET是否有获取用户有效文件夹权限的API?Windows API能否通过PInvoke调用?
在.NET中获取用户有效文件夹权限的Windows API(PInvoke方式)
你说得对,用DirectorySecurity.GetAccessRules()确实得自己手动梳理用户自身权限、所属组权限还有继承来的权限,合并起来特别麻烦。其实Windows原生API里有专门的函数可以直接帮你算出用户对某个文件夹的有效权限,不用自己折腾权限合并逻辑,咱们可以通过PInvoke来调用这些API。
核心Windows API介绍
最常用的两个关键函数是:
AccessCheck:这是核心函数,它会自动处理组权限、继承权限、拒绝权限的优先级等所有逻辑,直接返回用户对目标对象的最终有效权限集。GetNamedSecurityInfo:用来获取目标文件夹的安全描述符(包含DACL,也就是自主访问控制列表),作为AccessCheck的输入参数。
PInvoke代码示例
下面是一个完整的.NET代码示例,演示如何调用这些API来获取当前用户对指定文件夹的有效权限:
首先,定义必要的PInvoke结构体和函数声明:
using System; using System.Runtime.InteropServices; using System.Security.Principal; public static class EffectivePermissionsHelper { // 权限常量,对应FileSystemRights private const uint FILE_READ_DATA = 0x0001; private const uint FILE_WRITE_DATA = 0x0002; private const uint FILE_APPEND_DATA = 0x0004; private const uint FILE_READ_EA = 0x0008; private const uint FILE_WRITE_EA = 0x0010; private const uint FILE_EXECUTE = 0x0020; private const uint FILE_DELETE_CHILD = 0x0040; private const uint FILE_READ_ATTRIBUTES = 0x0080; private const uint FILE_WRITE_ATTRIBUTES = 0x0100; private const uint DELETE = 0x00010000; private const uint READ_CONTROL = 0x00020000; private const uint WRITE_DAC = 0x00040000; private const uint WRITE_OWNER = 0x00080000; private const uint SYNCHRONIZE = 0x00100000; [StructLayout(LayoutKind.Sequential)] private struct SID_AND_ATTRIBUTES { public IntPtr Sid; public uint Attributes; } [StructLayout(LayoutKind.Sequential)] private struct TOKEN_USER { public SID_AND_ATTRIBUTES User; } [DllImport("advapi32.dll", SetLastError = true)] private static extern bool GetNamedSecurityInfo( string pObjectName, int ObjectType, uint SecurityInfo, out IntPtr ppsidOwner, out IntPtr ppsidGroup, out IntPtr ppDacl, out IntPtr ppSacl, out IntPtr ppSecurityDescriptor); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool OpenProcessToken( IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool GetTokenInformation( IntPtr TokenHandle, int TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool AccessCheck( IntPtr pSecurityDescriptor, IntPtr ClientToken, uint DesiredAccess, IntPtr PrivilegeSet, out uint PrivilegeSetLength, out uint GrantedAccess, out uint AccessStatus, IntPtr AuditInfo); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll")] private static extern bool IsValidSid(IntPtr pSid); [DllImport("kernel32.dll")] private static extern void LocalFree(IntPtr hMem); private const int SE_FILE_OBJECT = 1; private const uint DACL_SECURITY_INFORMATION = 0x00000004; private const int TokenUser = 1; private const uint TOKEN_QUERY = 0x0008;
然后,实现获取有效权限的方法:
public static System.Security.AccessControl.FileSystemRights GetEffectiveFolderPermissions(string folderPath) { // 1. 获取当前进程的访问令牌 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, out IntPtr tokenHandle)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); try { // 2. 获取令牌中的用户SID uint bufferSize = 0; GetTokenInformation(tokenHandle, TokenUser, IntPtr.Zero, 0, out bufferSize); IntPtr tokenInfoPtr = Marshal.AllocHGlobal((int)bufferSize); try { if (!GetTokenInformation(tokenHandle, TokenUser, tokenInfoPtr, bufferSize, out bufferSize)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); TOKEN_USER tokenUser = Marshal.PtrToStructure<TOKEN_USER>(tokenInfoPtr); if (!IsValidSid(tokenUser.User.Sid)) throw new InvalidOperationException("Invalid user SID."); // 3. 获取文件夹的安全描述符(仅DACL) if (!GetNamedSecurityInfo(folderPath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, out _, out _, out IntPtr dacl, out _, out IntPtr securityDescriptor)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); try { // 4. 定义要检查的所有文件系统权限 uint desiredAccess = FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER | SYNCHRONIZE; // 5. 调用AccessCheck计算有效权限 uint grantedAccess = 0; uint accessStatus = 0; if (!AccessCheck(securityDescriptor, tokenHandle, desiredAccess, IntPtr.Zero, out _, out grantedAccess, out accessStatus, IntPtr.Zero)) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); // 6. 将原生权限转换为.NET的FileSystemRights枚举 return (System.Security.AccessControl.FileSystemRights)grantedAccess; } finally { LocalFree(securityDescriptor); } } finally { Marshal.FreeHGlobal(tokenInfoPtr); } } finally { if (tokenHandle != IntPtr.Zero) Marshal.Close(tokenHandle); } } }
使用示例
你可以这样调用这个方法:
string folderPath = @"C:\YourFolder"; var effectiveRights = EffectivePermissionsHelper.GetEffectiveFolderPermissions(folderPath); Console.WriteLine($"有效权限:{effectiveRights}");
注意事项
- 权限要求:你的程序需要有足够的权限读取目标文件夹的安全信息,否则API调用会失败。
- 权限映射:示例中已经把Windows原生的权限常量和.NET的
FileSystemRights做了对应,你可以根据需求调整要检查的权限集合。 - 指定用户:如果要获取非当前用户的有效权限,你需要先获取该用户的访问令牌(可以用
LogonUserAPI),再传入AccessCheck。
内容的提问来源于stack exchange,提问作者SullenMan
相关产品推荐
相关产品推荐

