Add modern COM interop support#173
Conversation
|
Copilot review |
Addressed one high-confidence issue from review and pushed a fix in |
There was a problem hiding this comment.
Pull request overview
Adds opt-in source-generated COM interop and NativeAOT support while retaining the legacy package surface.
Changes:
- Adds generated COM interfaces, proxies, events, factories, and WinForms hosts.
- Adds package mode selection and architecture/capability validation.
- Migrates the sample application and adds compatibility/package tests.
Reviewed changes
Copilot reviewed 62 out of 70 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
README.md |
Documents interop modes and APIs. |
dotnet/MsRdpEx_Test/Variant.cs |
Adds VARIANT layout tests. |
dotnet/MsRdpEx_Test/Utilities.cs |
Adds reflection test helpers. |
dotnet/MsRdpEx_Test/MsRdpEx_Test.csproj |
Configures interop tests. |
dotnet/MsRdpEx_Test/Compatibility.cs |
Tests legacy API compatibility. |
dotnet/MsRdpEx_Test/BinaryString.cs |
Tests binary-string semantics. |
dotnet/MsRdpEx_NativeAotInterop_Test/Program.cs |
Adds NativeAOT consumer checks. |
dotnet/MsRdpEx_NativeAotInterop_Test/MsRdpEx_NativeAotInterop_Test.csproj |
Configures NativeAOT publishing. |
dotnet/MsRdpEx_LegacyInterop_Test/Program.cs |
Adds legacy consumer fixture. |
dotnet/MsRdpEx_LegacyInterop_Test/MsRdpEx_LegacyInterop_Test.csproj |
Configures legacy package test. |
dotnet/MsRdpEx_Gen/Program.cs |
Adds interop source generator. |
dotnet/MsRdpEx_Gen/MsRdpEx_Gen.csproj |
Configures generator build. |
dotnet/MsRdpEx_App/RdpView.cs |
Adapts generated COM lifetimes. |
dotnet/MsRdpEx_App/Program.cs |
Disables default COM visibility. |
dotnet/MsRdpEx_App/MsRdpEx_App.csproj |
Uses generated interop projects. |
dotnet/MsRdpEx_App/MainDlg.cs |
Migrates calls to compatibility helpers. |
dotnet/Interop.MSTSCLib.Generated/Interop.MSTSCLib.Generated.csproj |
Builds standalone generated interop. |
dotnet/Interop.MSTSCLib.Generated.WinForms/Interop.MSTSCLib.Generated.WinForms.csproj |
Builds independent WinForms host. |
dotnet/Interop.MSTSCLib.Generated.WinForms/GeneratedRdpClientHost.cs |
Implements generated ActiveX hosting. |
dotnet/Devolutions.MsRdpEx/MarshalHelpers.cs |
Adds source-generated UTF-8 marshalling. |
dotnet/Devolutions.MsRdpEx/LoadBalanceInfo.cs |
Removes the legacy load-balance shim. |
dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.targets |
Adds package interop selection. |
dotnet/Devolutions.MsRdpEx/Devolutions.MsRdpEx.csproj |
Multi-targets and packages interop assets. |
dotnet/Devolutions.MsRdpEx/Bindings.cs |
Migrates native COM bindings. |
dotnet/CMakeLists.txt |
Includes new managed projects. |
dotnet/AxInterop.MSTSCLib/RdpAxHost.cs |
Adapts ActiveX hosting for generated COM. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/TransportSettings.cs |
Declares transport interfaces. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/Shell.cs |
Declares client-shell interfaces. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/SecuredSettings.cs |
Declares secured-settings interfaces. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/RemoteProgram.cs |
Declares RemoteApp interfaces. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/RemoteDesktopClient.cs |
Declares modern client interfaces. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/RedirectionInfo.cs |
Declares redirection settings. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/NonScriptableClient.cs |
Declares non-scriptable APIs. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/ExtendedSettings.cs |
Declares extended settings. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/Drive.cs |
Declares drive redirection APIs. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/Device.cs |
Declares device redirection APIs. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/Debug.cs |
Declares debug interfaces. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/Clipboard.cs |
Declares clipboard APIs. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/Client.cs |
Declares core RDP interfaces and events. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/Camera.cs |
Declares camera redirection APIs. |
dotnet/AxInterop.MSTSCLib/MSTSCLib/AdvancedSettings.cs |
Declares advanced settings. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/Variant.cs |
Implements VARIANT marshalling. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/RdpClientFactory.cs |
Adds typed COM activation. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/ProxyObject.cs |
Implements generated proxy wrapping. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/ProxyMetadata.cs |
Maps interfaces to proxies and IIDs. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/IDispatch.cs |
Declares generated IDispatch support. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/Extensions.cs |
Adds low-level interop adapters. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/BinaryStringRef.cs |
Adds borrowed binary BSTR support. |
dotnet/AxInterop.MSTSCLib/Interop/NetCore/BinaryString.cs |
Adds managed binary BSTR support. |
dotnet/AxInterop.MSTSCLib/Interop/Extensions.cs |
Adds compatibility extension methods. |
dotnet/AxInterop.MSTSCLib/Interop/Desktop/ProxyObject.cs |
Adds desktop proxy passthrough. |
dotnet/AxInterop.MSTSCLib/Interop/Desktop/Compatibility.cs |
Adds desktop compatibility attribute. |
dotnet/AxInterop.MSTSCLib/Interop/Desktop/BinaryString.cs |
Adds desktop binary BSTR support. |
dotnet/AxInterop.MSTSCLib/Interop/Compatibility.cs |
Adds classic event sink interfaces. |
dotnet/AxInterop.MSTSCLib/Interop/BinaryString.cs |
Adds classic custom marshaller. |
dotnet/AxInterop.MSTSCLib/Generated/Enums.cs |
Adds generated legacy-compatible enums. |
dotnet/AxInterop.MSTSCLib/AxInterop.MSTSCLib.csproj |
Configures generated WinForms interop. |
dotnet/AxInterop.MSTSCLib.Legacy/RdpAxHost.Legacy.cs |
Preserves legacy ActiveX hosting. |
dotnet/AxInterop.MSTSCLib.Legacy/AxInterop.MSTSCLib.Legacy.csproj |
Builds legacy interop assemblies. |
.github/workflows/build-package.yml |
Validates package consumers and PE payloads. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public static nint ConvertToUnmanaged(BinaryStringRef value) | ||
| { | ||
| fixed (byte* pointer = value.content) | ||
| return BinaryStringInterop.AllocateByteBuffer(pointer, value.content.Length); |
| nint result; | ||
| if (data.mTextData is not null) | ||
| { | ||
| fixed (char* pTextData = data.mTextData) | ||
| result = BinaryStringInterop.AllocateTextBuffer(pTextData, data.mTextData.Length); | ||
| } | ||
| else if (!data.mByteData.IsDefault) | ||
| { | ||
| fixed (byte* pByteData = data.mByteData.AsSpan()) | ||
| result = BinaryStringInterop.AllocateByteBuffer(pByteData, data.mByteData.Length); | ||
| } | ||
| else throw new UnreachableException(); |
| default: | ||
| throw new NotSupportedException(); |
There was a problem hiding this comment.
where is GetPublisherCertificateChain appearing? I couldn't find it with a quick search. I'd double check this claim before bothering with it, during my tests I didn't run into the exception (or I'd have added the missing cases)
There was a problem hiding this comment.
It is declared as IMsRdpClientNonScriptable4.GetPublisherCertificateChain in dotnet/AxInterop.MSTSCLib/MSTSCLib/NonScriptableClient.cs and called by the generated redirection adapter. The VARIANT marshaller now round-trips the expected VT_ARRAY | VT_UI1 payload as byte[]; this also supports SetPublisherCertificateChain.
| return ProxyObject.Pack(ComInterfaceMarshaller<object>.ConvertToManaged((void*)data.Content1)); | ||
|
|
||
| case VariantType.ByRefModifier | VariantType.Error: return *(int*)data.Content1; | ||
| case VariantType.ByRefModifier | VariantType.Boolean: return *(short*)data.Content1; |
| case VariantType.ByRefModifier | VariantType.Currency: return *(long*)data.Content1; | ||
| case VariantType.ByRefModifier | VariantType.DateTime: return *(double*)data.Content1; |
| <Error Condition="'$(MsRdpExComInterop)' == 'Generated' And !Exists('$(_MsRdpExGeneratedInteropPath)')" | ||
| Text="MsRdpExComInterop=Generated requires '$(_MsRdpExGeneratedInteropPath)', but the selected Devolutions.MsRdpEx package does not contain it. Select a package with MsRdpExGeneratedInteropSupported=true or use Legacy." /> |
| dotnet test .\dotnet\MsRdpEx_Test\MsRdpEx_Test.csproj -c Release -p:CMakeOutputPath="$BuildDir\Release" -v:minimal | ||
| dotnet run --project .\dotnet\MsRdpEx_LegacyInterop_Test\MsRdpEx_LegacyInterop_Test.csproj -c Release -p:MsRdpExTestPackageSource="$PackageSource" -p:MsRdpExPackageVersion="$PackageVersion" --no-launch-profile | ||
| dotnet publish .\dotnet\MsRdpEx_NativeAotInterop_Test\MsRdpEx_NativeAotInterop_Test.csproj -c Release -o .\nativeaot-test -p:MsRdpExTestPackageSource="$PackageSource" -p:MsRdpExPackageVersion="$PackageVersion" -v:minimal | ||
| & .\nativeaot-test\MsRdpEx_NativeAotInterop_Test.exe |
d435375 to
4d3c30c
Compare
Preserve legacy NuGet interop as the default while adding source-generated COM projections, NativeAOT support, generated WinForms hosting, consumer validation, and compatibility tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
4d3c30c to
4547687
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…175) #173 removed this hand-written declaration along with the rest of the manual interop surface — the generated interop types `LoadBalanceInfo` as `BinaryString` and no longer needs it. But legacy-interop consumers on the current package lose their only way to set a byte-exact `LoadBalanceInfo`. Restored verbatim from `2026.7.3`, relocated into `AxInterop.MSTSCLib.Legacy` so it ships in the legacy interop assembly and `Devolutions.MsRdpEx` stays MSTSCLib-free. Context: lets RDM bump to the current package (recording-cursor fix, #174) while staying on the legacy interop, without carrying this declaration itself. Verified against RDM: builds clean, `LoadBalanceInfo` path unchanged.
Summary
Adds source-generated COM interop alongside the preserved legacy default package surface.
Interop.MSTSCLibandAxInterop.MSTSCLibavailable by default fornet48andnet8.0-windowsconsumers.GeneratedCOM interop with NativeAOT support, proxy helpers, typed factories, and connection-point events.MsRdpExGeneratedWinForms=true.Fixes #97.
Credit
Many thanks to Tobias Käs (@weltkante) for the original experimental
srcgen/srcgen2implementation that this work builds upon.