Skip to content

Commit

Permalink
Merge pull request #510 from Sergio0694/dev/fix-cswinrt-marshalling
Browse files Browse the repository at this point in the history
Fix WinRT projections marshalling on WASDK
  • Loading branch information
Sergio0694 authored Apr 21, 2023
2 parents 12325f5 + a0fd03a commit d011a42
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/ComputeSharp.D2D1.UI/CanvasEffect.Interop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ unsafe int ICanvasImageInterop.Interface.GetDevice(ICanvasDevice** device, WIN2D
{
using ComPtr<ICanvasImageInterop> canvasImageInterop = default;

RcwMarshaller.GetNativeObject(GetCanvasImage(), canvasImageInterop.GetAddressOf()).Assert();
RcwMarshaller.GetNativeInterface(GetCanvasImage(), canvasImageInterop.GetAddressOf()).Assert();

return canvasImageInterop.Get()->GetDevice(device, type);
}
Expand All @@ -56,7 +56,7 @@ unsafe int ICanvasImageInterop.Interface.GetD2DImage(
{
using ComPtr<ICanvasImageInterop> canvasImageInterop = default;

RcwMarshaller.GetNativeObject(GetCanvasImage(), canvasImageInterop.GetAddressOf()).Assert();
RcwMarshaller.GetNativeInterface(GetCanvasImage(), canvasImageInterop.GetAddressOf()).Assert();

return canvasImageInterop.Get()->GetD2DImage(device, deviceContext, flags, targetDpi, realizeDpi, ppImage);
}
Expand Down
36 changes: 27 additions & 9 deletions src/ComputeSharp.D2D1.UI/Helpers/RcwMarshaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
using System.Diagnostics.CodeAnalysis;
#if WINDOWS_UWP
using System.Runtime.InteropServices;
using ComputeSharp.D2D1.Extensions;
#endif
using TerraFX.Interop.Windows;
#if !WINDOWS_UWP
using WinRT;
#endif
using IInspectable = TerraFX.Interop.WinRT.IInspectable;

#if WINDOWS_UWP
namespace ComputeSharp.D2D1.Uwp.Helpers;
Expand All @@ -17,22 +19,23 @@ namespace ComputeSharp.D2D1.WinUI.Helpers;
/// <summary>
/// A helper type to handle marshalling of RCW instances.
/// </summary>
internal static class RcwMarshaller
internal static unsafe class RcwMarshaller
{
/// <summary>
/// Gets or creates a managed object of a specified type for an input native object.
/// </summary>
/// <typeparam name="T">The interface type to retrieve an instance of.</typeparam>
/// <param name="nativeObject">A pointer to the native object to get a managed wrapper for.</param>
/// <returns>The resulting managed object wrapping <paramref name="nativeObject"/>.</returns>
public static unsafe T GetOrCreateManagedObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] T>(IUnknown* nativeObject)
/// <remarks>This method should only be called with <typeparamref name="T"/> being a projected interface type.</remarks>
public static T GetOrCreateManagedInterface<T>(IUnknown* nativeObject)
where T : class
{
#if WINDOWS_UWP
// On UWP, Marshal.GetObjectForIUnknown handles all the marshalling/wrapping logic
return (T)Marshal.GetObjectForIUnknown((IntPtr)nativeObject);
#else
return MarshalInspectable<T>.FromAbi((IntPtr)nativeObject);
return MarshalInterface<T>.FromAbi((IntPtr)nativeObject);
#endif
}

Expand All @@ -42,15 +45,21 @@ internal static class RcwMarshaller
/// <typeparam name="T">The type of managed object to unwrap.</typeparam>
/// <param name="managedObject">The input RCW instance to unwrap.</param>
/// <param name="nativeObject">A pointer to the resulting native object to retrieve.</param>
public static unsafe void GetNativeObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] T>(T managedObject, IUnknown** nativeObject)
/// <remarks>This method should only be called with <typeparamref name="T"/> being a concrete projected type.</remarks>
public static void GetNativeObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] T>(T managedObject, IInspectable** nativeObject)
where T : class
{
#if WINDOWS_UWP
// On UWP, due to built-in COM/WinRT support, Marshal.GetIUnknownForObject can handle all the logic
*nativeObject = (IUnknown*)Marshal.GetIUnknownForObject(managedObject);
using ComPtr<IUnknown> unknownObject = default;

// On UWP, due to built-in COM/WinRT support, Marshal.GetIUnknownForObject can handle all the logic.
// We get back an IUnknown* pointer, so we need to QueryInterface for IInspectable* ourselves.
unknownObject.Attach((IUnknown*)Marshal.GetIUnknownForObject(managedObject));

unknownObject.CopyTo(nativeObject).Assert();
#else
// On WinUI 3, delegate the RCW unwrapping or CCW creation logic to CsWinRT's APIs
*nativeObject = (IUnknown*)MarshalInspectable<T>.FromManaged(managedObject);
*nativeObject = (IInspectable*)MarshalInspectable<T>.FromManaged(managedObject);
#endif
}

Expand All @@ -62,13 +71,22 @@ internal static class RcwMarshaller
/// <param name="managedObject">The input RCW instance to unwrap.</param>
/// <param name="nativeObject">A pointer to the resulting native object to retrieve.</param>
/// <returns>The <see cref="HRESULT"/> for the operation.</returns>
public static unsafe HRESULT GetNativeObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces)] TFrom, TTo>(TFrom managedObject, TTo** nativeObject)
/// <remarks>This method should only be called with <typeparamref name="TFrom"/> being a projected interface type.</remarks>
public static HRESULT GetNativeInterface<TFrom, TTo>(TFrom managedObject, TTo** nativeObject)
where TFrom : class
where TTo : unmanaged // IUnknown
{
using ComPtr<IUnknown> unknownObject = default;

GetNativeObject(managedObject, unknownObject.GetAddressOf());
#if WINDOWS_UWP
unknownObject.Attach((IUnknown*)Marshal.GetIUnknownForObject(managedObject));

unknownObject.CopyTo(nativeObject).Assert();
#else
// Here we only want to get an IUnknown* pointer for a given interface, and then we'll do
// QueryInterface ourselves to get the target COM type. So MarshalInterface<TFrom> is fine.
unknownObject.Attach((IUnknown*)MarshalInterface<TFrom>.FromManaged(managedObject));
#endif

return unknownObject.CopyTo(nativeObject);
}
Expand Down
6 changes: 3 additions & 3 deletions src/ComputeSharp.D2D1.UI/Helpers/ResourceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static IGraphicsEffectSource GetOrCreate(ICanvasDevice* device, IUnknown*
wrapper: wrapperInspectable.GetAddressOf()).Assert();

// Get the runtime-provided RCW for the resulting WinRT wrapper
return RcwMarshaller.GetOrCreateManagedObject<IGraphicsEffectSource>((IUnknown*)wrapperInspectable.Get());
return RcwMarshaller.GetOrCreateManagedInterface<IGraphicsEffectSource>((IUnknown*)wrapperInspectable.Get());
}

/// <summary>
Expand All @@ -70,7 +70,7 @@ public static IGraphicsEffectSource GetOrCreate(ICanvasDevice* device, IUnknown*
using ComPtr<IInspectable> wrapperInspectable = default;

// Unwrap the input wrapper and get an IInspectable* object
RcwMarshaller.GetNativeObject(wrapper, wrapperInspectable.GetAddressOf()).Assert();
RcwMarshaller.GetNativeObject(wrapper, wrapperInspectable.GetAddressOf());

// Register this pair of native resource and inspectable wrapper
canvasFactoryNative.Get()->RegisterWrapper(resource, wrapperInspectable.Get()).Assert();
Expand Down Expand Up @@ -128,7 +128,7 @@ private static void GetActivationFactory(ICanvasFactoryNative** factoryNative)
// For instance, this will ensure the following call will work fine in unpackaged apps.
ICanvasFactoryNative.Interface canvasDeviceActivationFactory = CanvasDevice.As<ICanvasFactoryNative.Interface>();

*factoryNative = (ICanvasFactoryNative*)MarshalInspectable<ICanvasFactoryNative.Interface>.FromManaged(canvasDeviceActivationFactory);
*factoryNative = (ICanvasFactoryNative*)MarshalInterface<ICanvasFactoryNative.Interface>.FromManaged(canvasDeviceActivationFactory);
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public Rect GetRequiredSourceRectangle(
using ComPtr<ABI.Microsoft.Graphics.Canvas.Effects.ICanvasEffect> canvasEffectAbi = default;

// Get the ABI.Microsoft.Graphics.Canvas.Effects.ICanvasEffect object from the input interface
RcwMarshaller.GetNativeObject(sourceEffect, canvasEffectAbi.GetAddressOf()).Assert();
RcwMarshaller.GetNativeInterface(sourceEffect, canvasEffectAbi.GetAddressOf()).Assert();

Win2D.GetRequiredSourceRectanglesForICanvasImageInterop(
resourceCreator: resourceCreatorWithDpi.Get(),
Expand Down Expand Up @@ -155,7 +155,7 @@ public Rect[] GetRequiredSourceRectangles(
// Get the underlying ABI.Microsoft.Graphics.Canvas.Effects.ICanvasEffect object for each input effect
for (int i = 0; i < sourceEffects.Length; i++)
{
RcwMarshaller.GetNativeObject(sourceEffects[i], canvasEffects[i].GetAddressOf()).Assert();
RcwMarshaller.GetNativeInterface(sourceEffects[i], canvasEffects[i].GetAddressOf()).Assert();
}

Rect[] result = new Rect[sourceEffects.Length];
Expand Down Expand Up @@ -210,9 +210,9 @@ private void GetCanvasResourceCreatorAndCanvasImageInteropUnderlyingObjects(
ICanvasImageInterop** canvasImageInterop)
{
// Get the ABI.Microsoft.Graphics.Canvas.ICanvasResourceCreatorWithDpi object from the input interface
RcwMarshaller.GetNativeObject(resourceCreator, resourceCreatorWithDpi).Assert();
RcwMarshaller.GetNativeInterface(resourceCreator, resourceCreatorWithDpi).Assert();

// Get the ICanvasImageInterop object from the current instance
RcwMarshaller.GetNativeObject(this, canvasImageInterop).Assert();
RcwMarshaller.GetNativeInterface(this, canvasImageInterop).Assert();
}
}
4 changes: 2 additions & 2 deletions src/ComputeSharp.D2D1.UI/PixelShaderEffect{T}.ICanvasImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ private Rect GetBounds(ICanvasResourceCreator resourceCreator, Matrix3x2* transf
using ComPtr<ABI.Microsoft.Graphics.Canvas.ICanvasResourceCreator> resourceCreatorAbi = default;

// Get the ABI.Microsoft.Graphics.Canvas.ICanvasResourceCreator object from the input interface
RcwMarshaller.GetNativeObject(resourceCreator, resourceCreatorAbi.GetAddressOf()).Assert();
RcwMarshaller.GetNativeInterface(resourceCreator, resourceCreatorAbi.GetAddressOf()).Assert();

using ComPtr<ICanvasImageInterop> canvasImageInterop = default;

// Get the ICanvasImageInterop object from the current instance
RcwMarshaller.GetNativeObject(this, canvasImageInterop.GetAddressOf()).Assert();
RcwMarshaller.GetNativeInterface(this, canvasImageInterop.GetAddressOf()).Assert();

Rect bounds;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ private void RefreshInputs(WIN2D_GET_D2D_IMAGE_FLAGS flags, float targetDpi, ID2
using ComPtr<ICanvasImageInterop> canvasImageInterop = default;

// Convert to ICanvasImageInterop (this must always succeed, and throws if it doesn't)
RcwMarshaller.GetNativeObject(source, canvasImageInterop.GetAddressOf()).Assert();
RcwMarshaller.GetNativeInterface(source, canvasImageInterop.GetAddressOf()).Assert();

using ComPtr<ID2D1Image> d2D1Image = default;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ private bool SetD2DInput(
using ComPtr<ICanvasImageInterop> canvasImageInterop = default;

// Try to get the ICanvasImageInterop interface from the input source
HRESULT hresult = RcwMarshaller.GetNativeObject(value, canvasImageInterop.GetAddressOf());
HRESULT hresult = RcwMarshaller.GetNativeInterface(value, canvasImageInterop.GetAddressOf());

if (!Win32.SUCCEEDED(hresult))
{
Expand Down

0 comments on commit d011a42

Please sign in to comment.