P / Invoking时是否可以在C#中访问“errno”变量?这类似于Win32 GetLastError()。
答案 0 :(得分:8)
我很确定有办法,但这可能是一个坏主意。您如何保证运行时在其内部处理期间没有调用某些CRT函数,这会影响errno
?
出于同样的原因,您也不应直接致电GetLastError
。 DllImportAttribute
提供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
有两个问题:
答案 3 :(得分:0)
是的,有可能。
一开始,errno似乎很神奇。
但是所有的魔法都是基于欺骗的,错误号也是如此。
errno不是变量,它是一个预处理器定义!
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);