从C#访问C全局变量'errno'

时间:2010-03-21 02:30:17

标签: c# c mono pinvoke errno

P / Invoking时是否可以在C#中访问“errno”变量?这类似于Win32 GetLastError()。

4 个答案:

答案 0 :(得分:8)

我很确定有办法,但这可能是一个坏主意。您如何保证运行时在其内部处理期间没有调用某些CRT函数,这会影响errno

出于同样的原因,您也不应直接致电GetLastErrorDllImportAttribute提供SetLastError属性,因此运行时知道立即捕获上一个错误并将其存储在托管代码可以使用Marshal.GetLastWin32Error读取的位置。

我认为在这种情况下你能做的最强大的事情是制作一个C DLL,它既可以执行实际的C工作,也可以执行errno的捕获。 (请注意,只是在errno捕获周围编写一个包装器仍然会有上述问题。)

答案 1 :(得分:2)

是的,有可能 - GetLastError正是如此。但是,正如二进制编码器指出的那样,您不应该直接执行此操作 - 而是在SetLastError上设置DllImport以自动执行和缓存(并避免多线程问题或运行时调用的函数修改{ {1}} value) - 然后,在调用P / Invoked函数时,检查它的返回状态,如果它显示错误条件 - 抛出errno,它会自动读取上一个错误的值。是的,即使是Linux上的Mono。

答案 2 :(得分:2)

解决方案是在SetLastError上使用DllImport。这将使运行时保存最后一个错误,以便可以从Marshal.GetLastWin32Error访问它。

直接调用GetLastError有两个问题:

  • 运行时可能会在PInvoke返回之后的某个时间执行,然后才能获得最后一个错误
  • 多个.NET线程可以驻留在同一本机线程上。这可能导致2个.NET线程执行PInvokes,本机库不知道更好,然后会覆盖最后一个错误。因此,.NET中的线程A获取线程B的最后一个错误(可能)。

答案 3 :(得分:0)

是的,有可能。
一开始,errno似乎很神奇。
但是所有的魔法都是基于欺骗的,错误号也是如此。
errno不是变量,它是一个预处理器定义!

来自FreeBSD man-page

extern int* __error();
#define errno (* __error())

查看我的KDE-Neon Linux上的资源:

extern int *__errno_location (void) __THROW __attribute_const__;
# define errno (*__errno_location ())

(/ usr / include / errno.h)

因此您可以像这样在C#中获取它:
(警告,我认为它在Linux上也可以使用,并且我还没有测试我是否正确处理了指针)

namespace MonoReplacement
{


    class MonoSux
    {

        private const string LIBC = "libc";


        // [System.CLSCompliant(false)]
        [System.Flags]
        public enum AccessModes
            :int 
        {
            R_OK = 1,
            W_OK = 2,
            X_OK = 4,
            F_OK = 8
        }

        public enum Errno
            :int 
        {
            EPERM = 1,
            ENOENT = 2,
            ESRCH = 3,
            EINTR = 4,
            EIO = 5,
            ENXIO = 6,
            E2BIG = 7,
            ENOEXEC = 8,
            EBADF = 9,
            ECHILD = 10,
            EAGAIN = 11,
            EWOULDBLOCK = 11,
            ENOMEM = 12,
            EACCES = 13,
            EFAULT = 14,
            ENOTBLK = 15,
            EBUSY = 16,
            EEXIST = 17,
            EXDEV = 18,
            ENODEV = 19,
            ENOTDIR = 20,
            EISDIR = 21,
            EINVAL = 22,
            ENFILE = 23,
            EMFILE = 24,
            ENOTTY = 25,
            ETXTBSY = 26,
            EFBIG = 27,
            ENOSPC = 28,
            ESPIPE = 29,
            EROFS = 30,
            EMLINK = 31,
            EPIPE = 32,
            EDOM = 33,
            ERANGE = 34,
            EDEADLK = 35,
            EDEADLOCK = 35,
            ENAMETOOLONG = 36,
            ENOLCK = 37,
            ENOSYS = 38,
            ENOTEMPTY = 39,
            ELOOP = 40,
            ENOMSG = 42,
            EIDRM = 43,
            ECHRNG = 44,
            EL2NSYNC = 45,
            EL3HLT = 46,
            EL3RST = 47,
            ELNRNG = 48,
            EUNATCH = 49,
            ENOCSI = 50,
            EL2HLT = 51,
            EBADE = 52,
            EBADR = 53,
            EXFULL = 54,
            ENOANO = 55,
            EBADRQC = 56,
            EBADSLT = 57,
            EBFONT = 59,
            ENOSTR = 60,
            ENODATA = 61,
            ETIME = 62,
            ENOSR = 63,
            ENONET = 64,
            ENOPKG = 65,
            EREMOTE = 66,
            ENOLINK = 67,
            EADV = 68,
            ESRMNT = 69,
            ECOMM = 70,
            EPROTO = 71,
            EMULTIHOP = 72,
            EDOTDOT = 73,
            EBADMSG = 74,
            EOVERFLOW = 75,
            ENOTUNIQ = 76,
            EBADFD = 77,
            EREMCHG = 78,
            ELIBACC = 79,
            ELIBBAD = 80,
            ELIBSCN = 81,
            ELIBMAX = 82,
            ELIBEXEC = 83,
            EILSEQ = 84,
            ERESTART = 85,
            ESTRPIPE = 86,
            EUSERS = 87,
            ENOTSOCK = 88,
            EDESTADDRREQ = 89,
            EMSGSIZE = 90,
            EPROTOTYPE = 91,
            ENOPROTOOPT = 92,
            EPROTONOSUPPORT = 93,
            ESOCKTNOSUPPORT = 94,
            EOPNOTSUPP = 95,
            EPFNOSUPPORT = 96,
            EAFNOSUPPORT = 97,
            EADDRINUSE = 98,
            EADDRNOTAVAIL = 99,
            ENETDOWN = 100,
            ENETUNREACH = 101,
            ENETRESET = 102,
            ECONNABORTED = 103,
            ECONNRESET = 104,
            ENOBUFS = 105,
            EISCONN = 106,
            ENOTCONN = 107,
            ESHUTDOWN = 108,
            ETOOMANYREFS = 109,
            ETIMEDOUT = 110,
            ECONNREFUSED = 111,
            EHOSTDOWN = 112,
            EHOSTUNREACH = 113,
            EALREADY = 114,
            EINPROGRESS = 115,
            ESTALE = 116,
            EUCLEAN = 117,
            ENOTNAM = 118,
            ENAVAIL = 119,
            EISNAM = 120,
            EREMOTEIO = 121,
            EDQUOT = 122,
            ENOMEDIUM = 123,
            EMEDIUMTYPE = 124,
            ECANCELED = 125,
            ENOKEY = 126,
            EKEYEXPIRED = 127,
            EKEYREVOKED = 128,
            EKEYREJECTED = 129,
            EOWNERDEAD = 130,
            ENOTRECOVERABLE = 131,
            EPROCLIM = 1067,
            EBADRPC = 1072,
            ERPCMISMATCH = 1073,
            EPROGUNAVAIL = 1074,
            EPROGMISMATCH = 1075,
            EPROCUNAVAIL = 1076,
            EFTYPE = 1079,
            EAUTH = 1080,
            ENEEDAUTH = 1081,
            EPWROFF = 1082,
            EDEVERR = 1083,
            EBADEXEC = 1085,
            EBADARCH = 1086,
            ESHLIBVERS = 1087,
            EBADMACHO = 1088,
            ENOATTR = 1093,
            ENOPOLICY = 1103
        }


        // int access(const char *pathname, int mode);
        // https://linux.die.net/man/2/access
        [System.Security.SuppressUnmanagedCodeSecurity]
        [System.Runtime.InteropServices.DllImport(LIBC, CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "access", SetLastError=true)]
#if USE_LPUTF8Str
        internal static extern int access([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPUTF8Str)] string path, AccessModes mode);
#else
        internal static extern int access([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(libACL.Unix.FileNameMarshaler))] string path, AccessModes mode);
#endif


        // char *strerror(int errnum);
        // https://man7.org/linux/man-pages/man3/strerror.3.html
        [System.Security.SuppressUnmanagedCodeSecurity]
        [System.Runtime.InteropServices.DllImport(LIBC, CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "strerror")]
        internal static extern string strerror(Errno errnum);


        [System.Security.SuppressUnmanagedCodeSecurity]
        [System.Runtime.InteropServices.DllImport(LIBC, EntryPoint = "__errno_location", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
        internal static extern System.IntPtr __errno_location();
        




        /// <summary>
        /// access() checks whether the calling process can access the file pathname. If pathname is a symbolic link, it is dereferenced.
        /// </summary>
        /// <param name="pathmame"></param>
        /// <param name="mode"></param>
        /// <returns>On success (all requested permissions granted), zero is returned. On error (at least one bit in mode asked for a permission that is denied, or some other error occurred), -1 is returned, and errno is set appropriately.</returns>
        public static bool Access(string pathmame, AccessModes mode)
        {
            int ret = access(pathmame, mode);

            if (ret == -1)
            {
                // return null;
                System.Console.Error.WriteLine("Error on Access");

                // https://github.com/mono/mono/blob/master/mcs/class/Mono.Posix/Mono.Unix.Native/Stdlib.cs
                // https://stackoverflow.com/questions/2485648/access-c-global-variable-errno-from-c-sharp
                // Errno errno = error();
                System.IntPtr errorptr = __errno_location();
                Errno errno = (Errno)System.Runtime.InteropServices.Marshal.ReadInt32(errorptr);

                string message = strerror(errno);

                // throw ACLManagerException(Glib::locale_to_utf8(strerror(errno)));
                throw new System.InvalidOperationException(message);
            } // End if (ret == -1) 

            return ret == 0;
        } // End Function Access 


    }


}

注意:
正如Jason Kresowaty提到的那样,以这种方式获取错误不是一个好主意,因为CLR可能会同时调用函数。
但是根据developers.redhat.com,您也可以在Linux上使用Marshal.GetLastWin32Error。

示例:

[DllImport("libc", SetLastError = true))]
public static extern int kill(int pid, int sig);

SetLastError表示该函数使用errno指示发生了什么 错误。 Marshal.GetLastWin32Error可用于检索errno。

这也是Tmds.LibC所做的(此处,属性errorno代替了不可思议的预处理器定义):

using System.Runtime.InteropServices;

namespace Tmds.Linux
{
    public static partial class LibC
    {
        public static unsafe int errno
            // use the value captured by DllImport
            => Marshal.GetLastWin32Error();
    }
}

因此,为了模拟C#中的这种行为,我们改用一个属性:

public static Errno errno
{
    get
    {
        // How would you guarantee that the runtime has not called some CRT function 
        // during its internal processing that has affected the errno?

        // For the same reason, you should not call GetLastError directly either. 
        // The DllImportAttribute provides a SetLastError property so the runtime knows 
        // to immediately capture the last error and store it in a place that the managed code 
        // can read using Marshal.GetLastWin32Error.

        // this work on Linux !
        // Marshal.GetLastWin32Error can be used to retrieve errno.
        return (Errno)System.Runtime.InteropServices.Marshal.GetLastWin32Error();
    }
}

string message = strerror(errorno);