-
Notifications
You must be signed in to change notification settings - Fork 123
Add fallback logic for specialized RCW marshalling for unsealed types #2221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add fallback logic for specialized RCW marshalling for unsealed types #2221
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds a fallback path to produce specialized RCWs for unsealed/interface types when runtime class name resolution is unavailable or insufficient, instead of always returning an opaque IInspectable wrapper.
Changes:
- Update
WindowsRuntimeComWrappers.CreateObjectto fall back to the unsealed callback’sCreateObjectwhen no derived type can be resolved (including whenGetRuntimeClassNamefails). - Extend
IWindowsRuntimeUnsealedObjectComWrappersCallback/helpers with a newCreateObjectentrypoint and implement it for several built-in ABI callbacks. - Update the interop generator to emit and reference the new
CreateObjectmember for unsealed callbacks.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/WinRT.Runtime2/WindowsRuntimeObject.cs | Changes initialization of the cached InspectableObjectReference setter to support object initialization. |
| src/WinRT.Runtime2/InteropServices/WindowsRuntimeComWrappers.cs | Adds fallback RCW creation via unsealed callback when runtime class name-based resolution fails. |
| src/WinRT.Runtime2/InteropServices/Callbacks/WindowsRuntimeUnsealedObjectComWrappersCallback.cs | Adds abstract CreateObject forwarding method for unsealed callback hosts. |
| src/WinRT.Runtime2/InteropServices/Callbacks/IWindowsRuntimeUnsealedObjectComWrappersCallback.cs | Adds new static abstract CreateObject API for fallback wrapper creation. |
| src/WinRT.Runtime2/ABI/Windows.Foundation/IAsyncAction.cs | Implements CreateObject for the unsealed callback path. |
| src/WinRT.Runtime2/ABI/System/Collections/IList.cs | Implements CreateObject for the unsealed callback path. |
| src/WinRT.Runtime2/ABI/System/Collections/IEnumerator.cs | Implements CreateObject for the unsealed callback path. |
| src/WinRT.Runtime2/ABI/System/Collections/IEnumerable.cs | Implements CreateObject for the unsealed callback path. |
| src/WinRT.Interop.Generator/References/InteropReferences.cs | Adds a generator reference for IWindowsRuntimeUnsealedObjectComWrappersCallback.CreateObject. |
| src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.cs | Emits and implements the generated CreateObject method for unsealed callback types. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/WinRT.Runtime2/InteropServices/WindowsRuntimeComWrappers.cs
Outdated
Show resolved
Hide resolved
...WinRT.Runtime2/InteropServices/Callbacks/IWindowsRuntimeUnsealedObjectComWrappersCallback.cs
Show resolved
Hide resolved
src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.cs
Outdated
Show resolved
Hide resolved
src/WinRT.Runtime2/InteropServices/WindowsRuntimeComWrappers.cs
Outdated
Show resolved
Hide resolved
Introduce a CreateObject method for unsealed Windows Runtime object callbacks. Added a static abstract CreateObject(void* value, out CreatedWrapperFlags) to IWindowsRuntimeUnsealedObjectComWrappersCallback with XML docs explaining it's used when TryCreateObject fails and mirrors IWindowsRuntimeObjectComWrappersCallback.CreateObject. Updated WindowsRuntimeUnsealedObjectComWrappersCallback to declare the abstract CreateObject and implemented the host to forward calls to the generic TCallback.CreateObject. This enables creating managed RCWs for unsealed/native objects when no more specific wrapper is resolved.
Implement static CreateObject factory methods in IEnumerable, IEnumerator, IList and IAsyncAction ABI callback classes. Each method uses WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe with the appropriate IID (IBindableIterable, IBindableIterator, IBindableVector, IAsyncAction), returns the corresponding managed wrapper (WindowsRuntimeEnumerable, WindowsRuntimeEnumerator, WindowsRuntimeList, WindowsRuntimeAsyncAction) and outputs CreatedWrapperFlags. This allows ComWrappers to create proper managed wrappers for external WinRT COM objects.
Replace the property setter that used Interlocked.CompareExchange with an init accessor for _inspectableObjectReference. This simplifies initialization by making the reference assignable only during object initialization (via init), removing the previous CAS-based setter. The change assumes initialization-time assignment semantics and removes the runtime interlocked operation.
Refactor WindowsRuntimeComWrappers to handle runtime class name retrieval and unsealed type callbacks more robustly. The code now checks for GetRuntimeClassName success and, when available, converts the HSTRING, invokes UnsealedObjectComWrappersCallback.TryCreateObject, and then walks most-derived marshalling info via WindowsRuntimeMarshallingInfo to create the appropriate managed wrapper. HSTRING is freed in a finally block. If no derived/recognized type is found, the code falls back to calling the unsealed-type callback (if present) and only returns an opaque IInspectable wrapper as the last resort. Added explanatory comments and streamlined return paths.
Define and implement the CreateObject callback used by IWindowsRuntimeUnsealedObjectComWrappersCallback. Adds a static CreateObject method on the generated callback type (signature: object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags)) that invokes WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe and constructs a NativeObject, and registers it as the method implementation. Also adds a MemberReference for the CreateObject method in InteropReferences so the signature can be referenced during generation. This enables unsealed object creation support for the COM wrappers pipeline.
Update the method summary comment to reference 'CreateObject' instead of 'TryCreateObject', aligning the comment with the MethodDefinition in InteropTypeDefinitionBuilder. This is a documentation-only change with no behavioral impact.
Update XML remarks for IWindowsRuntimeUnsealedObjectComWrappersCallback.CreateObject to note the method can also be called if IInspectableVftbl.GetRuntimeClassNameUnsafe fails. This is a documentation-only clarification in src/WinRT.Runtime2/InteropServices/Callbacks/IWindowsRuntimeUnsealedObjectComWrappersCallback.cs and does not change runtime behavior.
Reformatted a long explanatory comment in src/WinRT.Runtime2/InteropServices/WindowsRuntimeComWrappers.cs for improved readability and fixed a typo (removed duplicated "with" -> "without"). No functional code changes; comment clarifies the fallback unsealed-type callback behavior used to produce specialized RCWs and avoid extra dynamic interface casts.
3112c33 to
fd50210
Compare
* Unit test and interop fixes * Fix issue where non generic enumerator fails to have ccw created due to internal type * Fix IAsyncInfo ABI getters; tidy signature call Correct the IAsyncInfo COM ABI getter implementations to match the Windows docs and expected signatures: rename and reorder get_Id, get_Status and get_ErrorCode methods, fix parameter types and null checks, and marshal ErrorCode via ExceptionMarshaller.ConvertToUnmanaged. Also simplify a MethodSignature.CreateInstance call in InteropTypeDefinitionBuilder by removing an explicit empty parameter list. These changes ensure correct vtable layout, proper pointer validation, and correct marshaling of the error code. * Implement IMarshal * add GUID attribute to IDIC types * Fix missing function when marshaling as object * Fix OOPExe * Fallback when GetRuntimeClassName isn't implemented * Add blank lines before returns in IMarshalImpl Insert blank lines for readability before the `return WellKnownErrorCodes.S_OK;` statements in several try blocks of src/WinRT.Runtime2/InteropServices/ProjectionImpls/IMarshalImpl.cs. No behavioral changes — purely formatting to improve visual separation between the marshaler calls and their success returns. * Validate out pointers in IMarshalImpl Add null checks for out pointer parameters in IMarshalImpl to avoid dereferencing null and to return WellKnownErrorCodes.E_POINTER early. Updated methods: GetUnmarshalClass (pCid), GetMarshalSizeMax (pSize), and UnmarshalInterface (ppv). Each check returns E_POINTER if the caller passed a null out pointer, and existing default/initialization of out values is preserved before the try block. * Remove redundant 'Add and implement' comments Removed unnecessary inline comments that prefaced .Methods.Add calls to reduce noise and improve code readability. Changes affect InteropTypeDefinitionBuilder.SzArray.cs, InteropTypeDefinitionBuilder.UserDefinedType.cs, InteropTypeDefinitionBuilder.cs, and InteropTypeDefinitionFactory.IReadOnlyCollectionKeyValuePair2.cs. * Rename IBindableIteratorAdapter class Rename IBindableIteratorAdapter to BindableIEnumeratorAdapter and update usages. The file was renamed, the constructor and GetInstance factory were updated, the ConditionalWeakTable now maps to BindableIEnumeratorAdapter, and IEnumeratorImpl calls were changed to use the new adapter (Current, HasCurrent, MoveNext). This clarifies naming to match IEnumerator semantics. * Add bindable IEnumerable adapters & unwrap logic Add bindable adapters and helper methods for marshaling System.Collections.IEnumerable/IEnumerator to IBindableIterable/IBindableIterator. New files BindableIEnumerableAdapter.cs and BindableIEnumerableMethods.cs provide the unmanaged marshaling/First/GetEnumerator logic. Refactor IEnumerable plumbing to call BindableIEnumerableMethods and remove older private adapter code from ABI/System/Collections/IEnumerable.cs. Update WindowsRuntimeEnumerable and WindowsRuntimeList to use the new methods. Enhance BindableIEnumeratorAdapter: implement IEnumerator<object> and IEnumeratorAdapter, expose Enumerator, adjust construction/unwrapping logic and introduce a ConditionalWeakTable and factory helper. Also update IEnumerableMethods (Collections) to properly unwrap nested IEnumeratorAdapter<object> and related cases. Minor using/pragma cleanups in IDictionaryMethods and IEnumerable ABI file. * Replace AddMethodImplementation with Methods.Add Simplify marshaller method registration by using Methods.Add instead of AddMethodImplementation and remove redundant inline comments. Updated InteropTypeDefinitionBuilder.Delegate.cs and InteropTypeDefinitionBuilder.KeyValuePair.cs to add ComputeVtables, GetOrCreateComInterfaceForObject, and CreateObject via Methods.Add and clean up duplicate explanatory comments. This makes method wiring more consistent and reduces noisy comment clutter. * Add IEnumerableMethods shim and update callers Introduce ABI.System.Collections.IEnumerableMethods as an interop shim for System.Collections.IEnumerable.GetEnumerator (marked Obsolete and EditorBrowsable(Never)) to centralize the ABI-facing entry point. Update WindowsRuntimeEnumerable, WindowsRuntimeList and the internal interface implementation to call the new IEnumerableMethods.GetEnumerator instead of BindableIEnumerableMethods.GetEnumerator. * Document opaque IInspectable fallback Expand comment explaining fallback when GetRuntimeClassName fails: treat the object as an opaque IInspectable and note scenarios where this occurs (e.g. MemoryBuffer.CreateReference). Clarifies caller expectations (consuming projected interfaces via dynamic interface casts) and adds a small formatting tweak before the return. * Fix typo in comment 'tirmming' -> 'trimming' Correct a spelling mistake in BindableIEnumerableAdapter.cs comment: 'tirmming' was changed to 'trimming'. The comment explains that trimming will automatically keep code for the constructed IEnumerator<object> if the code path is reachable. * Use IEnumerableMethods.GetEnumerator in collections Replace direct calls to TIIterableMethods.First(...) with IEnumerableMethods<T>.GetEnumerator<TIIterableMethods>(...) across WindowsRuntime collection types for consistent enumerator retrieval. Affected classes: WindowsRuntimeDictionary, WindowsRuntimeEnumerable, WindowsRuntimeList, WindowsRuntimeObservableMap, WindowsRuntimeObservableVector, WindowsRuntimeReadOnlyDictionary, and WindowsRuntimeReadOnlyList. This centralizes enumerator creation and improves consistency for different iterable method implementations. * Expose and track generic collection Enumerator types Add support for nested Enumerator generic types used by dictionary collections. InteropTypeDiscovery now calls TryTrackGenericTypeInstance for DictionaryKeyCollection/ValueCollection and ReadOnlyDictionaryKey/ValueCollection .Enumerator generic instances so the generator can discover those types. InteropReferences exposes TypeReference properties for each nested Enumerator (DictionaryKeyCollection2Enumerator, DictionaryValueCollection2Enumerator, ReadOnlyDictionaryKeyCollection2Enumerator, ReadOnlyDictionaryValueCollection2Enumerator). To enable resolution of those nested types at runtime, the nested Enumerator classes in DictionaryKeyCollection, DictionaryValueCollection, ReadOnlyDictionaryKeyCollection and ReadOnlyDictionaryValueCollection were changed from private to public and annotated with Obsolete (using WindowsRuntimeConstants private-implementation diagnostic values) and EditorBrowsableState.Never to indicate they are private implementation details. * Rename WindowsRuntimeIterator to WindowsRuntimeEnumerator Rename WindowsRuntimeIterator to WindowsRuntimeEnumerator (class and file) to better reflect its role as an IEnumerator wrapper. Update references in src/WinRT.Runtime2/ABI/System/Collections/IEnumerator.cs to construct WindowsRuntimeEnumerator and adjust the class declaration, constructor, and XML docs accordingly. * Fix test code * Make enum arrays blittable given we only use int and uint as underlying type and are using fixed expressions. (#2222) * Handle null interface in CreateObject path Handle a rare null-interfacePointer case (e.g. WeakReference<T> rehydration) by disallowing static callbacks, QueryInterface-ing for IInspectable, and releasing the acquired pointer on exit. Reorganize CreateObject logic: move runtime class name retrieval and HSTRING lifetime into a try/finally, attempt object creation via ObjectComWrappersCallback/UnsealedObjectComWrappersCallback, walk most-derived marshalling info, and fall back to an opaque WindowsRuntimeInspectable. Add WindowsRuntimeComWrappersExceptions.ThrowInvalidOperationException helper (with DoesNotReturn/StackTraceHidden) and add Diagnostics usings. * Add fallback logic for specialized RCW marshalling for unsealed types (#2221) * Add CreateObject API for unsealed RCW Introduce a CreateObject method for unsealed Windows Runtime object callbacks. Added a static abstract CreateObject(void* value, out CreatedWrapperFlags) to IWindowsRuntimeUnsealedObjectComWrappersCallback with XML docs explaining it's used when TryCreateObject fails and mirrors IWindowsRuntimeObjectComWrappersCallback.CreateObject. Updated WindowsRuntimeUnsealedObjectComWrappersCallback to declare the abstract CreateObject and implemented the host to forward calls to the generic TCallback.CreateObject. This enables creating managed RCWs for unsealed/native objects when no more specific wrapper is resolved. * Add CreateObject methods for WinRT ABIs Implement static CreateObject factory methods in IEnumerable, IEnumerator, IList and IAsyncAction ABI callback classes. Each method uses WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe with the appropriate IID (IBindableIterable, IBindableIterator, IBindableVector, IAsyncAction), returns the corresponding managed wrapper (WindowsRuntimeEnumerable, WindowsRuntimeEnumerator, WindowsRuntimeList, WindowsRuntimeAsyncAction) and outputs CreatedWrapperFlags. This allows ComWrappers to create proper managed wrappers for external WinRT COM objects. * Make InspectableObjectReference init-only Replace the property setter that used Interlocked.CompareExchange with an init accessor for _inspectableObjectReference. This simplifies initialization by making the reference assignable only during object initialization (via init), removing the previous CAS-based setter. The change assumes initialization-time assignment semantics and removes the runtime interlocked operation. * Improve GetRuntimeClassName handling in ComWrappers Refactor WindowsRuntimeComWrappers to handle runtime class name retrieval and unsealed type callbacks more robustly. The code now checks for GetRuntimeClassName success and, when available, converts the HSTRING, invokes UnsealedObjectComWrappersCallback.TryCreateObject, and then walks most-derived marshalling info via WindowsRuntimeMarshallingInfo to create the appropriate managed wrapper. HSTRING is freed in a finally block. If no derived/recognized type is found, the code falls back to calling the unsealed-type callback (if present) and only returns an opaque IInspectable wrapper as the last resort. Added explanatory comments and streamlined return paths. * Add CreateObject callback and reference Define and implement the CreateObject callback used by IWindowsRuntimeUnsealedObjectComWrappersCallback. Adds a static CreateObject method on the generated callback type (signature: object CreateObject(void* value, out CreatedWrapperFlags wrapperFlags)) that invokes WindowsRuntimeComWrappersMarshal.CreateObjectReferenceUnsafe and constructs a NativeObject, and registers it as the method implementation. Also adds a MemberReference for the CreateObject method in InteropReferences so the signature can be referenced during generation. This enables unsealed object creation support for the COM wrappers pipeline. * Correct comment to match CreateObject method Update the method summary comment to reference 'CreateObject' instead of 'TryCreateObject', aligning the comment with the MethodDefinition in InteropTypeDefinitionBuilder. This is a documentation-only change with no behavioral impact. * Document fallback when GetRuntimeClassNameUnsafe fails Update XML remarks for IWindowsRuntimeUnsealedObjectComWrappersCallback.CreateObject to note the method can also be called if IInspectableVftbl.GetRuntimeClassNameUnsafe fails. This is a documentation-only clarification in src/WinRT.Runtime2/InteropServices/Callbacks/IWindowsRuntimeUnsealedObjectComWrappersCallback.cs and does not change runtime behavior. * Reflow comments and fix typo in ComWrappers Reformatted a long explanatory comment in src/WinRT.Runtime2/InteropServices/WindowsRuntimeComWrappers.cs for improved readability and fixed a typo (removed duplicated "with" -> "without"). No functional code changes; comment clarifies the fallback unsealed-type callback behavior used to produce specialized RCWs and avoid extra dynamic interface casts. * Fix unsealed callback in codewriters * Add test for non projected class * Fix default value --------- Co-authored-by: Manodasan Wignarajah <mawign@microsoft.com> --------- Co-authored-by: Sergio Pedri <sergio0694@live.com>
Title. Best effort before returning just an opaque inspectable.