diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs index afacc85d34c863..77bd01b4bd3d4f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs @@ -163,11 +163,23 @@ private static unsafe void WriteValueSlow(object ptr, int ofs, T val, Action< } } + /// + /// Get the last platform invoke error on the current thread + /// + /// The last platform invoke error + /// + /// The last platform invoke error corresponds to the error set by either the most recent platform + /// invoke that was configured to set the last error or a call to . + /// [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int GetLastWin32Error(); + public static extern int GetLastPInvokeError(); + /// + /// Set the last platform invoke error on the current thread + /// + /// Error to set [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void SetLastWin32Error(int error); + public static extern void SetLastPInvokeError(int error); private static void PrelinkCore(MethodInfo m) { diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 9b84c7a8dd6b1b..8478635d24487d 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -742,8 +742,8 @@ FCFuncStart(gGCSettingsFuncs) FCFuncEnd() FCFuncStart(gInteropMarshalFuncs) - FCFuncElement("GetLastWin32Error", MarshalNative::GetLastWin32Error) - FCFuncElement("SetLastWin32Error", MarshalNative::SetLastWin32Error) + FCFuncElement("GetLastPInvokeError", MarshalNative::GetLastPInvokeError) + FCFuncElement("SetLastPInvokeError", MarshalNative::SetLastPInvokeError) FCFuncElement("SizeOfHelper", MarshalNative::SizeOfClass) FCFuncElement("StructureToPtr", MarshalNative::StructureToPtr) FCFuncElement("PtrToStructureHelper", MarshalNative::PtrToStructureHelper) diff --git a/src/coreclr/vm/marshalnative.cpp b/src/coreclr/vm/marshalnative.cpp index 3f247dca44d7c8..7100fe37d8b396 100644 --- a/src/coreclr/vm/marshalnative.cpp +++ b/src/coreclr/vm/marshalnative.cpp @@ -405,9 +405,9 @@ FCIMPL1(LPVOID, MarshalNative::GetFunctionPointerForDelegateInternal, Object* re FCIMPLEND /************************************************************************ - * PInvoke.GetLastWin32Error + * Marshal.GetLastPInvokeError */ -FCIMPL0(int, MarshalNative::GetLastWin32Error) +FCIMPL0(int, MarshalNative::GetLastPInvokeError) { FCALL_CONTRACT; @@ -415,11 +415,10 @@ FCIMPL0(int, MarshalNative::GetLastWin32Error) } FCIMPLEND - /************************************************************************ - * PInvoke.SetLastWin32Error + * Marshal.SetLastPInvokeError */ -FCIMPL1(void, MarshalNative::SetLastWin32Error, int error) +FCIMPL1(void, MarshalNative::SetLastPInvokeError, int error) { FCALL_CONTRACT; @@ -427,7 +426,6 @@ FCIMPL1(void, MarshalNative::SetLastWin32Error, int error) } FCIMPLEND - /************************************************************************ * Support for the GCHandle class. */ diff --git a/src/coreclr/vm/marshalnative.h b/src/coreclr/vm/marshalnative.h index fc92e26028a7fb..fea67622219c72 100644 --- a/src/coreclr/vm/marshalnative.h +++ b/src/coreclr/vm/marshalnative.h @@ -30,8 +30,8 @@ class MarshalNative static FCDECL2(UINT32, SizeOfClass, ReflectClassBaseObject* refClass, CLR_BOOL throwIfNotMarshalable); static FCDECL1(UINT32, OffsetOfHelper, ReflectFieldObject* pFieldUNSAFE); - static FCDECL0(int, GetLastWin32Error); - static FCDECL1(void, SetLastWin32Error, int error); + static FCDECL0(int, GetLastPInvokeError); + static FCDECL1(void, SetLastPInvokeError, int error); static FCDECL3(VOID, StructureToPtr, Object* pObjUNSAFE, LPVOID ptr, CLR_BOOL fDeleteOld); static FCDECL3(VOID, PtrToStructureHelper, LPVOID ptr, Object* pObjIn, CLR_BOOL allowValueClasses); diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ErrNo.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ErrNo.cs new file mode 100644 index 00000000000000..3e321d512635a4 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ErrNo.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal unsafe partial class Sys + { + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetErrNo")] + [SuppressGCTransition] + internal static extern int GetErrNo(); + + [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_SetErrNo")] + [SuppressGCTransition] + internal static extern void SetErrNo(int errorCode); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetLastError.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetLastError.cs new file mode 100644 index 00000000000000..1a4394800ce99b --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetLastError.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + [SuppressGCTransition] + internal static extern void SetLastError(int errorCode); + } +} diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index 53e30315ea0c19..a8be7305be7a93 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -239,6 +239,8 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_CreateAutoreleasePool) DllImportEntry(SystemNative_DrainAutoreleasePool) DllImportEntry(SystemNative_iOSSupportVersion) + DllImportEntry(SystemNative_GetErrNo) + DllImportEntry(SystemNative_SetErrNo) }; EXTERN_C const void* SystemResolveDllImport(const char* name); diff --git a/src/libraries/Native/Unix/System.Native/pal_errno.c b/src/libraries/Native/Unix/System.Native/pal_errno.c index 89b6e5bac6617f..4920a5442ec617 100644 --- a/src/libraries/Native/Unix/System.Native/pal_errno.c +++ b/src/libraries/Native/Unix/System.Native/pal_errno.c @@ -17,3 +17,13 @@ const char* SystemNative_StrErrorR(int32_t platformErrno, char* buffer, int32_t { return StrErrorR(platformErrno, buffer, bufferSize); } + +int32_t SystemNative_GetErrNo(void) +{ + return errno; +} + +void SystemNative_SetErrNo(int32_t errorCode) +{ + errno = errorCode; +} diff --git a/src/libraries/Native/Unix/System.Native/pal_errno.h b/src/libraries/Native/Unix/System.Native/pal_errno.h index d256052add6f28..8c5f9c9aa70d71 100644 --- a/src/libraries/Native/Unix/System.Native/pal_errno.h +++ b/src/libraries/Native/Unix/System.Native/pal_errno.h @@ -42,3 +42,13 @@ PALEXPORT int32_t SystemNative_ConvertErrorPalToPlatform(int32_t error); * as possible and null-terminated. */ PALEXPORT const char* SystemNative_StrErrorR(int32_t platformErrno, char* buffer, int32_t bufferSize); + +/** + * Gets the current errno value + */ +PALEXPORT int32_t SystemNative_GetErrNo(void); + +/** + * Sets the errno value + */ +PALEXPORT void SystemNative_SetErrNo(int32_t errorCode); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index a2bb38f63185c0..d406f36c32c487 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1387,6 +1387,9 @@ Common\Interop\Windows\Kernel32\Interop.GenericOperations.cs + + Common\Interop\Windows\Kernel32\Interop.GetLastError.cs + Common\Interop\Windows\Kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs @@ -1525,6 +1528,9 @@ Common\Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs + + Common\Interop\Windows\Kernel32\Interop.SetLastError.cs + Common\Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs @@ -1743,6 +1749,9 @@ Common\Interop\Unix\System.Native\Interop.Close.cs + + Common\Interop\Unix\System.Native\Interop.ErrNo.cs + Common\Interop\Unix\System.Native\Interop.FLock.cs @@ -2021,9 +2030,6 @@ - - Interop\Windows\Kernel32\Interop.GetLastError.cs - Interop\Windows\Kernel32\Interop.Threading.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs index bded46e242b6f8..0fe1dc698fd0a6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs @@ -146,11 +146,11 @@ private void Cleanup() // Save last error from P/Invoke in case the implementation of // ReleaseHandle trashes it (important because this ReleaseHandle could // occur implicitly as part of unmarshaling another P/Invoke). - int lastError = Marshal.GetLastWin32Error(); + int lastError = Marshal.GetLastPInvokeError(); ReleaseHandle(); - Marshal.SetLastWin32Error(lastError); + Marshal.SetLastPInvokeError(lastError); GC.SuppressFinalize(this); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs index f8af59eeae8651..08ab87a390fffc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs @@ -200,5 +200,29 @@ public static unsafe void FreeBSTR(IntPtr ptr) return null; } + + /// + /// Get the last system error on the current thread + /// + /// The last system error + /// + /// The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows) + /// + public static int GetLastSystemError() + { + return Interop.Sys.GetErrNo(); + } + + /// + /// Set the last system error on the current thread + /// + /// Error to set + /// + /// The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows) + /// + public static void SetLastSystemError(int error) + { + Interop.Sys.SetErrNo(error); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs index 6df77c5c6dbdb8..b7f4c11f7ccb42 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs @@ -233,5 +233,29 @@ public static void FreeBSTR(IntPtr ptr) return GetTypeFromCLSID(clsid, server, throwOnError); } + + /// + /// Get the last system error on the current thread + /// + /// The last system error + /// + /// The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows) + /// + public static int GetLastSystemError() + { + return Interop.Kernel32.GetLastError(); + } + + /// + /// Set the last system error on the current thread + /// + /// Error to set + /// + /// The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows) + /// + public static void SetLastSystemError(int error) + { + Interop.Kernel32.SetLastError(error); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 542f3ba41b19e2..70913553bd7b44 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -1266,5 +1266,10 @@ public static void InitHandle(SafeHandle safeHandle, IntPtr handle) // To help maximize performance of P/Invokes, don't check if safeHandle is null. safeHandle.SetHandle(handle); } + + public static int GetLastWin32Error() + { + return GetLastPInvokeError(); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs index 33c9dcb575ced8..6d1bbf831773a1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs @@ -243,9 +243,9 @@ private void InternalRelease(bool disposeOrFinalizeOperation) // Save last error from P/Invoke in case the implementation of ReleaseHandle // trashes it (important because this ReleaseHandle could occur implicitly // as part of unmarshaling another P/Invoke). - int lastError = Marshal.GetLastWin32Error(); + int lastError = Marshal.GetLastPInvokeError(); ReleaseHandle(); - Marshal.SetLastWin32Error(lastError); + Marshal.SetLastPInvokeError(lastError); } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 5fcb17353156e9..e116175fd3617e 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -570,6 +570,8 @@ public static void FreeHGlobal(System.IntPtr hglobal) { } public static System.IntPtr GetIDispatchForObject(object o) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static System.IntPtr GetIUnknownForObject(object o) { throw null; } + public static int GetLastPInvokeError() { throw null; } + public static int GetLastSystemError() { throw null; } public static int GetLastWin32Error() { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -662,6 +664,8 @@ public static void PtrToStructure(System.IntPtr ptr, [System.Diagnostics.Code public static System.IntPtr SecureStringToGlobalAllocUnicode(System.Security.SecureString s) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public static bool SetComObjectData(object obj, object key, object? data) { throw null; } + public static void SetLastPInvokeError(int error) { throw null; } + public static void SetLastSystemError(int error) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static int SizeOf(object structure) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj index e7d065da06ceae..79c9de91710d26 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj @@ -96,6 +96,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/LastErrorTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/LastErrorTests.cs new file mode 100644 index 00000000000000..e16110f1a07a42 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/LastErrorTests.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Runtime.InteropServices.Tests +{ + public class LastErrorTests + { + [Fact] + public void LastPInvokeError_RoundTrip() + { + int errorExpected = 123; + Marshal.SetLastPInvokeError(errorExpected); + Assert.Equal(errorExpected, Marshal.GetLastPInvokeError()); + } + + [Fact] + public void LastSystemError_RoundTrip() + { + int errorExpected = 123; + Marshal.SetLastSystemError(errorExpected); + Assert.Equal(errorExpected, Marshal.GetLastSystemError()); + } + + [Fact] + public void SetLastPInvokeError_GetLastWin32Error() + { + int errorExpected = 123; + Marshal.SetLastPInvokeError(errorExpected); + Assert.Equal(errorExpected, Marshal.GetLastWin32Error()); + } + + [Fact] + public void SetLastSystemError_PInvokeErrorUnchanged() + { + int pinvokeError = 123; + Marshal.SetLastPInvokeError(pinvokeError); + + int systemError = pinvokeError + 1; + Marshal.SetLastSystemError(systemError); + + // Setting last system error should not affect the last P/Invoke error + int pinvokeActual = Marshal.GetLastPInvokeError(); + Assert.NotEqual(systemError, pinvokeActual); + Assert.Equal(pinvokeError, pinvokeActual); + + int win32Actual = Marshal.GetLastWin32Error(); + Assert.NotEqual(systemError, win32Actual); + Assert.Equal(pinvokeError, win32Actual); + } + } +} diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Mono.cs index 68bb8fa466a381..6e712350cb5adb 100644 --- a/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Mono.cs @@ -11,8 +11,23 @@ namespace System.Runtime.InteropServices { public partial class Marshal { + /// + /// Get the last platform invoke error on the current thread + /// + /// The last platform invoke error + /// + /// The last platform invoke error corresponds to the error set by either the most recent platform + /// invoke that was configured to set the last error or a call to . + /// [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern int GetLastWin32Error(); + public static extern int GetLastPInvokeError(); + + /// + /// Set the last platform invoke error on the current thread + /// + /// Error to set + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public static extern void SetLastPInvokeError(int error); [MethodImplAttribute(MethodImplOptions.InternalCall)] public static extern void DestroyStructure(IntPtr ptr, Type structuretype); @@ -35,9 +50,6 @@ internal static bool IsPinnable(object? obj) //return !type.IsValueType || RuntimeTypeHandle.HasReferences (type as RuntimeType); } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern void SetLastWin32Error(int error); - private static void PrelinkCore(MethodInfo m) { if (!(m is RuntimeMethodInfo)) diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index d1b44dc81c4e0c..c51e5e680d1f23 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -346,12 +346,12 @@ ICALL_TYPE(MARSHAL, "System.Runtime.InteropServices.Marshal", MARSHAL_4) HANDLES(MARSHAL_4, "DestroyStructure", ves_icall_System_Runtime_InteropServices_Marshal_DestroyStructure, void, 2, (gpointer, MonoReflectionType)) HANDLES(MARSHAL_9, "GetDelegateForFunctionPointerInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointerInternal, MonoDelegate, 2, (gpointer, MonoReflectionType)) HANDLES(MARSHAL_10, "GetFunctionPointerForDelegateInternal", ves_icall_System_Runtime_InteropServices_Marshal_GetFunctionPointerForDelegateInternal, gpointer, 1, (MonoDelegate)) -NOHANDLES(ICALL(MARSHAL_11, "GetLastWin32Error", ves_icall_System_Runtime_InteropServices_Marshal_GetLastWin32Error)) +NOHANDLES(ICALL(MARSHAL_11, "GetLastPInvokeError", ves_icall_System_Runtime_InteropServices_Marshal_GetLastPInvokeError)) HANDLES(MARSHAL_48a, "IsPinnableType", ves_icall_System_Runtime_InteropServices_Marshal_IsPinnableType, MonoBoolean, 1, (MonoReflectionType)) HANDLES(MARSHAL_12, "OffsetOf", ves_icall_System_Runtime_InteropServices_Marshal_OffsetOf, int, 2, (MonoReflectionType, MonoString)) HANDLES(MARSHAL_13, "PrelinkInternal", ves_icall_System_Runtime_InteropServices_Marshal_Prelink, void, 1, (MonoReflectionMethod)) HANDLES(MARSHAL_20, "PtrToStructureInternal", ves_icall_System_Runtime_InteropServices_Marshal_PtrToStructureInternal, void, 3, (gconstpointer, MonoObject, MonoBoolean)) -NOHANDLES(ICALL(MARSHAL_29a, "SetLastWin32Error", ves_icall_System_Runtime_InteropServices_Marshal_SetLastWin32Error)) +NOHANDLES(ICALL(MARSHAL_29a, "SetLastPInvokeError", ves_icall_System_Runtime_InteropServices_Marshal_SetLastPInvokeError)) HANDLES(MARSHAL_30, "SizeOf", ves_icall_System_Runtime_InteropServices_Marshal_SizeOf, guint32, 1, (MonoReflectionType)) HANDLES(MARSHAL_31, "SizeOfHelper", ves_icall_System_Runtime_InteropServices_Marshal_SizeOfHelper, guint32, 2, (MonoReflectionType, MonoBoolean)) HANDLES(MARSHAL_34, "StructureToPtr", ves_icall_System_Runtime_InteropServices_Marshal_StructureToPtr, void, 3, (MonoObject, gpointer, MonoBoolean)) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 47d25a99f469dc..06d58ea7d9f87c 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -4785,19 +4785,19 @@ mono_marshal_clear_last_error (void) #endif } -guint32 -ves_icall_System_Runtime_InteropServices_Marshal_GetLastWin32Error (void) +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_GetLastPInvokeError (void) { return GPOINTER_TO_INT (mono_native_tls_get_value (last_error_tls_id)); } void -ves_icall_System_Runtime_InteropServices_Marshal_SetLastWin32Error (guint32 err) +ves_icall_System_Runtime_InteropServices_Marshal_SetLastPInvokeError (guint32 err) { mono_native_tls_set_value (last_error_tls_id, GINT_TO_POINTER (err)); } -guint32 +guint32 ves_icall_System_Runtime_InteropServices_Marshal_SizeOf (MonoReflectionTypeHandle rtype, MonoError *error) { if (MONO_HANDLE_IS_NULL (rtype)) { diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index ea3408748be47d..ee94b8e6bfa182 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -611,12 +611,12 @@ gpointer mono_marshal_lookup_pinvoke (MonoMethod *method); ICALL_EXPORT -guint32 -ves_icall_System_Runtime_InteropServices_Marshal_GetLastWin32Error (void); +guint32 +ves_icall_System_Runtime_InteropServices_Marshal_GetLastPInvokeError (void); ICALL_EXPORT void -ves_icall_System_Runtime_InteropServices_Marshal_SetLastWin32Error (guint32 err); +ves_icall_System_Runtime_InteropServices_Marshal_SetLastPInvokeError (guint32 err); ICALL_EXPORT mono_bstr diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index a39791f73c21f5..8622b4f4da4b26 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(PInvoke/Delegate) add_subdirectory(PInvoke/Primitives/Int) add_subdirectory(PInvoke/Primitives/RuntimeHandles) add_subdirectory(PInvoke/Primitives/Pointer) +add_subdirectory(PInvoke/SetLastError) add_subdirectory(PInvoke/SizeParamIndex/PInvoke/PassingByOut) add_subdirectory(PInvoke/SizeParamIndex/PInvoke/PassingByRef) add_subdirectory(PInvoke/SizeParamIndex/ReversePInvoke/PassingByOut) diff --git a/src/tests/Interop/PInvoke/SetLastError/CMakeLists.txt b/src/tests/Interop/PInvoke/SetLastError/CMakeLists.txt new file mode 100644 index 00000000000000..4a0cae2e12ca0e --- /dev/null +++ b/src/tests/Interop/PInvoke/SetLastError/CMakeLists.txt @@ -0,0 +1,10 @@ +project(SetLastErrorNative) +include("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") +set(SOURCES SetLastErrorNative.cpp) + +# add the shared library +add_library(SetLastErrorNative SHARED ${SOURCES}) +target_link_libraries(SetLastErrorNative ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install(TARGETS SetLastErrorNative DESTINATION bin) diff --git a/src/tests/Interop/PInvoke/SetLastError/SetLastErrorNative.cpp b/src/tests/Interop/PInvoke/SetLastError/SetLastErrorNative.cpp new file mode 100644 index 00000000000000..e85413102a79bb --- /dev/null +++ b/src/tests/Interop/PInvoke/SetLastError/SetLastErrorNative.cpp @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#ifndef WINDOWS +#include +#endif + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE SetError(int err, bool shouldSetError) +{ + if (!shouldSetError) + return; + +#ifdef WINDOWS + ::SetLastError(err); +#else + errno = err; +#endif +} diff --git a/src/tests/Interop/PInvoke/SetLastError/SetLastErrorTest.cs b/src/tests/Interop/PInvoke/SetLastError/SetLastErrorTest.cs new file mode 100644 index 00000000000000..d607ebd191b7d8 --- /dev/null +++ b/src/tests/Interop/PInvoke/SetLastError/SetLastErrorTest.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using TestLibrary; + +class SetLastErrorTest +{ + private static class SetLastErrorNative + { + private const string SetError = nameof(SetError); + + [DllImport(nameof(SetLastErrorNative), EntryPoint = SetError)] + public static extern void SetError_Default(int error, [MarshalAs(UnmanagedType.U1)] bool shouldSetError); + + [DllImport(nameof(SetLastErrorNative), EntryPoint = SetError, SetLastError = false)] + public static extern void SetError_False(int error, [MarshalAs(UnmanagedType.U1)] bool shouldSetError); + + [DllImport(nameof(SetLastErrorNative), EntryPoint = SetError, SetLastError = true)] + public static extern void SetError_True(int error, [MarshalAs(UnmanagedType.U1)] bool shouldSetError); + } + + public static void LastErrorHasExpectedValue() + { + // Default (same behaviour as SetLastError=false) + { + int expected = Marshal.GetLastPInvokeError(); + SetLastErrorNative.SetError_Default(expected + 1, shouldSetError: true); + int actual = Marshal.GetLastPInvokeError(); + Assert.AreEqual(expected, actual); + } + + // SetLastError=false + { + int expected = Marshal.GetLastPInvokeError(); + SetLastErrorNative.SetError_False(expected + 1, shouldSetError: true); + int actual = Marshal.GetLastPInvokeError(); + Assert.AreEqual(expected, actual); + } + + // SetLastError=true + { + int expected = Marshal.GetLastPInvokeError(); + expected++; + SetLastErrorNative.SetError_True(expected, shouldSetError: true); + int actual = Marshal.GetLastPInvokeError(); + Assert.AreEqual(expected, actual); + } + } + + public static void ClearPreviousError() + { + // Set the last P/Invoke error to non-zero + int error = 100; + Marshal.SetLastPInvokeError(error); + + // Don't actually set the error in the native call. + // Calling a P/Invoke with SetLastError=true should clear any existing error. + SetLastErrorNative.SetError_True(error, shouldSetError: false); + int actual = Marshal.GetLastPInvokeError(); + Assert.AreEqual(0, actual); + } + + static int Main(string[] args) + { + try + { + LastErrorHasExpectedValue(); + ClearPreviousError(); + } + catch (Exception e) + { + Console.WriteLine($"Test Failure: {e}"); + return 101; + } + + return 100; + } +} diff --git a/src/tests/Interop/PInvoke/SetLastError/SetLastErrorTest.csproj b/src/tests/Interop/PInvoke/SetLastError/SetLastErrorTest.csproj new file mode 100644 index 00000000000000..38fdf93e5a0cef --- /dev/null +++ b/src/tests/Interop/PInvoke/SetLastError/SetLastErrorTest.csproj @@ -0,0 +1,12 @@ + + + Exe + + + + + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index b575a39cc9caea..0050dc3e563bc2 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1296,6 +1296,9 @@ https://github.com/dotnet/runtime/issues/48084 + + https://github.com/dotnet/runtime/issues/51600 + needs triage