Skip to content

ComWrappers/CsWinRT code produces different (wrong) results on NativeAOT #1321

@Sergio0694

Description

@Sergio0694

Describe the bug

Opening this as part of an investigation into a NativeAOT crash I noticed in ComputeSharp, which we've been trying to narrow down (as a collaborative effort over in the C# Discord). While trying to put together a minimal repro, I couldn't quite reproduce the same crash (which seems to be something going wrong in ComWrappers on NAOT), but I still noticed what seems to be incorrect behavior on NAOT, so here's a separate issue to track that. Not entirely sure whether this is more an issue with ComWrappers or CsWinRT, so opening it here for now. Will try to create a ComWrappers-only repro too later on to see if it also repros with just that.

To Reproduce

  1. Create a new project with new7-windows10.0.22621 as TFM
  2. Paste the following code:
Code (click to expand):
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WinRT.Interop;
using WinRT;

unsafe
{
    DummyUnknown unknown = new();

    for (int i = 0; i < 10; i++)
    {
        var value2 = MarshalInspectable<object>.CreateMarshaler2(unknown);

        try
        {
            var abi = (IUnknownVftbl*)value2.GetAbi();
            var guid = new Guid("33D8B971-2ABF-4A2B-8071-1FFCBCBC8124");
            DummyUnknownAbi* ppv = null;

            ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface((IntPtr)abi, ref guid, out *(IntPtr*)&ppv));

            Console.WriteLine($"{i}: {ppv->GetNumber()}");

            ppv->Release();
        }
        finally
        {
            MarshalInspectable<object>.DisposeMarshaler(value2);
        }
    }
}

Console.WriteLine("Done!");


public unsafe class DummyUnknown : IDummyUnknown
{
    private int number = 42;

    public int GetNumber()
    {
        return number++;
    }
}

public unsafe struct DummyUnknownAbi
{
    public void** lpVtbl;

    public int QueryInterface(Guid* riid, void** ppvObject)
    {
        return ((delegate* unmanaged[Stdcall]<DummyUnknownAbi*, Guid*, void**, int>)lpVtbl[0])((DummyUnknownAbi*)Unsafe.AsPointer(ref this), riid, ppvObject);
    }

    public uint AddRef()
    {
        return ((delegate* unmanaged[Stdcall]<DummyUnknownAbi*, uint>)lpVtbl[1])((DummyUnknownAbi*)Unsafe.AsPointer(ref this));
    }

    public uint Release()
    {
        return ((delegate* unmanaged[Stdcall]<DummyUnknownAbi*, uint>)lpVtbl[2])((DummyUnknownAbi*)Unsafe.AsPointer(ref this));
    }

    public int GetNumber()
    {
        return ((delegate* unmanaged[Stdcall]<DummyUnknownAbi*, int>)lpVtbl[3])((DummyUnknownAbi*)Unsafe.AsPointer(ref this));
    }
}

[Guid("33D8B971-2ABF-4A2B-8071-1FFCBCBC8124")]
[WindowsRuntimeType]
[WindowsRuntimeHelperType(typeof(IDummyUnknown))]
public interface IDummyUnknown
{
    int GetNumber();

    [Guid("33D8B971-2ABF-4A2B-8071-1FFCBCBC8124")]
    public unsafe struct Vftbl
    {
        public static readonly IntPtr AbiToProjectionVftablePtr = InitVtbl();

        private static IntPtr InitVtbl()
        {
            Vftbl* lpVtbl = (Vftbl*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(Vftbl));

            lpVtbl->IUnknownVftbl = IUnknownVftbl.AbiToProjectionVftbl;
            lpVtbl->GetNumber = &GetNumberFromAbi;

            return (IntPtr)lpVtbl;
        }

        internal IUnknownVftbl IUnknownVftbl;
        internal delegate* unmanaged[Stdcall]<IntPtr, int> GetNumber;

        [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
        private static int GetNumberFromAbi(IntPtr thisPtr)
        {
            try
            {
                return ComWrappersSupport.FindObject<IDummyUnknown>(thisPtr).GetNumber();
            }
            catch (Exception e)
            {
                ExceptionHelpers.SetErrorInfo(e);

                return Marshal.GetHRForException(e);
            }
        }
    }
}
  1. Add <PublishAot>true</PublishAot> to the .csproj file
  2. Publish with NAOT:
msbuild ComWrappersRepro.csproj -t:restore,publish /p:Configuration=Release /p:Platform=x64 /p:RuntimeIdentifier=win10-x64 /p:TreatWarningsAsErrors=False

Expected behavior

0: 42
1: 43
2: 44
3: 45
4: 46
5: 47
6: 48
7: 49
8: 50
9: 51
Done!

This is what you also get with a normal F5 deploy.

Actual behavior

0: 42
1: -2147467261
2: -2147467261
3: -2147467261
4: -2147467261
5: -2147467261
6: -2147467261
7: -2147467261
8: -2147467261
9: -2147467261
Done!

...?!?!?? wha-

Metadata

Metadata

Assignees

No one assigned

    Labels

    AOTbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions