Skip to content

Commit 07b2c6a

Browse files
authored
Add remarks to last error Marshal APIs (#7334)
1 parent 8a20dff commit 07b2c6a

File tree

4 files changed

+108
-17
lines changed

4 files changed

+108
-17
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//<snippet1>
2+
using System;
3+
using System.Runtime.InteropServices;
4+
5+
// These functions specify SetLastError=true to propagate the last error from the p/invoke
6+
// such that it can be retrieved using Marshal.GetLastPInvokeError().
7+
internal static class Kernel32
8+
{
9+
[DllImport(nameof(Kernel32), ExactSpelling = true, SetLastError = true)]
10+
internal static extern bool SetCurrentDirectoryW([MarshalAs(UnmanagedType.LPWStr)] string path);
11+
}
12+
13+
internal static class libc
14+
{
15+
[DllImport(nameof(libc), SetLastError = true)]
16+
internal static extern int chdir([MarshalAs(UnmanagedType.LPUTF8Str)] string path);
17+
}
18+
19+
class Program
20+
{
21+
public static void Main(string[] args)
22+
{
23+
// Call p/invoke with valid arguments.
24+
CallPInvoke(AppContext.BaseDirectory);
25+
26+
// Call p/invoke with invalid arguments.
27+
CallPInvoke(string.Empty);
28+
}
29+
30+
private static void CallPInvoke(string path)
31+
{
32+
if (OperatingSystem.IsWindows())
33+
{
34+
Console.WriteLine($"Calling SetCurrentDirectoryW with path '{path}'");
35+
Kernel32.SetCurrentDirectoryW(path);
36+
}
37+
else
38+
{
39+
Console.WriteLine($"Calling chdir with path '{path}'");
40+
libc.chdir(path);
41+
}
42+
43+
// Get the last p/invoke error and display it.
44+
int error = Marshal.GetLastPInvokeError();
45+
Console.WriteLine($"Last p/invoke error: {error}");
46+
}
47+
}
48+
//</snippet1>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
</Project>

xml/System.Runtime.InteropServices/DllImportAttribute.xml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -557,16 +557,21 @@
557557
<ReturnType>System.Boolean</ReturnType>
558558
</ReturnValue>
559559
<Docs>
560-
<summary>Indicates whether the callee calls the <see langword="SetLastError" /> Windows API function before returning from the attributed method.</summary>
560+
<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>
561561
<remarks>
562562
<format type="text/markdown"><![CDATA[
563563
564564
## Remarks
565-
`true` to indicate that the callee will call `SetLastError`; otherwise, `false`. The default is `false`.
566-
567-
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>.
565+
`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`.
568566
567+
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.
568+
You can retrieve the error code by calling <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A> on .NET 6.0 and above
569+
or <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A> on .NET 5.0 and below or .NET Framework.
569570
571+
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.
572+
This means that error information returned by <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A> and <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>
573+
on .NET represents only the error information from the last p/invoke with <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError?displayProperty=nameWithType>
574+
set to `true`. On .NET Framework, the error information can persist from one p/invoke to the next.
570575
571576
## Examples
572577
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.
@@ -577,6 +582,7 @@
577582
578583
]]></format>
579584
</remarks>
585+
<altmember cref="M:System.Runtime.InteropServices.Marshal.GetLastPInvokeError" />
580586
<altmember cref="M:System.Runtime.InteropServices.Marshal.GetLastWin32Error" />
581587
</Docs>
582588
</Member>

xml/System.Runtime.InteropServices/Marshal.xml

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4362,15 +4362,31 @@
43624362
</ReturnValue>
43634363
<Parameters />
43644364
<Docs>
4365-
<summary>Get the last platform invoke error on the current thread</summary>
4366-
<returns>The last platform invoke error</returns>
4365+
<summary>Get the last platform invoke error on the current thread.</summary>
4366+
<returns>The last platform invoke error.</returns>
43674367
<remarks>
43684368
<format type="text/markdown"><![CDATA[
43694369

43704370
## Remarks
43714371

4372-
The last platform invoke error corresponds to the error set by either the most recent platform
4373-
invoke that was configured to set the last error or a call to <xref:System.Runtime.InteropServices.Marshal.SetLastPInvokeError(System.Int32)>.
4372+
The last platform invoke error corresponds to the error set either by the most recent platform invoke that was
4373+
configured with <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError?displayProperty=nameWithType>
4374+
set to `true` or by a call to <xref:System.Runtime.InteropServices.Marshal.SetLastPInvokeError(System.Int32)>,
4375+
whichever happened last.
4376+
4377+
This method will only return errors set via the mentioned scenarios. To get the last system error independent of
4378+
platform invoke usage, use <xref:System.Runtime.InteropServices.Marshal.GetLastSystemError%2A>.
4379+
4380+
This method is functionally equivalent to <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>. It is
4381+
named to better reflect the intent of the API and its cross-platform nature. <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A>
4382+
should be preferred over <xref:System.Runtime.InteropServices.Marshal.GetLastWin32Error%2A>.
4383+
4384+
## Examples
4385+
4386+
The following example defines a p/invoke with <xref:System.Runtime.InteropServices.DllImportAttribute.SetLastError?displayProperty=nameWithType>
4387+
set to `true` and demonstrates using <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A> to get the last p/invoke error.
4388+
4389+
:::code language="csharp" source="~/samples/snippets/csharp/System.Runtime.InteropServices/Marshal/GetLastPInvokeError.cs" id="Snippet1":::
43744390

43754391
]]></format>
43764392
</remarks>
@@ -4399,14 +4415,17 @@ The last platform invoke error corresponds to the error set by either the most r
43994415
</ReturnValue>
44004416
<Parameters />
44014417
<Docs>
4402-
<summary>Get the last system error on the current thread</summary>
4403-
<returns>The last system error</returns>
4418+
<summary>Gets the last system error on the current thread.</summary>
4419+
<returns>The last system error.</returns>
44044420
<remarks>
44054421
<format type="text/markdown"><![CDATA[
44064422

44074423
## Remarks
44084424

4409-
The error is that for the current operating system (e.g. errno on Unix, GetLastError on Windows)
4425+
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.
4426+
4427+
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
4428+
platform invoke usage. To get the last platform invoke error, use <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A>.
44104429

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

44744493
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.
44754494

4495+
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>.
4496+
44764497
## Examples
44774498
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.
44784499

@@ -9692,9 +9713,17 @@ There is a difference in the behavior of the `GetLastWin32Error` method on .NET
96929713
<Parameter Name="error" Type="System.Int32" Index="0" FrameworkAlternate="net-6.0" />
96939714
</Parameters>
96949715
<Docs>
9695-
<param name="error">Error to set</param>
9696-
<summary>Set the last platform invoke error on the current thread</summary>
9697-
<remarks>To be added.</remarks>
9716+
<param name="error">The error to set.</param>
9717+
<summary>Sets the last platform invoke error on the current thread.</summary>
9718+
<remarks>
9719+
<format type="text/markdown"><![CDATA[
9720+
9721+
## Remarks
9722+
9723+
The last platform invoke error is stored per-thread and can be retrieved using <xref:System.Runtime.InteropServices.Marshal.GetLastPInvokeError%2A>.
9724+
9725+
]]></format>
9726+
</remarks>
96989727
</Docs>
96999728
</Member>
97009729
<Member MemberName="SetLastSystemError">
@@ -9722,14 +9751,14 @@ There is a difference in the behavior of the `GetLastWin32Error` method on .NET
97229751
<Parameter Name="error" Type="System.Int32" Index="0" FrameworkAlternate="net-6.0" />
97239752
</Parameters>
97249753
<Docs>
9725-
<param name="error">Error to set</param>
9726-
<summary>Set the last system error on the current thread</summary>
9754+
<param name="error">The error to set.</param>
9755+
<summary>Sets the last system error on the current thread.</summary>
97279756
<remarks>
97289757
<format type="text/markdown"><![CDATA[
97299758

97309759
## Remarks
97319760

9732-
The error is that for the current operating system (e.g. errno on Unix, SetLastError on Windows)
9761+
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.
97339762

97349763
]]></format>
97359764
</remarks>

0 commit comments

Comments
 (0)