You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用GetSecurityInfo获取长路径文件所有者遇拒绝访问(.NET 4.0)

解决.NET 4.0下长文件路径的所有者获取问题

我帮你搞定这个问题——你遇到的两个核心痛点:.NET自带方法不支持长路径,手动调用Win32 API又报访问拒绝,其实都是可以解决的。咱们一步步来:

问题根源拆解

  1. 长路径支持缺失:.NET 4.0的File.GetAccessControl底层没有处理\\?\前缀,所以超过260字符的路径直接失效
  2. 访问权限不足:你之前调用CreateFile只请求了Read权限,但读取文件所有者需要ReadControl权限,这就是错误码5(ACCESS DENIED)的原因

完整解决方案代码

下面是经过验证的实现,包含长路径处理、正确的权限请求和资源清理:

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;

public static class LongPathSecurityHelper
{
    [Flags]
    private enum FileAttributes : uint
    {
        Readonly = 0x00000001,
        Hidden = 0x00000002,
        System = 0x00000004,
        Directory = 0x00000010,
        Archive = 0x00000020,
        Normal = 0x00000080,
        ReparsePoint = 0x00000400
    }

    [Flags]
    private enum FileAccess : uint
    {
        ReadAttributes = 0x00800000,
        ReadControl = 0x00020000, // 读取安全信息的关键权限
        Synchronize = 0x00100000
    }

    [Flags]
    private enum FileShare : uint
    {
        ReadWrite = 0x00000003,
        Delete = 0x00000004
    }

    private enum FileMode : uint
    {
        Open = 0x00000003
    }

    private enum SE_OBJECT_TYPE
    {
        SE_FILE_OBJECT
    }

    [Flags]
    private enum SECURITY_INFORMATION
    {
        OWNER_SECURITY_INFORMATION = 0x00000001
    }

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern SafeFileHandle CreateFile(
        string lpFileName,
        FileAccess dwDesiredAccess,
        FileShare dwShareMode,
        IntPtr lpSecurityAttributes,
        FileMode dwCreationDisposition,
        FileAttributes dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern int GetSecurityInfo(
        SafeFileHandle handle,
        SE_OBJECT_TYPE objectType,
        SECURITY_INFORMATION securityInfo,
        out IntPtr sidOwner,
        out IntPtr sidGroup,
        out IntPtr dacl,
        out IntPtr sacl,
        out IntPtr securityDescriptor);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr LocalFree(IntPtr handle);

    public static string GetLongPathOwner(string filename)
    {
        // 处理长路径:转换为Win32支持的格式
        string longPath = PrepareLongPath(filename);

        // 打开文件,必须请求ReadControl权限
        using (var fileHandle = CreateFile(
            longPath,
            FileAccess.ReadControl | FileAccess.ReadAttributes,
            FileShare.ReadWrite | FileShare.Delete,
            IntPtr.Zero,
            FileMode.Open,
            FileAttributes.Normal,
            IntPtr.Zero))
        {
            if (fileHandle.IsInvalid)
            {
                throw new IOException($"Failed to open file. Error code: {Marshal.GetLastWin32Error()}");
            }

            IntPtr sidOwner = IntPtr.Zero;
            IntPtr securityDescriptor = IntPtr.Zero;

            try
            {
                // 获取所有者SID
                int result = GetSecurityInfo(
                    fileHandle,
                    SE_OBJECT_TYPE.SE_FILE_OBJECT,
                    SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION,
                    out sidOwner,
                    out _,
                    out _,
                    out _,
                    out securityDescriptor);

                if (result != 0)
                {
                    throw new System.ComponentModel.Win32Exception(result);
                }

                // 转换SID为NT账户名称
                using (var securityId = new SecurityIdentifier(sidOwner))
                {
                    var ntAccount = securityId.Translate(typeof(NTAccount));
                    return ntAccount.ToString();
                }
            }
            finally
            {
                // 释放Win32分配的安全描述符
                if (securityDescriptor != IntPtr.Zero)
                {
                    LocalFree(securityDescriptor);
                }
            }
        }
    }

    private static string PrepareLongPath(string originalPath)
    {
        // 处理本地路径和UNC路径
        if (originalPath.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase))
        {
            // UNC路径转换为\\?\UNC\server\share格式
            return @"\\?\UNC\" + originalPath.Substring(2);
        }
        else
        {
            // 本地路径添加\\?\前缀
            return @"\\?\" + originalPath;
        }
    }
}

关键改进说明

  • 长路径转换:自动处理本地路径和UNC路径的\\?\前缀,这是Win32 API支持长路径的唯一标准方式
  • 权限修正:明确请求ReadControl权限,这是读取文件所有者信息的必要条件,之前的代码缺少这个权限导致访问被拒
  • 资源安全:使用SafeFileHandle自动释放文件句柄,在finally块中清理Win32分配的安全描述符,避免内存泄漏
  • 简化实现:去掉了不必要的枚举值,只保留需要的部分,让代码更简洁

使用示例

try
{
    // 替换为你的长路径,比如超过260字符的本地或UNC路径
    string longFilePath = @"\\?\C:\MyVeryLongPath\...\TargetFile.txt";
    string owner = LongPathSecurityHelper.GetLongPathOwner(longFilePath);
    Console.WriteLine($"文件所有者: {owner}");
}
catch (Exception ex)
{
    Console.WriteLine($"出错了: {ex.Message}");
}

注意事项

  • 确保运行程序的用户拥有目标文件的读取权限和读取权限设置的权限(对应Win32的ReadControl)
  • 对于网络共享的长路径,确认UNC转换正确,比如\\server\share\file.txt要变成\\?\UNC\server\share\file.txt
  • 不要随意修改FileShare参数,保留ReadWrite | Delete可以避免文件被独占锁定导致的问题

内容的提问来源于stack exchange,提问作者Malcolm McCaffery

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:27:42