Skip to content

Generate safe COM output pointer ABI - #1831

Merged
Jevan Saks (jevansaks) merged 6 commits into
mainfrom
feature/com-out-ptr-abi
Oct 2, 2026
Merged

Jevan Saks (jevansaks) merged 6 commits into
mainfrom
feature/com-out-ptr-abi

Conversation

@jevansaks

@jevansaks Jevan Saks (jevansaks) commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Summary

  • project canonical COM IID + [ComOutPtr] void** interface parameters as implementation-safe out void*
  • generate Windows.Win32.ComOutPtr.FromManaged to return the exact requested interface with caller-owned COM reference semantics
  • preserve generic out T friendly overloads and adaptive COM/WinRT projection
  • preserve flat P/Invoke, no-marshalling, and UseIntPtrForComOutPointers behavior
  • cover IClassFactory implementations across source-generated COM, built-in COM, and auto-WinRT enabled/disabled configurations

Fixes #1827

Rationale and compatibility

A native COM method with an IID + ppv parameter pair promises more than simply returning a COM pointer. On success, ppv must point at the exact interface identified by the IID, with that interface's vtable layout and a caller-owned reference. The native caller immediately interprets the returned pointer according to the requested interface and invokes its vtable slots.

The current managed out object projection cannot guarantee that contract for implementations. The COM source generator recognizes IidParameterIndex, but it does not make the sibling IID value available to the custom marshaller for the [ComOutPtr] parameter. The marshaller can obtain a COM pointer for the managed object, but it cannot perform QueryInterface for the IID the caller actually requested. The pointer may therefore represent IUnknown or another interface rather than the requested interface. It can be a valid COM pointer while still exposing the wrong vtable to the native caller.

Until CsWin32 can build on a separately extensible COM source-generator package, or custom marshallers can otherwise receive the value identified by IidParameterIndex, this change projects the implementation-facing parameter as out void*. Implementers can use ComOutPtr.FromManaged to perform QueryInterface for the exact requested IID and transfer the resulting caller-owned interface pointer. This makes the unsafe ABI representation visible, but makes it possible to implement the ABI correctly.

Callers are generally unaffected because CsWin32 continues to generate the friendly generic out T overload, which supplies the IID for T and converts the returned native interface pointer to T. This is a source change for managed implementations that currently declare the parameter as out object.

The prior raw IID, out object calling pattern is no longer available. Callers with only a runtime IID must use the raw output pointer and observe normal COM ownership rules. We are accepting that tradeoff for now and will listen for feedback if the dynamic-IID pattern is important in practice. If the source-generator extensibility described above becomes available, the managed out object projection can be reconsidered without sacrificing exact interface identity.

Validation

  • 129 COM generator tests passed
  • source-generated COM runtime tests passed on .NET 9 and .NET 10
  • built-in COM runtime tests passed on .NET 9 and .NET 10
  • auto-WinRT-disabled IClassFactory runtime tests passed on .NET 9 and .NET 10
  • affected projects build with zero warnings
  • full GitHub build, test, documentation, and CodeQL checks passed
  • git diff --check passed

Project canonical IID/ComOutPtr pairs as out void* and provide ComOutPtr.FromManaged for managed COM implementers. Preserve friendly generic overloads and cover source-generated, built-in, and WinRT marshalling configurations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Project the returned IDispatch pointer through built-in COM and release the caller-owned native reference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build unofficial VS-feed previews without strong-name identities when package signing is disabled, avoiding invalid delay-signed assemblies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The signing plugin rejects assemblies that intentionally have no public key, so omit it only when publishing unsigned VS-feed preview packages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the pull request focused on the COM output pointer ABI now that preview validation is complete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jevansaks
Jevan Saks (jevansaks) marked this pull request as ready for review October 1, 2026 06:10
Comment thread test/GenerationSandbox.Tests/ComRuntimeTests.cs
@jevansaks
Jevan Saks (jevansaks) enabled auto-merge (squash) October 2, 2026 22:46
@jevansaks
Jevan Saks (jevansaks) merged commit 88ef109 into main Oct 2, 2026
20 checks passed
@jevansaks
Jevan Saks (jevansaks) deleted the feature/com-out-ptr-abi branch October 2, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IClassFactory.CreateInstance returns IUnknown instead of the interface requested by riid

2 participants