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

从Object-C迁移到.NET7:如何在macOS用C#获取文件所有者信息?

解决.NET 7跨平台获取文件所有者和组的问题

问题原因

直接PInvoke调用Objective-C的NSFileManager无效,因为Objective-C的类和方法基于消息发送机制实现,并非C风格的导出函数,无法通过DllImport直接调用。而FileInfo.GetAccessControl仅支持Windows系统,无法在macOS/Linux上使用。

推荐解决方案:调用Unix标准C API

通过调用macOS/Linux系统标准C库(libc.dylib/libc.so)中的stat、getpwuid、getgrgid函数,获取文件的UID/GID后转换为对应的用户名和组名,这是跨平台且稳定的实现方式:

using System;
using System.Runtime.InteropServices;
using System.ComponentModel;

public static class UnixFileInfoHelper
{
    [StructLayout(LayoutKind.Sequential)]
    private struct stat
    {
        public long st_dev;
        public uint st_mode;
        public long st_nlink;
        public uint st_ino;
        public uint st_uid;
        public uint st_gid;
        public long st_rdev;
        public long st_size;
        public long st_atime;
        public long st_mtime;
        public long st_ctime;
        public long st_blksize;
        public long st_blocks;
    }

    [DllImport("libc", SetLastError = true)]
    private static extern int stat(string path, ref stat buf);

    [DllImport("libc")]
    private static extern IntPtr getpwuid(uint uid);

    [DllImport("libc")]
    private static extern IntPtr getgrgid(uint gid);

    [StructLayout(LayoutKind.Sequential)]
    private struct passwd
    {
        public IntPtr pw_name;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct group
    {
        public IntPtr gr_name;
    }

    public static (string Owner, string Group) GetFileOwnerAndGroup(string filePath)
    {
        var statBuffer = new stat();
        if (stat(filePath, ref statBuffer) != 0)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        // 解析用户名
        var pwPtr = getpwuid(statBuffer.st_uid);
        if (pwPtr == IntPtr.Zero)
        {
            throw new InvalidOperationException($"无法解析UID {statBuffer.st_uid} 对应的用户名");
        }
        var passwdData = Marshal.PtrToStructure<passwd>(pwPtr);
        string owner = Marshal.PtrToStringAnsi(passwdData.pw_name) ?? statBuffer.st_uid.ToString();

        // 解析组名
        var grPtr = getgrgid(statBuffer.st_gid);
        if (grPtr == IntPtr.Zero)
        {
            throw new InvalidOperationException($"无法解析GID {statBuffer.st_gid} 对应的组名");
        }
        var groupData = Marshal.PtrToStructure<group>(grPtr);
        string group = Marshal.PtrToStringAnsi(groupData.gr_name) ?? statBuffer.st_gid.ToString();

        return (owner, group);
    }
}

使用示例

try
{
    var (owner, group) = UnixFileInfoHelper.GetFileOwnerAndGroup("/path/to/your/file");
    Console.WriteLine($"所有者:{owner},组:{group}");
}
catch (Exception ex)
{
    Console.WriteLine($"获取文件信息失败:{ex.Message}");
}

备选方案:调用Objective-C运行时API(不推荐)

如果一定要沿用Objective-C的逻辑,需要通过Objective-C运行时的objc_msgSend发送消息调用API,但需要手动管理内存,复杂度较高:

using System;
using System.Runtime.InteropServices;

public static class ObjCFileInfoHelper
{
    [DllImport("/usr/lib/libobjc.dylib")]
    private static extern IntPtr objc_getClass(string className);

    [DllImport("/usr/lib/libobjc.dylib")]
    private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector);

    [DllImport("/usr/lib/libobjc.dylib")]
    private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, IntPtr arg1, int arg2);

    [DllImport("/usr/lib/libobjc.dylib")]
    private static extern IntPtr objc_msgSend(IntPtr receiver, IntPtr selector, IntPtr arg);

    [DllImport("/usr/lib/libobjc.dylib")]
    private static extern IntPtr sel_registerName(string selectorName);

    [DllImport("/usr/lib/libobjc.dylib")]
    private static extern void objc_release(IntPtr obj);

    public static (string Owner, string Group) GetFileOwnerAndGroup(string filePath)
    {
        // 创建NSString路径
        IntPtr nsStringClass = objc_getClass("NSString");
        IntPtr stringWithUTF8Sel = sel_registerName("stringWithUTF8String:");
        IntPtr nsFilePath = objc_msgSend(nsStringClass, stringWithUTF8Sel, filePath);

        // 获取NSFileManager实例
        IntPtr fileManagerClass = objc_getClass("NSFileManager");
        IntPtr sharedInstanceSel = sel_registerName("sharedInstance");
        IntPtr fileManager = objc_msgSend(fileManagerClass, sharedInstanceSel);

        // 获取文件属性字典
        IntPtr getAttributesSel = sel_registerName("fileAttributesAtPath:traverseLink:");
        IntPtr attributesDict = objc_msgSend(fileManager, getAttributesSel, nsFilePath, 1);

        // 获取所有者名称
        IntPtr ownerKey = objc_msgSend(nsStringClass, stringWithUTF8Sel, "NSFileOwnerAccountName");
        IntPtr ownerNsString = objc_msgSend(attributesDict, sel_registerName("objectForKey:"), ownerKey);
        IntPtr ownerCStr = objc_msgSend(ownerNsString, sel_registerName("UTF8String"));
        string owner = Marshal.PtrToStringAnsi(ownerCStr);

        // 获取组名称
        IntPtr groupKey = objc_msgSend(nsStringClass, stringWithUTF8Sel, "NSFileGroupOwnerAccountName");
        IntPtr groupNsString = objc_msgSend(attributesDict, sel_registerName("objectForKey:"), groupKey);
        IntPtr groupCStr = objc_msgSend(groupNsString, sel_registerName("UTF8String"));
        string group = Marshal.PtrToStringAnsi(groupCStr);

        // 释放Objective-C对象,避免内存泄漏
        objc_release(nsFilePath);
        objc_release(ownerKey);
        objc_release(ownerNsString);
        objc_release(groupKey);
        objc_release(groupNsString);
        // 注意:NSFileManager的sharedInstance是单例,不需要release

        return (owner, group);
    }
}

注意事项

  • 该方案仅适用于macOS,无法在Linux上运行
  • 必须严格管理Objective-C对象的内存,否则会导致内存泄漏
  • 需要处理Objective-C方法调用可能返回的nil情况

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:25:01