Skip to content

Commit 1ab4b9b

Browse files
authored
Add remarks to UnmanagedCallConvAttribute documentation (#7356)
1 parent 07b2c6a commit 1ab4b9b

File tree

6 files changed

+116
-3
lines changed

6 files changed

+116
-3
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class Program
2+
{
3+
public static void Main(string[] args)
4+
{
5+
// Snippets in this project currently only target fake native libraries,
6+
// so we do not actually invoke any of the targets here.
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7+
</PropertyGroup>
8+
9+
</Project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
internal static class Cdecl
6+
{
7+
//<SnippetCdecl>
8+
// Target will be invoked using the cdecl calling convention
9+
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
10+
[DllImport("NativeLibrary", EntryPoint = "native_function_cdecl")]
11+
internal static extern int NativeFunction(int arg);
12+
13+
// Target will be invoked using the cdecl calling convention and with the GC transition suppressed
14+
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl), typeof(CallConvSuppressGCTransition) })]
15+
[DllImport("NativeLibrary", EntryPoint = "native_function_cdecl")]
16+
internal static extern int NativeFunction_NoGCTransition(int arg);
17+
//</SnippetCdecl>
18+
}
19+
20+
internal static class Stdcall
21+
{
22+
//<SnippetStdcall>
23+
// Target will be invoked using the stdcall calling convention
24+
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvStdcall) })]
25+
[DllImport("NativeLibrary", EntryPoint = "native_function_stdcall")]
26+
internal static extern int NativeFunction(int arg);
27+
28+
// Target will be invoked using the stdcall calling convention and with the GC transition suppressed
29+
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvStdcall), typeof(CallConvSuppressGCTransition) })]
30+
[DllImport("NativeLibrary", EntryPoint = "native_function_stdcall")]
31+
internal static extern int NativeFunction_NoGCTransition(int arg);
32+
//</SnippetStdcall>
33+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
internal static class UnmanagedCallersOnly
6+
{
7+
//<SnippetUnmanagedCallersOnly>
8+
[DllImport("NativeLibrary")]
9+
internal static extern unsafe void NativeFunctionWithCallback(delegate* unmanaged[Cdecl]<int, int> callback);
10+
11+
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
12+
private static int DoubleInt(int i) => i * 2;
13+
14+
public static unsafe void PassCallbackToNativeFunction()
15+
{
16+
NativeFunctionWithCallback(&DoubleInt);
17+
}
18+
//</SnippetUnmanagedCallersOnly>
19+
}

xml/System.Runtime.InteropServices/UnmanagedCallConvAttribute.xml

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,36 @@
2121
</Attributes>
2222
<Docs>
2323
<summary>Provides an equivalent to <see cref="T:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute" /> for native functions declared in .NET.</summary>
24-
<remarks>To be added.</remarks>
24+
<remarks>
25+
<format type="text/markdown"><![CDATA[
26+
27+
## Remarks
28+
29+
When this attribute is applied to a method with <xref:System.Runtime.InteropServices.DllImportAttribute>
30+
where <xref:System.Runtime.InteropServices.DllImportAttribute.CallingConvention> is either not set or set
31+
to <xref:System.Runtime.InteropServices.CallingConvention.Winapi>, the .NET runtime will use
32+
<xref:System.Runtime.InteropServices.UnmanagedCallConvAttribute.CallConvs> to determine the calling convention
33+
of the p/invoke.
34+
35+
This attribute is ignored if applied to a method without <xref:System.Runtime.InteropServices.DllImportAttribute>
36+
or with <xref:System.Runtime.InteropServices.DllImportAttribute.CallingConvention> set to something other than
37+
<xref:System.Runtime.InteropServices.CallingConvention.Winapi>.
38+
39+
## Examples
40+
41+
The following examples declare p/invokes using the <xref:System.Runtime.InteropServices.UnmanagedCallConvAttribute>
42+
to specify the calling convention.
43+
44+
Using the `cdecl` calling convention:
45+
46+
:::code language="csharp" source="~/samples/snippets/csharp/System.Runtime.InteropServices/Attributes/UnmanagedCallConv.cs" id="SnippetCdecl":::
47+
48+
Using the `stdcall` calling convention:
49+
50+
:::code language="csharp" source="~/samples/snippets/csharp/System.Runtime.InteropServices/Attributes/UnmanagedCallConv.cs" id="SnippetStdcall":::
51+
52+
]]></format>
53+
</remarks>
2554
</Docs>
2655
<Members>
2756
<Member MemberName=".ctor">
@@ -57,16 +86,20 @@
5786
<ReturnType>System.Type[]</ReturnType>
5887
</ReturnValue>
5988
<Docs>
60-
<summary>Types indicating calling conventions for the unmanaged target.</summary>
89+
<summary>Optional. Types indicating calling conventions for the unmanaged target.</summary>
6190
<remarks>
6291
<format type="text/markdown"><![CDATA[
6392
6493
## Remarks
6594
66-
If `null`, the semantics are identical to `CallingConvention.Winapi`.
95+
If `null`, the runtime will use the default platform calling convention&mdash;identical to <xref:System.Runtime.InteropServices.CallingConvention.Winapi>
96+
for <xref:System.Runtime.InteropServices.DllImportAttribute.CallingConvention>.
6797
6898
]]></format>
6999
</remarks>
100+
<altmember cref="T:System.Runtime.CompilerServices.CallConvCdecl" />
101+
<altmember cref="T:System.Runtime.CompilerServices.CallConvStdcall" />
102+
<altmember cref="T:System.Runtime.CompilerServices.CallConvSuppressGCTransition" />
70103
</Docs>
71104
</Member>
72105
</Members>

xml/System.Runtime.InteropServices/UnmanagedCallersOnlyAttribute.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ Methods marked with this attribute have the following restrictions:
3232
* Must not be called from managed code.
3333
* Must only have [blittable](https://docs.microsoft.com/dotnet/framework/interop/blittable-and-non-blittable-types) arguments.
3434
* Must not have generic type parameters or be contained within a generic class.
35+
36+
## Examples
37+
38+
The following example demonstrates passing a callback marked <xref:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute>
39+
to a native function.
40+
41+
:::code language="csharp" source="~/samples/snippets/csharp/System.Runtime.InteropServices/Attributes/UnmanagedCallersOnly.cs" id="SnippetUnmanagedCallersOnly":::
42+
3543
]]></format>
3644
</remarks>
3745
</Docs>
@@ -84,6 +92,9 @@ The "default platform calling convention" is generally unambiguous for all suppo
8492
* Non-Windows - [cdecl](https://docs.microsoft.com/cpp/cpp/cdecl)
8593
]]></format>
8694
</remarks>
95+
<altmember cref="T:System.Runtime.CompilerServices.CallConvCdecl" />
96+
<altmember cref="T:System.Runtime.CompilerServices.CallConvStdcall" />
97+
<altmember cref="T:System.Runtime.CompilerServices.CallConvSuppressGCTransition" />
8798
</Docs>
8899
</Member>
89100
<Member MemberName="EntryPoint">

0 commit comments

Comments
 (0)