Add CoRegisterClassObject regression tests for #1670 (no projection change)#1692
Closed
jevansaks wants to merge 1 commit into
Closed
Add CoRegisterClassObject regression tests for #1670 (no projection change)#1692jevansaks wants to merge 1 commit into
jevansaks wants to merge 1 commit into
Conversation
Issue #1670 reported that `PInvoke.CoRegisterClassObject` did not register their class factory under NativeAOT + DisableRuntimeMarshalling. Investigation shows this is a usage error, not a CsWin32 bug: * The reporter pre-marshalled their factory via `ComWrappers.GetOrCreateComInterfaceForObject` and then passed the resulting `nint` to the generated PInvoke. Because the parameter is `[MarshalAs(UnmanagedType.Interface)] object`, the boxed `IntPtr` is then wrapped *again* by `ComInterfaceMarshaller<object>`, so what gets registered with COM is a wrapper around an `IntPtr` rather than the real `IClassFactory`. `CoCreateInstance` then fails to invoke the factory. * Passing the managed factory object directly to the same parameter works correctly: the source-generated marshaller calls ComWrappers under the hood and registers the right IUnknown. These tests document both behaviors so future regressions are caught: * `RegisteringManagedFactory_InvokesCreateInstance` -- happy path; verifies that the cswin32-generated PInvoke registers a managed `[GeneratedComClass]` factory and `CoCreateInstance` invokes its `CreateInstance`. * `PassingRawPointerAsObject_DoesNotRegisterFactory` -- documents the `[MarshalAs(UnmanagedType.Interface)]` semantics: passing a pre-marshalled `nint` boxed as `object` does not register the original factory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6e7b7b8 to
7b94e3f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Investigates #1670. Conclusion: not a CsWin32 bug — the issue is a usage error. This PR adds two regression tests that document the correct vs incorrect call pattern; no source-generator changes.
What the filer did
The cswin32-generated parameter is
[MarshalAs(UnmanagedType.Interface)] object pUnk. Passing thenintboxes it asSystem.IntPtr; the LibraryImport-generatedComInterfaceMarshaller<object>then callsComWrappersagain to wrap that boxedIntPtras a fresh COM object. What gets registered with COM is a wrapper around anIntPtr, not the originalIClassFactory. SCM's QueryInterface forIClassFactorythen fails, soCoCreateInstance(or out-of-proc activation) never invokes the factory.What they should do
Pass the managed factory directly — let the source-generated marshaller do the ComWrappers call for you:
(
ThumbnailProviderFactorymust be a[GeneratedComClass]implementing a[GeneratedComInterface]forIClassFactory, which they already had since their hand-written workaround proved their ComWrappers setup was sound.)If they want explicit control over raw COM pointers (e.g. to use a specific ComWrappers strategy), they can set
"allowMarshaling": falseinNativeMethods.json, which projectsIUnknown*parameters asvoid*and lets them pass thenintdirectly via(void*)factoryPunk.Tests added
Both run on a dedicated STA thread under the existing
useComSourceGenerators: true+DisableRuntimeMarshalling=truesetup (matches the filer's environment).RegisteringManagedFactory_InvokesCreateInstance— happy path. Passes the managed[GeneratedComClass] TestClassFactorydirectly toPInvoke.CoRegisterClassObjectand verifiesCoCreateInstanceinvokesCreateInstance.PassingRawPointerAsObject_DoesNotRegisterFactory— documents the broken pattern from the issue. Pre-marshals via ComWrappers, passes the resultingnint, and asserts the factory is never invoked. This locks in the documented behavior so we notice if it ever changes.Validation
GenerationSandbox.BuildTask.Testsstill pass