Skip to content

Bug: CsWinRT 3 undetermined-interface case relies on undefined COM behavior #2483

Description

@dongle-the-gadget

Description

When CsWinRT could not determine which interface to use in the marshaller, it falls back to using IInspectable (see code below). However, performing a QueryInterface to IInspectable is undefined behavior.

// As a last resort, if we couldn't find any partially derived type to marshal and we also had no static type
// information at all, we just return an opaque object. It can still be used via interfaces (including generics)
// by doing 'IDynamicInterfaceCastable' casts on it. It will still work the same, just with lower performance.
WindowsRuntimeObjectReference objectReference = WindowsRuntimeComWrappersMarshal.CreateObjectReference(
externalComObject: interfacePointer,
iid: in WellKnownWindowsInterfaceIIDs.IID_IInspectable,
wrapperFlags: out wrapperFlags);

Steps To Reproduce

[This sample assumes that you have OneDrive installed with an account signed in and the machine runs Windows 11 version 24H2 or higher]

  1. Go to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager
  2. Find subkeys with the prefix OneDrive!, and copy the subkey name for any one of them.
  3. Create a console project, using CsWinRT 3 and enabling unsafe code
  4. Paste the following code in, replacing the sri variable with the subkey name copied above.
using Microsoft.Win32;
using System.Runtime.InteropServices;
using Windows.Storage.Provider;
using WindowsRuntime.InteropServices;

string sri = "[subkey name]";

using var key = Registry.LocalMachine.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SyncRootManager\\{sri}")!;
Guid factoryClsid = Guid.Parse((string)key.GetValue("StorageProviderStatusUISourceFactory")!);

IStorageProviderStatusUISourceFactory factory;

unsafe
{
    Marshal.ThrowExceptionForHR(PInvoke.CoCreateInstance(factoryClsid, null, 0x4, typeof(IStorageProviderStatusUISourceFactory).GUID, out void* ppv));
    factory = (IStorageProviderStatusUISourceFactory)WindowsRuntimeMarshal.ConvertToManaged(ppv)!;
    var statusUISource = factory.GetStatusUISource(sri);
    Console.WriteLine(statusUISource.GetStatusUI().QuotaUI.QuotaUsedLabel);
}

static partial class PInvoke
{
    [LibraryImport("combase")]
    public static unsafe partial int CoCreateInstance(
        in Guid rclsid,
        void* pUnkOuter,
        uint dwClsContext,
        in Guid riid,
        out void* ppv);
}
  1. Run the project, observe InvalidCastException at the statusUISource line.

Expected Behavior

The console should print the storage usage.

Version Info

CsWinRT 3.0.0-preview.260319.2 (latest on NuGet)

Additional Context

This is essentially the same issue as microsoft/CsWin32#1716, but for WinRT

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions