Skip to content

With AOT support, authoring a COM class that derives from WinRT interfaces doesn't work (or causes System.ExecutionEngineException) #1722

@smourier

Description

@smourier

Describe the bug
With AOT, GeneratedComClasses and GeneratedComInterfaces and with runtime marshaling disabled, I'm trying to create a COM class that derives from a C#/WinRT generated interface, for example IGeometrySource2D (it's the same for all interfaces but I tried to choose a simple one) and it doesn't work, it throws:

Unhandled exception. System.ArgumentException: The parameter is incorrect.
Invalid argument to parameter source. Object parameter must not be null.
   at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|38_0(Int32 hr)
   at WinRT.ExceptionHelpers.ThrowExceptionForHR(Int32 hr)

I've tried more complex implementations, for example with ICustomQueryInterface (see Geo2 class below) since it's based on IntPtr instead of .NET objects, but I can't find a way that works.

Note that I need this class to also implement a non WinRT, IUnknown-derived class (here IGeometrySource2DInterop) declared manually.

Is there a way to make this work?

To Reproduce
Here's my .csproj:

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
    <InvariantGlobalization>true</InvariantGlobalization>
    <WindowsSdkPackageVersion>10.0.26100.42</WindowsSdkPackageVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.1.1" />
  </ItemGroup>

</Project>

Here's my Progam.cs:

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Windows.Graphics;
using Windows.UI.Composition;
using WinRT;

[assembly: DisableRuntimeMarshalling]

namespace ConsoleAotAuthor;

internal class Program
{
    static void Main()
    {
        var geo = new Geo(); // or Geo2 (it's worse)
        var compositor = new CompositionPath(geo);
    }
}

[GeneratedComClass]
public partial class Geo : IGeometrySource2D, IGeometrySource2DInterop
{
    // these are never called anyway
    public int GetGeometry(out nint value) => throw new NotImplementedException();
    public int TryGetGeometryUsingFactory(nint factory, out nint value) => throw new NotImplementedException();
}

[GeneratedComClass]
public partial class Geo2 : IGeometrySource2D, IGeometrySource2DInterop, ICustomQueryInterface
{
    // these are never called anyway
    public int GetGeometry(out nint value) => throw new NotImplementedException();
    public int TryGetGeometryUsingFactory(nint factory, out nint value) => throw new NotImplementedException();

    CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out nint ppv)
    {
        if (iid == typeof(IGeometrySource2D).GUID)
        {
            ppv = MarshalInspectable<IGeometrySource2D>.FromManaged(this);
            return CustomQueryInterfaceResult.Handled;
        }
        // what to return here???
        else if (iid == typeof(IGeometrySource2DInterop).GUID)
        {
            ComWrappers.TryGetComInstance(this, out var unk); // unk is 0

            // returning this causes System.ExecutionEngineException "Fatal error. Internal CLR error. (0x80131506)" later on
            ppv = new StrategyBasedComWrappers().GetOrCreateComInterfaceForObject(this, CreateComInterfaceFlags.None);
            return CustomQueryInterfaceResult.Handled;
        }

        ppv = 0;
        return CustomQueryInterfaceResult.NotHandled;
    }
}

// from %ProgramFiles(x86)%\Windows Kits\10\Include\10.0.26100.0\winrt\windows.graphics.interop.h
[GeneratedComInterface, Guid("0657af73-53fd-47cf-84ff-c8492d2a80a3")]
public partial interface IGeometrySource2DInterop
{
    [PreserveSig]
    int GetGeometry(out nint value);

    [PreserveSig]
    int TryGetGeometryUsingFactory(nint factory, out nint value);
}

Expected behavior
It works with older C#/WinRT and w/o AOT, I can do something like

var unk = Marshal.GetIUnknownForObject(this);
return WinRT.MarshalInspectable<Windows.Graphics.IGeometrySource2D>.FromAbi(unk);

But Marshal.GetIUnknownForObject cannot be used with disabled runtime marshaling, that I also require for proper AOT support.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions