-
Notifications
You must be signed in to change notification settings - Fork 124
Description
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.