Skip to content

Commit 7ee226d

Browse files
authored
Add Get/SetLastPInvokeError and Get/SetLastSystemError APIs (#51505)
1 parent 6ca3d91 commit 7ee226d

File tree

28 files changed

+352
-32
lines changed

28 files changed

+352
-32
lines changed

src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,23 @@ private static unsafe void WriteValueSlow<T>(object ptr, int ofs, T val, Action<
163163
}
164164
}
165165

166+
/// <summary>
167+
/// Get the last platform invoke error on the current thread
168+
/// </summary>
169+
/// <returns>The last platform invoke error</returns>
170+
/// <remarks>
171+
/// The last platform invoke error corresponds to the error set by either the most recent platform
172+
/// invoke that was configured to set the last error or a call to <see cref="SetLastPInvokeError(int)" />.
173+
/// </remarks>
166174
[MethodImpl(MethodImplOptions.InternalCall)]
167-
public static extern int GetLastWin32Error();
175+
public static extern int GetLastPInvokeError();
168176

177+
/// <summary>
178+
/// Set the last platform invoke error on the current thread
179+
/// </summary>
180+
/// <param name="error">Error to set</param>
169181
[MethodImpl(MethodImplOptions.InternalCall)]
170-
internal static extern void SetLastWin32Error(int error);
182+
public static extern void SetLastPInvokeError(int error);
171183

172184
private static void PrelinkCore(MethodInfo m)
173185
{

src/coreclr/vm/ecalllist.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,8 +742,8 @@ FCFuncStart(gGCSettingsFuncs)
742742
FCFuncEnd()
743743

744744
FCFuncStart(gInteropMarshalFuncs)
745-
FCFuncElement("GetLastWin32Error", MarshalNative::GetLastWin32Error)
746-
FCFuncElement("SetLastWin32Error", MarshalNative::SetLastWin32Error)
745+
FCFuncElement("GetLastPInvokeError", MarshalNative::GetLastPInvokeError)
746+
FCFuncElement("SetLastPInvokeError", MarshalNative::SetLastPInvokeError)
747747
FCFuncElement("SizeOfHelper", MarshalNative::SizeOfClass)
748748
FCFuncElement("StructureToPtr", MarshalNative::StructureToPtr)
749749
FCFuncElement("PtrToStructureHelper", MarshalNative::PtrToStructureHelper)

src/coreclr/vm/marshalnative.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -405,29 +405,27 @@ FCIMPL1(LPVOID, MarshalNative::GetFunctionPointerForDelegateInternal, Object* re
405405
FCIMPLEND
406406

407407
/************************************************************************
408-
* PInvoke.GetLastWin32Error
408+
* Marshal.GetLastPInvokeError
409409
*/
410-
FCIMPL0(int, MarshalNative::GetLastWin32Error)
410+
FCIMPL0(int, MarshalNative::GetLastPInvokeError)
411411
{
412412
FCALL_CONTRACT;
413413

414414
return (UINT32)(GetThread()->m_dwLastError);
415415
}
416416
FCIMPLEND
417417

418-
419418
/************************************************************************
420-
* PInvoke.SetLastWin32Error
419+
* Marshal.SetLastPInvokeError
421420
*/
422-
FCIMPL1(void, MarshalNative::SetLastWin32Error, int error)
421+
FCIMPL1(void, MarshalNative::SetLastPInvokeError, int error)
423422
{
424423
FCALL_CONTRACT;
425424

426425
GetThread()->m_dwLastError = (DWORD)error;
427426
}
428427
FCIMPLEND
429428

430-
431429
/************************************************************************
432430
* Support for the GCHandle class.
433431
*/

src/coreclr/vm/marshalnative.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ class MarshalNative
3030
static FCDECL2(UINT32, SizeOfClass, ReflectClassBaseObject* refClass, CLR_BOOL throwIfNotMarshalable);
3131

3232
static FCDECL1(UINT32, OffsetOfHelper, ReflectFieldObject* pFieldUNSAFE);
33-
static FCDECL0(int, GetLastWin32Error);
34-
static FCDECL1(void, SetLastWin32Error, int error);
33+
static FCDECL0(int, GetLastPInvokeError);
34+
static FCDECL1(void, SetLastPInvokeError, int error);
3535

3636
static FCDECL3(VOID, StructureToPtr, Object* pObjUNSAFE, LPVOID ptr, CLR_BOOL fDeleteOld);
3737
static FCDECL3(VOID, PtrToStructureHelper, LPVOID ptr, Object* pObjIn, CLR_BOOL allowValueClasses);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
internal static partial class Interop
8+
{
9+
internal unsafe partial class Sys
10+
{
11+
[DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetErrNo")]
12+
[SuppressGCTransition]
13+
internal static extern int GetErrNo();
14+
15+
[DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_SetErrNo")]
16+
[SuppressGCTransition]
17+
internal static extern void SetErrNo(int errorCode);
18+
}
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.InteropServices;
5+
6+
internal partial class Interop
7+
{
8+
internal static partial class Kernel32
9+
{
10+
[DllImport(Libraries.Kernel32)]
11+
[SuppressGCTransition]
12+
internal static extern void SetLastError(int errorCode);
13+
}
14+
}

src/libraries/Native/Unix/System.Native/entrypoints.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ static const Entry s_sysNative[] =
239239
DllImportEntry(SystemNative_CreateAutoreleasePool)
240240
DllImportEntry(SystemNative_DrainAutoreleasePool)
241241
DllImportEntry(SystemNative_iOSSupportVersion)
242+
DllImportEntry(SystemNative_GetErrNo)
243+
DllImportEntry(SystemNative_SetErrNo)
242244
};
243245

244246
EXTERN_C const void* SystemResolveDllImport(const char* name);

src/libraries/Native/Unix/System.Native/pal_errno.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,13 @@ const char* SystemNative_StrErrorR(int32_t platformErrno, char* buffer, int32_t
1717
{
1818
return StrErrorR(platformErrno, buffer, bufferSize);
1919
}
20+
21+
int32_t SystemNative_GetErrNo(void)
22+
{
23+
return errno;
24+
}
25+
26+
void SystemNative_SetErrNo(int32_t errorCode)
27+
{
28+
errno = errorCode;
29+
}

src/libraries/Native/Unix/System.Native/pal_errno.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,13 @@ PALEXPORT int32_t SystemNative_ConvertErrorPalToPlatform(int32_t error);
4242
* as possible and null-terminated.
4343
*/
4444
PALEXPORT const char* SystemNative_StrErrorR(int32_t platformErrno, char* buffer, int32_t bufferSize);
45+
46+
/**
47+
* Gets the current errno value
48+
*/
49+
PALEXPORT int32_t SystemNative_GetErrNo(void);
50+
51+
/**
52+
* Sets the errno value
53+
*/
54+
PALEXPORT void SystemNative_SetErrNo(int32_t errorCode);

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,9 @@
13831383
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GenericOperations.cs">
13841384
<Link>Common\Interop\Windows\Kernel32\Interop.GenericOperations.cs</Link>
13851385
</Compile>
1386+
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetLastError.cs">
1387+
<Link>Common\Interop\Windows\Kernel32\Interop.GetLastError.cs</Link>
1388+
</Compile>
13861389
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs">
13871390
<Link>Common\Interop\Windows\Kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs</Link>
13881391
</Compile>
@@ -1521,6 +1524,9 @@
15211524
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs">
15221525
<Link>Common\Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs</Link>
15231526
</Compile>
1527+
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetLastError.cs">
1528+
<Link>Common\Interop\Windows\Kernel32\Interop.SetLastError.cs</Link>
1529+
</Compile>
15241530
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs">
15251531
<Link>Common\Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs</Link>
15261532
</Compile>
@@ -1740,6 +1746,9 @@
17401746
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Close.cs">
17411747
<Link>Common\Interop\Unix\System.Native\Interop.Close.cs</Link>
17421748
</Compile>
1749+
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ErrNo.cs">
1750+
<Link>Common\Interop\Unix\System.Native\Interop.ErrNo.cs</Link>
1751+
</Compile>
17431752
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.FLock.cs">
17441753
<Link>Common\Interop\Unix\System.Native\Interop.FLock.cs</Link>
17451754
</Compile>
@@ -2020,9 +2029,6 @@
20202029
<ItemGroup Condition="'$(FeatureCoreCLR)' != 'true' and '$(TargetsWindows)' == 'true'">
20212030
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Thread.Windows.cs" />
20222031
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\WaitHandle.Windows.cs" />
2023-
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GetLastError.cs">
2024-
<Link>Interop\Windows\Kernel32\Interop.GetLastError.cs</Link>
2025-
</Compile>
20262032
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.Threading.cs">
20272033
<Link>Interop\Windows\Kernel32\Interop.Threading.cs</Link>
20282034
</Compile>

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CriticalHandle.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,11 @@ private void Cleanup()
146146
// Save last error from P/Invoke in case the implementation of
147147
// ReleaseHandle trashes it (important because this ReleaseHandle could
148148
// occur implicitly as part of unmarshaling another P/Invoke).
149-
int lastError = Marshal.GetLastWin32Error();
149+
int lastError = Marshal.GetLastPInvokeError();
150150

151151
ReleaseHandle();
152152

153-
Marshal.SetLastWin32Error(lastError);
153+
Marshal.SetLastPInvokeError(lastError);
154154
GC.SuppressFinalize(this);
155155
}
156156

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,5 +200,29 @@ public static unsafe void FreeBSTR(IntPtr ptr)
200200

201201
return null;
202202
}
203+
204+
/// <summary>
205+
/// Get the last system error on the current thread
206+
/// </summary>
207+
/// <returns>The last system error</returns>
208+
/// <remarks>
209+
/// The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows)
210+
/// </remarks>
211+
public static int GetLastSystemError()
212+
{
213+
return Interop.Sys.GetErrNo();
214+
}
215+
216+
/// <summary>
217+
/// Set the last system error on the current thread
218+
/// </summary>
219+
/// <param name="error">Error to set</param>
220+
/// <remarks>
221+
/// The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows)
222+
/// </remarks>
223+
public static void SetLastSystemError(int error)
224+
{
225+
Interop.Sys.SetErrNo(error);
226+
}
203227
}
204228
}

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Windows.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,5 +233,29 @@ public static void FreeBSTR(IntPtr ptr)
233233

234234
return GetTypeFromCLSID(clsid, server, throwOnError);
235235
}
236+
237+
/// <summary>
238+
/// Get the last system error on the current thread
239+
/// </summary>
240+
/// <returns>The last system error</returns>
241+
/// <remarks>
242+
/// The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows)
243+
/// </remarks>
244+
public static int GetLastSystemError()
245+
{
246+
return Interop.Kernel32.GetLastError();
247+
}
248+
249+
/// <summary>
250+
/// Set the last system error on the current thread
251+
/// </summary>
252+
/// <param name="error">Error to set</param>
253+
/// <remarks>
254+
/// The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows)
255+
/// </remarks>
256+
public static void SetLastSystemError(int error)
257+
{
258+
Interop.Kernel32.SetLastError(error);
259+
}
236260
}
237261
}

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,5 +1279,10 @@ public static void InitHandle(SafeHandle safeHandle, IntPtr handle)
12791279
// To help maximize performance of P/Invokes, don't check if safeHandle is null.
12801280
safeHandle.SetHandle(handle);
12811281
}
1282+
1283+
public static int GetLastWin32Error()
1284+
{
1285+
return GetLastPInvokeError();
1286+
}
12821287
}
12831288
}

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ private void InternalRelease(bool disposeOrFinalizeOperation)
243243
// Save last error from P/Invoke in case the implementation of ReleaseHandle
244244
// trashes it (important because this ReleaseHandle could occur implicitly
245245
// as part of unmarshaling another P/Invoke).
246-
int lastError = Marshal.GetLastWin32Error();
246+
int lastError = Marshal.GetLastPInvokeError();
247247
ReleaseHandle();
248-
Marshal.SetLastWin32Error(lastError);
248+
Marshal.SetLastPInvokeError(lastError);
249249
}
250250
}
251251
}

src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ public static void FreeHGlobal(System.IntPtr hglobal) { }
570570
public static System.IntPtr GetIDispatchForObject(object o) { throw null; }
571571
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
572572
public static System.IntPtr GetIUnknownForObject(object o) { throw null; }
573+
public static int GetLastPInvokeError() { throw null; }
574+
public static int GetLastSystemError() { throw null; }
573575
public static int GetLastWin32Error() { throw null; }
574576
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
575577
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
@@ -662,6 +664,8 @@ public static void PtrToStructure<T>(System.IntPtr ptr, [System.Diagnostics.Code
662664
public static System.IntPtr SecureStringToGlobalAllocUnicode(System.Security.SecureString s) { throw null; }
663665
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
664666
public static bool SetComObjectData(object obj, object key, object? data) { throw null; }
667+
public static void SetLastPInvokeError(int error) { throw null; }
668+
public static void SetLastSystemError(int error) { throw null; }
665669
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
666670
public static int SizeOf(object structure) { throw null; }
667671
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]

src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
<Compile Include="System\Runtime\InteropServices\Marshal\InitHandleTests.cs" />
9797
<Compile Include="System\Runtime\InteropServices\Marshal\IsComObjectTests.cs" />
9898
<Compile Include="System\Runtime\InteropServices\Marshal\IsComObjectTests.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
99+
<Compile Include="System\Runtime\InteropServices\Marshal\LastErrorTests.cs" />
99100
<Compile Include="System\Runtime\InteropServices\Marshal\ReleaseTests.cs" />
100101
<Compile Include="System\Runtime\InteropServices\Marshal\ReleaseComObjectTests.cs" />
101102
<Compile Include="System\Runtime\InteropServices\Marshal\ReleaseComObjectTests.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Xunit;
5+
6+
namespace System.Runtime.InteropServices.Tests
7+
{
8+
public class LastErrorTests
9+
{
10+
[Fact]
11+
public void LastPInvokeError_RoundTrip()
12+
{
13+
int errorExpected = 123;
14+
Marshal.SetLastPInvokeError(errorExpected);
15+
Assert.Equal(errorExpected, Marshal.GetLastPInvokeError());
16+
}
17+
18+
[Fact]
19+
public void LastSystemError_RoundTrip()
20+
{
21+
int errorExpected = 123;
22+
Marshal.SetLastSystemError(errorExpected);
23+
Assert.Equal(errorExpected, Marshal.GetLastSystemError());
24+
}
25+
26+
[Fact]
27+
public void SetLastPInvokeError_GetLastWin32Error()
28+
{
29+
int errorExpected = 123;
30+
Marshal.SetLastPInvokeError(errorExpected);
31+
Assert.Equal(errorExpected, Marshal.GetLastWin32Error());
32+
}
33+
34+
[Fact]
35+
public void SetLastSystemError_PInvokeErrorUnchanged()
36+
{
37+
int pinvokeError = 123;
38+
Marshal.SetLastPInvokeError(pinvokeError);
39+
40+
int systemError = pinvokeError + 1;
41+
Marshal.SetLastSystemError(systemError);
42+
43+
// Setting last system error should not affect the last P/Invoke error
44+
int pinvokeActual = Marshal.GetLastPInvokeError();
45+
Assert.NotEqual(systemError, pinvokeActual);
46+
Assert.Equal(pinvokeError, pinvokeActual);
47+
48+
int win32Actual = Marshal.GetLastWin32Error();
49+
Assert.NotEqual(systemError, win32Actual);
50+
Assert.Equal(pinvokeError, win32Actual);
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)