Skip to content

As<I>() method returns invalid pointer for activation factory casts #1326

@Sergio0694

Description

@Sergio0694

Describe the bug

I'm noticing random crashes in my Win2D WASDK Win32 sample, which I've narrowed down to what seems to be invalid use when calling the As<I>() method from CsWinRT to retrieve the activation factory of a given WinRT type and cast it to some type.

To double check, I've tried calling RoGetActivationFactory manually to get the activation factory, which does work just fine (though I can't use it, as it's not guaranteed to work in unpackaged scenarios, as it doesn't have the fallback .dll loading paths). The issue only seems to happen when using As<I>() to cast the activation factory to some interface and then marshalling that.

Note: this is blocking Sergio0694/ComputeSharp#509.

To Reproduce

  1. Create a new project, use this .csproj:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0-windows10.0.22621</TargetFramework>
    <Nullable>enable</Nullable>
    <Platforms>x64</Platforms>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Graphics.Win2D" Version="1.1.0-preview1" />
  </ItemGroup>

</Project>
  1. If needed, configure the platform in the solution to use x64 as CPU architecture
  2. Paste this code:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Graphics.Canvas;
using WinRT;
using WinRT.Interop;

unsafe
{
    ICanvasFactoryNative.Interface canvasDeviceActivationFactory = CanvasDevice.As<ICanvasFactoryNative.Interface>();

    ICanvasFactoryNative* factoryNative = (ICanvasFactoryNative*)MarshalInspectable<ICanvasFactoryNative.Interface>.FromManaged(canvasDeviceActivationFactory);

    int hresult = factoryNative->RegisterWrapper(null, null);

    Debug.Assert(hresult == unchecked((int)0x80070057U));
}

[Guid("695C440D-04B3-4EDD-BFD9-63E51E9F7202")]
internal unsafe struct ICanvasFactoryNative
{
    public void** lpVtbl;

    public int RegisterWrapper(IUnknownVftbl* resource, IInspectable.Vftbl* wrapper)
    {
        return ((delegate* unmanaged[Stdcall]<ICanvasFactoryNative*, IUnknownVftbl*, IInspectable.Vftbl*, int>)this.lpVtbl[7])(
            (ICanvasFactoryNative*)Unsafe.AsPointer(ref this),
            resource,
            wrapper);
    }

    [Guid("695C440D-04B3-4EDD-BFD9-63E51E9F7202")]
    [WindowsRuntimeType]
    [WindowsRuntimeHelperType(typeof(Interface))]
    public interface Interface
    {
        [Guid("695C440D-04B3-4EDD-BFD9-63E51E9F7202")]
        public readonly struct Vftbl
        {
            public static readonly IntPtr AbiToProjectionVftablePtr = IUnknownVftbl.AbiToProjectionVftblPtr;
        }
    }
}
  1. Run the sample

Expected behavior

The code should return E_INVALIDARG (as we're calling the method passing null as params).

Actual behavior
The method will return seemingly random HRESULTs. Debugging the code, it seems some completely unrelated method is being invoked. Sometimes this ends up invoking the destructor of the activation factory, for whatever reason. It seems like CsWinRT is marshalling the activation factory incorrect? Or is my projected interface somehow wrong? 🤔

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