Skip to content

Commit

Permalink
Add remarks to last error Marshal APIs (#7334)
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung committed Nov 5, 2021
1 parent 8a20dff commit 07b2c6a
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//<snippet1>
using System;
using System.Runtime.InteropServices;

// These functions specify SetLastError=true to propagate the last error from the p/invoke
// such that it can be retrieved using Marshal.GetLastPInvokeError().
internal static class Kernel32
{
[DllImport(nameof(Kernel32), ExactSpelling = true, SetLastError = true)]
internal static extern bool SetCurrentDirectoryW([MarshalAs(UnmanagedType.LPWStr)] string path);
}

internal static class libc
{
[DllImport(nameof(libc), SetLastError = true)]
internal static extern int chdir([MarshalAs(UnmanagedType.LPUTF8Str)] string path);
}

class Program
{
public static void Main(string[] args)
{
// Call p/invoke with valid arguments.
CallPInvoke(AppContext.BaseDirectory);

// Call p/invoke with invalid arguments.
CallPInvoke(string.Empty);
}

private static void CallPInvoke(string path)
{
if (OperatingSystem.IsWindows())
{
Console.WriteLine($"Calling SetCurrentDirectoryW with path '{path}'");
Kernel32.SetCurrentDirectoryW(path);
}
else
{
Console.WriteLine($"Calling chdir with path '{path}'");
libc.chdir(path);
}

// Get the last p/invoke error and display it.
int error = Marshal.GetLastPInvokeError();
Console.WriteLine($"Last p/invoke error: {error}");
}
}
//</snippet1>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

</Project>
14 changes: 10 additions & 4 deletions xml/System.Runtime.InteropServices/DllImportAttribute.xml
Original file line number Diff line number Diff line change
Expand Up @@ -557,16 +557,21 @@
<ReturnType>System.Boolean</ReturnType>
</ReturnValue>
<Docs>
<summary>Indicates whether the callee calls the <see langword="SetLastError" /> Windows API function before returning from the attributed method.</summary>
<summary>Indicates whether the callee sets an error (<see langword="SetLastError" /> on Windows or <see langword="errno" /> on other platforms) before returning from the attributed method.</summary>
<remarks>
<format type="text/markdown"><![CDATA[
## Remarks
`true` to indicate that the callee will call `SetLastError`; otherwise, `false`. The default is `false`.
The runtime marshaler calls `GetLastError` and caches the value returned to prevent it from being overwritten by other API calls. You can retrieve the error code by calling <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>.
`true` to indicate that the callee will set an error via `SetLastError` on Windows or `errno` on other platforms; otherwise, `false`. The default is `false`.
If this field is set to `true`, the runtime marshaler calls `GetLastError` or `errno` and caches the value returned to prevent it from being overwritten by other API calls.
You can retrieve the error code by calling <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A> on .NET 6.0 and above
or <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A> on .NET 5.0 and below or .NET Framework.
On .NET, the error information is cleared (set to `0`) before invoking the callee when this field is set to `true`. On .NET Framework, the error information is not cleared.
This means that error information returned by <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A> and <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>
on .NET represents only the error information from the last p/invoke with <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError?displayProperty=nameWithType>
set to `true`. On .NET Framework, the error information can persist from one p/invoke to the next.
## Examples
In some cases, Visual Basic developers use the <xref:System.Runtime.InteropServices.DllImportAttribute>, instead of using the `Declare` statement, to define a DLL function in managed code. Setting the <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError> field is one of those cases.
Expand All @@ -577,6 +582,7 @@
]]></format>
</remarks>
<altmember cref="M:System.Runtime.InteropServices.Marshal.GetLastPInvokeError" />
<altmember cref="M:System.Runtime.InteropServices.Marshal.GetLastWin32Error" />
</Docs>
</Member>
Expand Down
55 changes: 42 additions & 13 deletions xml/System.Runtime.InteropServices/Marshal.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4362,15 +4362,31 @@
</ReturnValue>
<Parameters />
<Docs>
<summary>Get the last platform invoke error on the current thread</summary>
<returns>The last platform invoke error</returns>
<summary>Get the last platform invoke error on the current thread.</summary>
<returns>The last platform invoke error.</returns>
<remarks>
<format type="text/markdown"><![CDATA[

## 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 <xref:System.Runtime.InteropServices.Marshal.SetLastPInvokeError(System.Int32)>.
The last platform invoke error corresponds to the error set either by the most recent platform invoke that was
configured with <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError?displayProperty=nameWithType>
set to `true` or by a call to <xref:System.Runtime.InteropServices.Marshal.SetLastPInvokeError(System.Int32)>,
whichever happened last.

This method will only return errors set via the mentioned scenarios. To get the last system error independent of
platform invoke usage, use <xref:System.Runtime.InteropServices.Marshal.GetLastSystemError%2A>.

This method is functionally equivalent to <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>. It is
named to better reflect the intent of the API and its cross-platform nature. <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A>
should be preferred over <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>.

## Examples

The following example defines a p/invoke with <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError?displayProperty=nameWithType>
set to `true` and demonstrates using <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A> to get the last p/invoke error.

:::code language="csharp" source="~/samples/snippets/csharp/System.Runtime.InteropServices/Marshal/GetLastPInvokeError.cs" id="Snippet1":::

]]></format>
</remarks>
Expand Down Expand Up @@ -4399,14 +4415,17 @@ The last platform invoke error corresponds to the error set by either the most r
</ReturnValue>
<Parameters />
<Docs>
<summary>Get the last system error on the current thread</summary>
<returns>The last system error</returns>
<summary>Gets the last system error on the current thread.</summary>
<returns>The last system error.</returns>
<remarks>
<format type="text/markdown"><![CDATA[

## Remarks

The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows)
The system error is based on the current operating system&mdash;that is, `errno` on non-Windows and [GetLastError](/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) on Windows.

This method is provided as a low-level API to allow getting the current system error in a cross-platform manner. It is not tied to
platform invoke usage. To get the last platform invoke error, use <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A>.

]]></format>
</remarks>
Expand Down Expand Up @@ -4473,6 +4492,8 @@ You can use this method to obtain error codes only if you apply the <xref:System

There is a difference in the behavior of the `GetLastWin32Error` method on .NET Core and .NET Framework when <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError?displayProperty=nameWithType> is `true`. On .NET Framework, the `GetLastWin32Error` method can retain error information from one P/Invoke call to the next. On .NET Core, error information is cleared before P/Invoke call, and the `GetLastWin32Error` represents only error information from the last method call.

On .NET 6 and later versions, this method is functionally equivalent to <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A>, which is named to better reflect the intent of the API and its cross-platform nature. <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A> should be preferred over <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>.

## Examples
The following example calls the `GetLastWin32Error` method. The example first demonstrates calling the method with no error present and then demonstrates calling the method with an error present.

Expand Down Expand Up @@ -9692,9 +9713,17 @@ There is a difference in the behavior of the `GetLastWin32Error` method on .NET
<Parameter Name="error" Type="System.Int32" Index="0" FrameworkAlternate="net-6.0" />
</Parameters>
<Docs>
<param name="error">Error to set</param>
<summary>Set the last platform invoke error on the current thread</summary>
<remarks>To be added.</remarks>
<param name="error">The error to set.</param>
<summary>Sets the last platform invoke error on the current thread.</summary>
<remarks>
<format type="text/markdown"><![CDATA[

## Remarks

The last platform invoke error is stored per-thread and can be retrieved using <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A>.

]]></format>
</remarks>
</Docs>
</Member>
<Member MemberName="SetLastSystemError">
Expand Down Expand Up @@ -9722,14 +9751,14 @@ There is a difference in the behavior of the `GetLastWin32Error` method on .NET
<Parameter Name="error" Type="System.Int32" Index="0" FrameworkAlternate="net-6.0" />
</Parameters>
<Docs>
<param name="error">Error to set</param>
<summary>Set the last system error on the current thread</summary>
<param name="error">The error to set.</param>
<summary>Sets the last system error on the current thread.</summary>
<remarks>
<format type="text/markdown"><![CDATA[

## Remarks

The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows)
The system error is based on the current operating system&mdash;that is, `errno` on Unix and [SetLastError](/windows/win32/api/errhandlingapi/nf-errhandlingapi-setlasterror) on Windows.

]]></format>
</remarks>
Expand Down

0 comments on commit 07b2c6a

Please sign in to comment.