Skip to content

Commit

Permalink
Add Get/SetLastPInvokeError and Get/SetLastSystemError APIs (#51505)
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung authored Apr 22, 2021
1 parent 6ca3d91 commit 7ee226d
Show file tree
Hide file tree
Showing 28 changed files with 352 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,23 @@ private static unsafe void WriteValueSlow<T>(object ptr, int ofs, T val, Action<
}
}

/// <summary>
/// Get the last platform invoke error on the current thread
/// </summary>
/// <returns>The last platform invoke error</returns>
/// <remarks>
/// 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 <see cref="SetLastPInvokeError(int)" />.
/// </remarks>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern int GetLastWin32Error();
public static extern int GetLastPInvokeError();

/// <summary>
/// Set the last platform invoke error on the current thread
/// </summary>
/// <param name="error">Error to set</param>
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetLastWin32Error(int error);
public static extern void SetLastPInvokeError(int error);

private static void PrelinkCore(MethodInfo m)
{
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 4 additions & 6 deletions src/coreclr/vm/marshalnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,29 +405,27 @@ FCIMPL1(LPVOID, MarshalNative::GetFunctionPointerForDelegateInternal, Object* re
FCIMPLEND

/************************************************************************
* PInvoke.GetLastWin32Error
* Marshal.GetLastPInvokeError
*/
FCIMPL0(int, MarshalNative::GetLastWin32Error)
FCIMPL0(int, MarshalNative::GetLastPInvokeError)
{
FCALL_CONTRACT;

return (UINT32)(GetThread()->m_dwLastError);
}
FCIMPLEND


/************************************************************************
* PInvoke.SetLastWin32Error
* Marshal.SetLastPInvokeError
*/
FCIMPL1(void, MarshalNative::SetLastWin32Error, int error)
FCIMPL1(void, MarshalNative::SetLastPInvokeError, int error)
{
FCALL_CONTRACT;

GetThread()->m_dwLastError = (DWORD)error;
}
FCIMPLEND


/************************************************************************
* Support for the GCHandle class.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/marshalnative.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
2 changes: 2 additions & 0 deletions src/libraries/Native/Unix/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
10 changes: 10 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,9 @@
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GenericOperations.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.GenericOperations.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetLastError.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.GetLastError.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.GET_FILEEX_INFO_LEVELS.cs</Link>
</Compile>
Expand Down Expand Up @@ -1521,6 +1524,9 @@
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetLastError.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.SetLastError.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs</Link>
</Compile>
Expand Down Expand Up @@ -1740,6 +1746,9 @@
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Close.cs">
<Link>Common\Interop\Unix\System.Native\Interop.Close.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ErrNo.cs">
<Link>Common\Interop\Unix\System.Native\Interop.ErrNo.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.FLock.cs">
<Link>Common\Interop\Unix\System.Native\Interop.FLock.cs</Link>
</Compile>
Expand Down Expand Up @@ -2020,9 +2029,6 @@
<ItemGroup Condition="'$(FeatureCoreCLR)' != 'true' and '$(TargetsWindows)' == 'true'">
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Thread.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\WaitHandle.Windows.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.GetLastError.cs">
<Link>Interop\Windows\Kernel32\Interop.GetLastError.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.Threading.cs">
<Link>Interop\Windows\Kernel32\Interop.Threading.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,29 @@ public static unsafe void FreeBSTR(IntPtr ptr)

return null;
}

/// <summary>
/// Get the last system error on the current thread
/// </summary>
/// <returns>The last system error</returns>
/// <remarks>
/// The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows)
/// </remarks>
public static int GetLastSystemError()
{
return Interop.Sys.GetErrNo();
}

/// <summary>
/// Set the last system error on the current thread
/// </summary>
/// <param name="error">Error to set</param>
/// <remarks>
/// The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows)
/// </remarks>
public static void SetLastSystemError(int error)
{
Interop.Sys.SetErrNo(error);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,5 +233,29 @@ public static void FreeBSTR(IntPtr ptr)

return GetTypeFromCLSID(clsid, server, throwOnError);
}

/// <summary>
/// Get the last system error on the current thread
/// </summary>
/// <returns>The last system error</returns>
/// <remarks>
/// The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows)
/// </remarks>
public static int GetLastSystemError()
{
return Interop.Kernel32.GetLastError();
}

/// <summary>
/// Set the last system error on the current thread
/// </summary>
/// <param name="error">Error to set</param>
/// <remarks>
/// The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows)
/// </remarks>
public static void SetLastSystemError(int error)
{
Interop.Kernel32.SetLastError(error);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1279,5 +1279,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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -662,6 +664,8 @@ public static void PtrToStructure<T>(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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
<Compile Include="System\Runtime\InteropServices\Marshal\InitHandleTests.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal\IsComObjectTests.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal\IsComObjectTests.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\Marshal\LastErrorTests.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal\ReleaseTests.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal\ReleaseComObjectTests.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal\ReleaseComObjectTests.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading

0 comments on commit 7ee226d

Please sign in to comment.