microsoft / CsWinRT

C# language projection for the Windows Runtime
MIT License
547 stars 104 forks source link

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

Closed Sergio0694 closed 1 year ago

Sergio0694 commented 1 year ago

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):
```csharp 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.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.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])lpVtbl[0])((DummyUnknownAbi*)Unsafe.AsPointer(ref this), riid, ppvObject); } public uint AddRef() { return ((delegate* unmanaged[Stdcall])lpVtbl[1])((DummyUnknownAbi*)Unsafe.AsPointer(ref this)); } public uint Release() { return ((delegate* unmanaged[Stdcall])lpVtbl[2])((DummyUnknownAbi*)Unsafe.AsPointer(ref this)); } public int GetNumber() { return ((delegate* unmanaged[Stdcall])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] GetNumber; [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] private static int GetNumberFromAbi(IntPtr thisPtr) { try { return ComWrappersSupport.FindObject(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-

Sergio0694 commented 1 year ago

cc. @AaronRobinsonMSFT @jkoritzinsky as this might potentially be related to ComWrappers. cc. @MichalStrehovsky as this is NAOT-related, maybe the output there gives you any ideas?

Sergio0694 commented 1 year ago

This was the same issue (just with a slightly different observable result) as https://github.com/dotnet/runtime/issues/84908. Fixed by https://github.com/dotnet/runtime/pull/85087 🎉