Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Get/SetLastPInvokeError and Get/SetLastSystemError APIs #51505

Merged
merged 8 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
jkotas marked this conversation as resolved.
Show resolved Hide resolved

/// <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 @@ -1387,6 +1387,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 @@ -1525,6 +1528,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 @@ -1743,6 +1749,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 @@ -2021,9 +2030,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 @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If GetLastPInvokeError is the recommended API to use moving forward, should we change all of our netcoreapp usage of GetLastWin32Error to instead use GetLastPInvokeError?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think so. I didn't try to do it with this change for adding the new API just because it would touch so much, but the plan would be to do so for 6.0.

}
jkotas marked this conversation as resolved.
Show resolved Hide resolved
}
}
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