Two verified limitations in the pinned Windows runtime's .NET bridge, blocking any JS-side use of APIs that need managed interop — most concretely, WinRT pickers that require IInitializeWithWindow owner HWND setup.
Real-world impact: Octane Xplat's files.pick/pickMultiple/export on Windows needed a fully managed workaround (app-compiled helper + DispatcherQueue enqueue) because neither projected WinRT objects nor async picker calls can cross the bridge. Verified on packaged MSIX 1.0.0.224–227, runtime @nativescript/[email protected].
1. Projected WinRT objects marshal as System.String, never RCWs
Every candidate carrier handed to dotnet.invoke arrives in managed code as System.String:
- the picker proxy itself →
System.String
picker.handle, interop.handleOf(picker).handle → Cannot convert object to primitive value
dotnet.wrap(picker), interop.pointer(picker) → System.String
ServiceInterop.Describe (a managed helper taking object) reports System.String|IsInitWithWindow=False, so neither a ComImport IInitializeWithWindow cast nor WinRT.Interop.InitializeWithWindow can run — every call fails InvalidCastException: Failed to create a CCW for object of type 'System.String'. The bridge's CoerceBin has a WinRtRef path (tag 0x0A → Marshal.GetObjectForIUnknown) but no JS value appears to serialize to it.
2. Task-returning methods are awaited on the caller thread
dotnet.invoke on a Task<T>-returning method blocks the JS/UI thread until the task completes (AwaitIfTask in Bridge.BinaryDispatch.cs). For a modal picker this is a guaranteed deadlock: the dialog needs the same thread's message pump. Verified deterministically — a Task<string> PickSingle helper hung the packaged app on two consecutive runs; enqueueing the same work on the captured DispatcherQueue and polling a string state works.
Workaround shipped: an app-compiled ServiceInterop helper does construct + InitializeWithWindow + await in-process on the dispatcher; JS only passes long/string and polls PickState. Packaged evidence: a real FileOpenPicker/FileSavePicker dialog opened, accepted a typed-path selection, and committed a save — while every JS-side picker invoke still rejects WinRT async operation failed.