Skip to content

Diagnose LibraryImport/[GeneratedComInterface] with restricted CCW/RCW options - #130097

Open
AaronRobinsonMSFT with Copilot wants to merge 6 commits into
mainfrom
copilot/resolve-com-generic-interface-warning
Open

Diagnose LibraryImport/[GeneratedComInterface] with restricted CCW/RCW options#130097
AaronRobinsonMSFT with Copilot wants to merge 6 commits into
mainfrom
copilot/resolve-com-generic-interface-warning

Conversation

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

When a [LibraryImport] (or a virtual member of a [GeneratedComInterface]) marshals a [GeneratedComInterface]-attributed type in a direction its ComInterfaceOptions cannot support, the generated code compiles but silently uses ComInterfaceMarshaller<T>.ConvertToUnmanaged (needs CCW = ManagedObjectWrapper) or ConvertToManaged (needs RCW = ComObjectWrapper), and fails at runtime. This surfaces the problem at build time as SYSLIB1051.

Given the repro from the issue:

[LibraryImport("dxgi")]
public static partial void CreateDXGIFactory2(uint flags, in Guid riid, out IDXGIFactory6 ppFactory);

[GeneratedComInterface(Options = ComInterfaceOptions.ManagedObjectWrapper)]
[Guid("...")]
public partial interface IDXGIFactory6 { }

the generator now reports on ppFactory:

The interface 'IDXGIFactory6' does not specify 'ComInterfaceOptions.ComObjectWrapper' so no COM object wrapper (RCW) is generated. Marshalling this interface from unmanaged code to managed code will not work.

Changes

  • NativeMarshallingAttributeInfo — added two init bool properties, SupportsManagedToUnmanagedMarshalling and SupportsUnmanagedToManagedMarshalling, both defaulting to true (no behavior change for other marshallers). Inherited by IidParameterIndexNativeMarshallingInfo and NativeLinearCollectionMarshallingInfo.
  • ComInterfaceMarshallingInfoProvider — reads [GeneratedComInterface(Options = …)] off the interface symbol and sets the two flags. Options is read as a raw int (0x1 = ManagedObjectWrapper/CCW, 0x2 = ComObjectWrapper/RCW) to avoid a shared-project dependency on the ComInterfaceOptions enum type owned by ComInterfaceGenerator.
  • AttributedMarshallingModelGeneratorResolver.CreateCustomNativeTypeMarshaller — validates the resolved MarshalDirection against the flags and returns ResolvedGenerator.NotSupported with a targeted message when a required direction isn't supported. Fast-paths the common "both supported" case.
  • Resources — two new strings in Common/Resources/Strings.resx (auto-propagated to xlf files) naming the missing wrapper option.

Diagnostic behavior

Direction of use Required option Emitted when option is missing
managed → unmanaged (by-value / in param on LibraryImport; return on a virtual COM method; ref) ManagedObjectWrapper (CCW) SYSLIB1051 "…does not specify 'ComInterfaceOptions.ManagedObjectWrapper'…"
unmanaged → managed (return / out on LibraryImport; by-value / in param on a virtual COM method; ref) ComObjectWrapper (RCW) SYSLIB1051 "…does not specify 'ComInterfaceOptions.ComObjectWrapper'…"

Tests

  • LibraryImportGenerator.UnitTests/CompileFails.cs — two new [Fact] tests covering in/out/ref/return positions with the two restricted-option interfaces, plus a CodeSnippets.GeneratedComInterfaceWithOptions helper.
  • ComInterfaceGenerator.Unit.Tests/CompileFails.cs — two new [Fact] tests covering the corresponding cases for virtual COM methods (return-value and parameter positions).

Note

This PR description is AI/Copilot-generated.

…W options

When a method marshals a `[GeneratedComInterface]`-attributed type in a direction its `ComInterfaceOptions` cannot support, the generated marshalling code silently uses `ComInterfaceMarshaller<T>.ConvertToUnmanaged` (needs CCW = ManagedObjectWrapper) or `ConvertToManaged` (needs RCW = ComObjectWrapper), and fails at runtime.

Emit `SYSLIB1051` (Parameter/ReturnTypeNotSupportedWithDetails) with a message naming the missing wrapper option, so users see the problem at build time.

- `NativeMarshallingAttributeInfo` grows two boolean init properties (`SupportsManagedToUnmanagedMarshalling`, `SupportsUnmanagedToManagedMarshalling`) defaulting to `true` — no behavior change for other marshallers.
- `ComInterfaceMarshallingInfoProvider` reads `[GeneratedComInterface(Options = ...)]` off the interface symbol and sets the flags accordingly. Options are read as `int` to avoid a shared-project dependency on the `ComInterfaceOptions` enum type owned by ComInterfaceGenerator.
- `AttributedMarshallingModelGeneratorResolver.CreateCustomNativeTypeMarshaller` validates the resolved `MarshalDirection` against the flags and returns `ResolvedGenerator.NotSupported` when a required direction isn't supported.
- Tests exercise `in`/`out`/`ref`/return positions on `LibraryImport` and on `[GeneratedComInterface]` VtableIndex members.

Closes #92222.

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 1, 2026 20:28
Copilot AI changed the title [WIP] Add warning for LibraryImport with GeneratedComInterface Diagnose LibraryImport/[GeneratedComInterface] with restricted CCW/RCW options Jul 1, 2026
Copilot AI requested a review from AaronRobinsonMSFT July 1, 2026 20:29
@AaronRobinsonMSFT

Copy link
Copy Markdown
Member

@copilot Please address the feedback.

…efault

Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
Comment on lines +66 to +70
Marshallers: marshallers.Value)
{
SupportsManagedToUnmanagedMarshalling = supportsManagedObjectWrapper,
SupportsUnmanagedToManagedMarshalling = supportsComObjectWrapper,
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Instead of introducing new SupportsManagedToUnmanagedMarshalling and SupportsUnmanagedToManagedMarshalling properties, can we instead generate a new CustomTypeMarshallers value that only contains the supported modes?

Ie, instead of setting SupportsManagedToUnmanagedMarshalling = true and SupportsUnmanagedToManagedMarshalling = false, we create a new CustomTypeMarshallers instance that only has marshallers for ManagedToUnmanagedIn and UnmanagedToManagedOut and the correct element marshallers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f8878fb. ComInterfaceMarshallingInfoProvider now filters the CustomTypeMarshallers dictionary based on the interface's ComInterfaceOptions — CCW-only interfaces retain only {ManagedToUnmanagedIn, UnmanagedToManagedOut, ElementIn} and RCW-only interfaces retain only {ManagedToUnmanagedOut, UnmanagedToManagedIn, ElementOut} (the Default fallback is dropped when either wrapper is missing). The existing ValidateCustomNativeTypeMarshallingSupported in AttributedMarshallingModelGeneratorResolver then emits ManagedToUnmanagedMissingRequiredMarshaller / UnmanagedToManagedMissingRequiredMarshaller / BidirectionalMissingRequiredMarshaller for unsupported directions. The SupportsManagedToUnmanagedMarshalling/SupportsUnmanagedToManagedMarshalling properties and the two custom SR strings have been removed. Tests updated accordingly.

Note

AI/Copilot-generated reply.

…using new flag properties

Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 22:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because there is no eligible user to bill. To allow Copilot reviews on bot-authored pull requests, enable direct organization billing in your organization's Copilot settings.

Copilot AI review requested due to automatic review settings July 24, 2026 18:17
@jkoritzinsky
jkoritzinsky marked this pull request as ready for review July 24, 2026 18:18
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment on lines +47 to +54
// Filter the available marshallers based on the ComInterfaceOptions declared on the interface.
// A [GeneratedComInterface] that only specifies ManagedObjectWrapper (CCW) supports data flow
// in the managed-to-unmanaged direction, and one that only specifies ComObjectWrapper (RCW)
// supports the unmanaged-to-managed direction. Restricting the CustomTypeMarshallers dictionary
// to only the modes that match the supported direction lets the shared resolver machinery
// (AttributedMarshallingModelGeneratorResolver) surface a build-time diagnostic when a caller
// marshals in an unsupported direction.
CustomTypeMarshallers filteredMarshallers = FilterMarshallersByComInterfaceOptions(marshallers.Value, interfaceType);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, please enhance the CustomTypeMarshallers type to allow usages to specify a specific error string for when marshalling is not supported in a particular direction for a particular scenario.

Then, update the following string IDs to allow a more specific reason to be appended at the end by adding {1} at the end of the strings: UnmanagedToManagedMissingRequiredMarshaller, BidirectionalMissingRequiredMarshaller, ManagedToUnmanagedMissingRequiredMarshaller.

Finally, update ValidateCustomNativeTypeMarshallingSupported to pass any custom reason (or an empty string if no custom reason exists) to the SR.Format calls.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the latest commit. Here's a summary of what was changed:

  1. CustomTypeMarshallers — added string? UnsupportedReason = null as a second primary constructor parameter; updated Equals and GetHashCode to include it.

  2. Resource strings — updated BidirectionalMissingRequiredMarshaller, ManagedToUnmanagedMissingRequiredMarshaller, and UnmanagedToManagedMissingRequiredMarshaller to end with {1}. Added two new strings ComObjectWrapperNotSpecifiedReason and ManagedObjectWrapperNotSpecifiedReason (each starting with a space for natural sentence concatenation).

  3. ValidateCustomNativeTypeMarshallingSupported — extracts marshalInfo.Marshallers.UnsupportedReason ?? "" and passes it as {1} to all three SR.Format calls.

  4. ComInterfaceMarshallingInfoProviderFilterMarshallersByComInterfaceOptions now passes the COM-specific reason string when constructing the filtered CustomTypeMarshallers.

  5. Tests — all SR.Format call sites updated to pass a second argument ("" for generic cases, interface-specific reason for COM restriction tests).

Note

AI/Copilot-generated reply.

…ostic strings with {1}

Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 24, 2026 19:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because there is no eligible user to bill. To allow Copilot reviews on bot-authored pull requests, enable direct organization billing in your organization's Copilot settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

No warning for [LibraryImport] returning a [GeneratedComInterface] with CCW options

4 participants