C#如何正确P/Invoke调用libc库getpwnam函数获取用户UID/GID
错误原因
- 结构体定义不符合非托管封送要求:原生
passwd结构体中的字符串字段为char*指针,你直接定义为托管string类型会导致运行时把非托管指针当作托管对象访问,触发内存错误。同时未显式指定结构体布局,字段偏移可能和原生实现不匹配。 getpwnam的P/Invoke签名错误:原生getpwnam返回值为struct passwd*指针(指向glibc内部静态存储区),不是void,也不会把结果填充到你传入的out参数中,调用逻辑完全不符合接口约定。getpwnam_r的签名错误:原生getpwnam_r返回int类型错误码,第二参数是struct passwd*缓冲区指针,最后一个参数是struct passwd**结果指针,你当前的值类型传参方式完全错误。
正确实现
你只需要获取Uid和Gid的话可以用如下简化实现,不需要处理字符串字段的封送转换:
using System; using System.Runtime.InteropServices; if (args.Length < 1) { Console.Error.WriteLine("Provide user name."); Environment.Exit(-1); } var name = args[0]; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { var pwdPtr = Syscall.getpwnam(name); if (pwdPtr == IntPtr.Zero) { Console.WriteLine($"User {name} not found."); return; } var passwd = Marshal.PtrToStructure<Passwd>(pwdPtr); Console.WriteLine($"User = {name}, UID = {passwd.Uid}, GID = {passwd.Gid}"); } else { Console.WriteLine("It supposed to be run on Linux."); } [StructLayout(LayoutKind.Sequential)] public struct Passwd { // 前两个字段为char*指针,占位保证Uid/Gid偏移和原生结构体匹配,不需要处理内容 public IntPtr Name; public IntPtr Password; public uint Uid; public uint Gid; // 后续字段无需读取可省略 } static class Syscall { [DllImport("libc", SetLastError = true)] public static extern IntPtr getpwnam(string name); }
如果需要多线程安全的实现,可改用getpwnam_r版本,避免静态缓冲区的重入问题。
内容的提问来源于stack exchange,提问作者Harry
相关产品推荐
相关产品推荐

