Skip to content

Commit fca7a37

Browse files
jtschusterAaronRobinsonMSFTelinor-fungjkoritzinskygewarren
authored
Create Documentation for the ComInterfaceGenerator (#36833)
Create documentation for the ComInterfaceGenerator and update related pages with links. Co-authored-by: Aaron Robinson <[email protected]> Co-authored-by: Elinor Fung <[email protected]> Co-authored-by: Jeremy Koritzinsky <[email protected]> Co-authored-by: Genevieve Warren <[email protected]>
1 parent 8be7d68 commit fca7a37

File tree

9 files changed

+299
-16
lines changed

9 files changed

+299
-16
lines changed

docs/navigate/advanced-programming/toc.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,14 @@ items:
364364
href: ../../standard/native-interop/com-callable-wrapper.md
365365
- name: "Tutorial: Use the ComWrappers API"
366366
href: ../../standard/native-interop/tutorial-comwrappers.md
367+
- name: Source generation
368+
href: ../../standard/native-interop/comwrappers-source-generation.md
367369
- name: Qualify .NET types for COM interop
368370
href: ../../standard/native-interop/qualify-net-types-for-interoperation.md
369371
- name: Apply interop attributes
370372
href: ../../standard/native-interop/apply-interop-attributes.md
373+
- name: HRESULT and PreserveSig
374+
href: ../../standard/native-interop/preserve-sig.md
371375
- name: Exceptions
372376
href: ../../standard/native-interop/exceptions-interoperability.md
373377
- name: Memory management

docs/standard/native-interop/com-wrappers.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: "COM Wrappers"
33
description: COM clients and .NET objects interact by using a COM callable wrapper and a runtime callable wrapper. The CLR creates wrappers automatically.
44
ms.date: "03/30/2017"
5-
helpviewer_keywords:
5+
helpviewer_keywords:
66
- "wrapper classes"
77
- "COM interop, COM wrappers"
88
- "COM wrappers"
@@ -13,22 +13,23 @@ ms.assetid: e56c485b-6b67-4345-8e66-fd21835a6092
1313
---
1414
# COM Wrappers
1515

16-
COM differs from the .NET runtime object model in several important ways:
17-
18-
- Clients of COM objects must manage the lifetime of those objects; the common language runtime manages the lifetime of objects in its environment.
19-
20-
- Clients of COM objects discover whether a service is available by requesting an interface that provides that service and getting back an interface pointer, or not. Clients of .NET objects can obtain a description of an object's functionality using reflection.
21-
22-
- NET objects reside in memory managed by the .NET runtime execution environment. The execution environment can move objects around in memory for performance reasons and update all references to the objects it moves. Unmanaged clients, having obtained a pointer to an object, rely on the object to remain at the same location. These clients have no mechanism for dealing with an object whose location is not fixed.
23-
24-
To overcome these differences, the runtime provides wrapper classes to make both managed and unmanaged clients think they are calling objects within their respective environment. Whenever your managed client calls a method on a COM object, the runtime creates a [runtime callable wrapper](runtime-callable-wrapper.md) (RCW). RCWs abstract the differences between managed and unmanaged reference mechanisms, among other things. The runtime also creates a [COM callable wrapper](com-callable-wrapper.md) (CCW) to reverse the process, enabling a COM client to seamlessly call a method on a .NET object. As the following illustration shows, the perspective of the calling code determines which wrapper class the runtime creates.
25-
26-
![COM wrapper overview](./media/com-wrappers/bidirectional-com-overview.gif)
27-
28-
In most cases, the standard RCW or CCW generated by the runtime provides adequate marshalling for calls that cross the boundary between COM and the .NET runtime. Using custom attributes, you can optionally adjust the way the runtime represents managed and unmanaged code.
29-
16+
COM differs from the .NET runtime object model in several important ways:
17+
18+
- Clients of COM objects must manage the lifetime of those objects; the common language runtime manages the lifetime of objects in its environment.
19+
20+
- Clients of COM objects discover whether a service is available by requesting an interface that provides that service and getting back an interface pointer, or not. Clients of .NET objects can obtain a description of an object's functionality using reflection.
21+
22+
- NET objects reside in memory managed by the .NET runtime execution environment. The execution environment can move objects around in memory for performance reasons and update all references to the objects it moves. Unmanaged clients, having obtained a pointer to an object, rely on the object to remain at the same location. These clients have no mechanism for dealing with an object whose location is not fixed.
23+
24+
To overcome these differences, the runtime provides wrapper classes to make both managed and unmanaged clients think they are calling objects within their respective environment. Whenever your managed client calls a method on a COM object, the runtime creates a [runtime callable wrapper](runtime-callable-wrapper.md) (RCW). RCWs abstract the differences between managed and unmanaged reference mechanisms, among other things. The runtime also creates a [COM callable wrapper](com-callable-wrapper.md) (CCW) to reverse the process, enabling a COM client to seamlessly call a method on a .NET object. As the following illustration shows, the perspective of the calling code determines which wrapper class the runtime creates.
25+
26+
![COM wrapper overview](./media/com-wrappers/bidirectional-com-overview.gif)
27+
28+
In most cases, the standard RCW or CCW generated by the runtime provides adequate marshalling for calls that cross the boundary between COM and the .NET runtime. Using custom attributes, you can optionally adjust the way the runtime represents managed and unmanaged code.
29+
3030
## See also
3131

32+
- [ComWrappers Source Generation](./comwrappers-source-generation.md)
3233
- [Advanced COM Interoperability in .NET Framework](/previous-versions/dotnet/netframework-4.0/bd9cdfyx(v=vs.100))
3334
- [Runtime Callable Wrapper](runtime-callable-wrapper.md)
3435
- [COM Callable Wrapper](com-callable-wrapper.md)

docs/standard/native-interop/cominterop.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ ms.date: 07/11/2019
88

99
The Component Object Model (COM) lets an object expose its functionality to other components and to host applications on Windows platforms. To help enable users to interoperate with their existing code bases, .NET Framework has always provided strong support for interoperating with COM libraries. In .NET Core 3.0, a large portion of this support has been added to .NET Core on Windows. The documentation here explains how the common COM interop technologies work and how you can utilize them to interoperate with your existing COM libraries.
1010

11+
## Built-in and source-generated COM interop
12+
13+
COM interop functionality can be achieved through a built-in system in the .NET runtime or through implementing the [ComWrappers API](./tutorial-comwrappers.md) (introduced in .NET 6). Starting in .NET 8, you can use the [COM source generator](./comwrappers-source-generation.md) to automatically implement the `ComWrappers` API for `IUnknown`-based interfaces.
14+
1115
- [COM Wrappers](./com-wrappers.md)
1216
- [COM Callable Wrappers](./com-callable-wrapper.md)
1317
- [Runtime Callable Wrappers](./runtime-callable-wrapper.md)
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
---
2+
title: ComWrappers source generation
3+
description: Learn about compile-time source generation for ComWrappers in .NET.
4+
ms.date: 08/25/2023
5+
---
6+
7+
# Source generation for ComWrappers
8+
9+
.NET 8 introduces a source generator that creates an implementation of the [ComWrappers](./com-wrappers.md) API for you. The generator recognizes the <xref:System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute>.
10+
11+
The .NET runtime's built-in (not source-generated), Windows-only, COM interop system generates an IL stub&mdash;a stream of IL instructions that's JIT-ed&mdash;at run time to facilitate the transition from managed code to COM, and vice-versa. Since this IL stub is generated at run time, it's incompatible with [NativeAOT](../../core/deploying/native-aot/index.md) and [IL trimming](../../core/deploying/trimming/trim-self-contained.md). Stub generation at run time can also make diagnosing marshalling issues difficult.
12+
13+
Built-in interop uses attributes such as `ComImport` or `DllImport`, which rely on code generation at run time. The following code shows an example of this:
14+
15+
```csharp
16+
[ComImport]
17+
interface IFoo
18+
{
19+
void Method(int i);
20+
}
21+
22+
[DllImport("MyComObjectProvider.dll")]
23+
static nint GetPointerToComInterface();
24+
25+
[DllImport("MyComObjectProvider.dll")]
26+
static void GivePointerToComInterface(nint comObject);
27+
28+
// Use the system to create a Runtime Callable Wrapper to use in managed code
29+
nint ptr = GetPointerToComInterface();
30+
IFoo foo = (IFoo)Marshal.GetObjectForIUnknown(ptr);
31+
foo.Method(0);
32+
...
33+
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
34+
IFoo foo = GetManagedIFoo();
35+
nint ptr = Marshal.GetIUnknownForObject(foo);
36+
GivePointerToComInterface(ptr);
37+
```
38+
39+
The `ComWrappers` API enables interacting with COM in C# without using the built-in COM system, but [requires substantial boilerplate and hand-written unsafe code](./tutorial-comwrappers.md). The COM interface generator automates this process and makes `ComWrappers` as easy as built-in COM, but delivers it in a trimmable and AOT-friendly manner.
40+
41+
## Basic usage
42+
43+
To use the COM interface generator, add the <xref:System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute> and <xref:System.Runtime.InteropServices.GuidAttribute> attributes on the interface definition that you want to import from or expose to COM. The type must be marked `partial` and have `internal` or `public` visibility for the generated code to be able to access it.
44+
45+
```csharp
46+
[GeneratedComInterface]
47+
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
48+
internal partial IFoo
49+
{
50+
void Method(int i);
51+
}
52+
```
53+
54+
Then, to expose a class that implements an interface to COM, add the <xref:System.Runtime.InteropServices.Marshalling.GeneratedComClassAttribute> to the implementing class. This class must also be `partial` and either `internal` or `public`.
55+
56+
```csharp
57+
[GeneratedComClass]
58+
internal partial class Foo : IFoo
59+
{
60+
public void Method(int i)
61+
{
62+
// Do things
63+
}
64+
}
65+
```
66+
67+
At compile time, the generator creates an implementation of the ComWrappers API, and you can use the <xref:System.Runtime.InteropServices.Marshalling.StrategyBasedComWrappers> type or a custom derived type to consume or expose the COM interface.
68+
69+
```csharp
70+
[LibraryImport("MyComObjectProvider.dll")]
71+
static nint GetPointerToComInterface();
72+
73+
[LibraryImport("MyComObjectProvider.dll")]
74+
static void GivePointerToComInterface(nint comObject);
75+
76+
// Use the ComWrappers API to create a Runtime Callable Wrapper to use in managed code
77+
ComWrappers cw = new StrategyBasedComWrappers();
78+
nint ptr = GetPointerToComInterface();
79+
IFoo foo = (IFoo)cw.GetOrCreateObjectForComInterface(ptr);
80+
foo.Method(0);
81+
...
82+
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
83+
ComWrappers cw = new StrategyBasedComWrappers();
84+
Foo foo = new();
85+
nint ptr = cw.GetOrCreateComInterfaceForObject(foo);
86+
GivePointerToComInterface(ptr);
87+
```
88+
89+
## Customize marshalling
90+
91+
The COM interface generator respects the <xref:System.Runtime.InteropServices.Marshalling.MarshalUsingAttribute> attribute and some usages of the <xref:System.Runtime.InteropServices.MarshalAsAttribute> attribute to customize marshalling of parameters. For more information, see how to [customize source-generated marshalling with the `MarshalUsing` attribute](./custom-marshalling-source-generation.md) and [customize parameter marshalling with the `MarshalAs` attribute](./customize-parameter-marshalling.md). The <xref:System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute.StringMarshalling?displayProperty=nameWithType> and <xref:System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute.StringMarshallingCustomType?displayProperty=nameWithType> properties apply to all parameters and return types of type `string` in the interface if they don't have other marshalling attributes.
92+
93+
## Implicit HRESULTs and PreserveSig
94+
95+
COM methods in C# have a different signature than the native methods. Standard COM has a return type of `HRESULT`, a 4 byte integer type representing error and success states. This `HRESULT` return value is hidden by default in the C# signature and converted to an exception when an error value is returned. The last "out" parameter of the native COM signature may optionally be converted into the return in the C# signature.
96+
97+
For example, the following snippets show C# method signatures and the corresponding native signature the generator infers.
98+
99+
```csharp
100+
void Method1(int i);
101+
102+
int Method2(float i);
103+
```
104+
105+
```c
106+
HRESULT Method1(int i);
107+
108+
HRESULT Method2(float i, _Out_ int* returnValue);
109+
```
110+
111+
If you want to handle the `HRESULT` yourself, you can use the <xref:System.Runtime.InteropServices.PreserveSigAttribute> on the method to indicate the generator should not do this transformation. The following snippets demonstrate what native signature the generator expects when `[PreserveSig]` is applied. COM methods must return `HRESULT`, so the return value of any method with `PreserveSig` should be `int`.
112+
113+
```csharp
114+
[PreserveSig]
115+
int Method1(int i, out int j);
116+
117+
[PreserveSig]
118+
int Method2(float i);
119+
```
120+
121+
```c
122+
HRESULT Method1(int i, int* j);
123+
124+
HRESULT Method2(float i);
125+
```
126+
127+
For more information, see [Implicit method signature translations in .NET interop](./preserve-sig.md)
128+
129+
## Incompatibilities and differences to built-in COM
130+
131+
### `IUnknown` only
132+
133+
The only supported interface base is [`IUnknown`](/windows/win32/api/unknwn/nn-unknwn-iunknown). Interfaces with an <xref:System.Runtime.InteropServices.InterfaceTypeAttribute> that has a value other than <xref:System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown> are not supported in source-generated COM. Any interfaces without an `InterfaceTypeAttribute` are assumed to derive from `IUnknown`. This differs from built-in COM where the default is <xref:System.Runtime.InteropServices.ComInterfaceType.InterfaceIsDual>.
134+
135+
## Marshalling defaults and support
136+
137+
Source-generated COM has some different default marshalling behaviors from built-in COM.
138+
139+
- In the built-in COM system, all types have an implicit `[In]` attribute except for arrays of blittable elements, which have implicit `[In, Out]` attributes. In source-generated COM, all types, including arrays of blittable elements, have `[In]` semantics.
140+
141+
- `[In]` and `[Out]` attributes are only allowed on arrays. If `[Out]` or `[In, Out]` behavior is required on other types, use the `in` and `out` parameter modifiers.
142+
143+
### Derived interfaces
144+
145+
In the built-in COM system, if you have interfaces that derive from other COM interfaces, you must declare a shadowing method for each base method on the base interfaces with the `new` keyword. For more information, see [COM interface inheritance and .NET](./qualify-net-types-for-interoperation.md#com-interface-inheritance-and-net).
146+
147+
```csharp
148+
[ComImport]
149+
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
150+
interface IBase
151+
{
152+
void Method1(int i);
153+
void Method2(float i);
154+
}
155+
156+
[ComImport]
157+
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
158+
interface IDerived : IBase
159+
{
160+
new void Method1(int i);
161+
new void Method2(float f);
162+
void Method3(long l);
163+
void Method4(double d);
164+
}
165+
```
166+
167+
The COM interface generator does not expect any shadowing of base methods. To create a method that inherits from another, simply indicate the base interface as a C# base interface and add the derived interface's methods. For more information, see [the design doc](https://github.com/dotnet/runtime/blob/main/docs/design/libraries/ComInterfaceGenerator/DerivedComInterfaces.md).
168+
169+
```csharp
170+
[GeneratedComInterface]
171+
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
172+
interface IBase
173+
{
174+
void Method1(int i);
175+
void Method2(float i);
176+
}
177+
178+
[GeneratedComInterface]
179+
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
180+
interface IDerived : IBase
181+
{
182+
void Method3(long l);
183+
void Method4(double d);
184+
}
185+
```
186+
187+
Note that an interface with the `GeneratedComInterface` attribute can only inherit from one base interface that has the `GeneratedComInterface` attribute.
188+
189+
### Marshal APIs
190+
191+
Some APIs in <xref:System.Runtime.InteropServices.Marshal> are not compatible with source-generated COM. Replace these methods with their corresponding methods on a `ComWrappers` implementation.
192+
193+
## See also
194+
195+
- [ComWrappers](./com-wrappers.md)
196+
- [Customizing Parameter Marshalling](./customize-parameter-marshalling.md)
197+
- [Runtime Callable Wrapper](runtime-callable-wrapper.md)
198+
- [COM Callable Wrapper](com-callable-wrapper.md)

docs/standard/native-interop/customize-parameter-marshalling.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ ms.topic: how-to
99

1010
When the .NET runtime's default parameter marshalling behavior doesn't do what you want, use can use the <xref:System.Runtime.InteropServices.MarshalAsAttribute?displayProperty=nameWithType> attribute to customize how your parameters are marshalled. These customization features do not apply when [runtime marshalling is disabled](disabled-marshalling.md).
1111

12+
> [!NOTE]
13+
> Source-generated interop for [P/Invokes](./pinvoke-source-generation.md) and [COM](./comwrappers-source-generation.md) only respects a small subset of <xref:System.Runtime.InteropServices.MarshalAsAttribute> on parameters. It is recommended to use <xref:System.Runtime.InteropServices.Marshalling.MarshalUsingAttribute> for source-generated interop instead. For more information, see [Custom marshalling for source generation](./custom-marshalling-source-generation.md).
14+
1215
## Customizing string parameters
1316

1417
.NET has a variety of formats for marshalling strings. These methods are split into distinct sections on C-style strings and Windows-centric string formats.

docs/standard/native-interop/customize-struct-marshalling.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ ms.topic: how-to
1111

1212
Sometimes the default marshalling rules for structures aren't exactly what you need. The .NET runtimes provide a few extension points for you to customize your structure's layout and how fields are marshalled. Customizing structure layout is supported for all scenarios, but customizing field marshalling is only supported for scenarios where runtime marshalling is enabled. If [runtime marshalling is disabled](disabled-marshalling.md), then any field marshalling must be done manually.
1313

14-
## Customizing structure layout
14+
> [!NOTE]
15+
> This article doesn't cover customizing marshalling for source-generated interop. If you're using [source-generated interop for P/Invokes](./pinvoke-source-generation.md) or [COM](./comwrappers-source-generation.md), see [customizing marshalling](./custom-marshalling-source-generation.md).
16+
17+
## Customize structure layout
1518

1619
.NET provides the <xref:System.Runtime.InteropServices.StructLayoutAttribute?displayProperty=nameWithType> attribute and the <xref:System.Runtime.InteropServices.LayoutKind?displayProperty=nameWithType> enumeration to allow you to customize how fields are placed in memory. The following guidance will help you avoid common issues.
1720

docs/standard/native-interop/pinvoke.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ Both of the previous examples depend on parameters, and in both cases, the param
6060

6161
## More resources
6262

63+
- [Writing cross platform P/Invokes](./native-library-loading.md)
64+
- [Source generated P/Invoke marshalling](./pinvoke-source-generation.md)
65+
- [PInvoke.net wiki](https://www.pinvoke.net/) an excellent Wiki with information on common Windows APIs and how to call them.
6366
- [C#/Win32 P/Invoke source generator](https://github.com/microsoft/CsWin32/) automatically generates definitions for Windows APIs.
6467
- [P/Invoke in C++/CLI](/cpp/dotnet/native-and-dotnet-interoperability)
6568
- [Mono documentation on P/Invoke](https://www.mono-project.com/docs/advanced/pinvoke/)

0 commit comments

Comments
 (0)