A wrapper source generator for WinRT
- C# ISO-2 or later
- Add the NuGet package
WinRTWrapper
to your project. - Find a class or interface that you want to wrap for WinRT. For example, let's say you have a simple class like this:
public sealed class Simple { public int Property { get; set; } public int Method() => Property; public Task MethodAsync() => Task.CompletedTask; public Task MethodWithTokenAsync(CancellationToken cancellationToken) => Task.CompletedTask; public event EventHandler<int>? Event; }
- Add the
GenerateWinRTWrapper
attribute to your class or interface that you want to wrap.[GenerateWinRTWrapper(typeof(Simple))] public sealed partial class SimpleWrapper;
- The source generator will automatically generate a WinRT wrapper for the specified class or interface. You can then use this wrapper in your WinRT applications.
// <auto-generated/> #pragma warning disable /// <inheritdoc cref="T:Simple"/> public sealed partial class SimpleWrapper { /// <summary> /// The target <see cref="T:Simple"/> object of the wrapper. /// </summary> private readonly global::WinRTWrapper.Test.Simple target; /// <summary> /// Initializes a new instance of the <see cref="T:SimpleWrapper"/> class with the specified target <see cref="T:Simple"/> object. /// </summary> /// <param name="target">The target <see cref="T:Simple"/> object.</param> internal SimpleWrapper(global::WinRTWrapper.Test.Simple target) { this.target = target; } /// <inheritdoc cref="P:Simple.Property"/> public int Property { get { return this.target.Property; } set { this.target.Property = value; } } /// <inheritdoc cref="M:Simple.Method"/> public int Method() { return this.target.Method(); } /// <inheritdoc cref="M:Simple.MethodAsync"/> public global::Windows.Foundation.IAsyncAction MethodAsync() { return global::System.WindowsRuntimeSystemExtensions.AsAsyncAction(this.target.MethodAsync()); } /// <inheritdoc cref="M:Simple.MethodWithTokenAsync(System.Threading.CancellationToken)"/> public global::Windows.Foundation.IAsyncAction MethodWithTokenAsync() { return global::System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.Run(delegate (global::System.Threading.CancellationToken cancellationToken) { return this.target.MethodWithTokenAsync(cancellationToken); }); } /// <inheritdoc cref="E:Simple.Event"/> public event global::System.EventHandler<int> Event { add { this.target.Event += value; } remove { this.target.Event -= value; } } }
You can specify which types of members to generate in the wrapper class by add parameters to the GenerateWinRTWrapper
attribute. For example, GenerateMember.Interface
will only generate interface members:
[GenerateWinRTWrapper(typeof(Simple), GenerateMember.Interface)]
public sealed partial class SimpleWrapper : I;
GenerateMember.Defined
will only generate members that are defined in the class:
[GenerateWinRTWrapper(typeof(Simple), GenerateMember.Defined)]
public sealed partial class SimpleWrapper
{
public partial int Method();
public static partial SimpleWrapper GetSelf(SimpleWrapper self);
}
You can also specify interfaces which not implemented in the class as a filter:
[GenerateWinRTWrapper(typeof(Simple), typeof(I))]
public sealed partial class SimpleWrapper;
You can define a custom marshaller for a specific type by WinRTWrapperMarshaller
attribute. A marshaller should implement ConvertToWrapper
and ConvertToManaged
methods. For example:
[WinRTWrapperMarshaller(typeof(Simple), typeof(SimpleWrapper))]
public sealed partial class SimpleWrapper
{
/// <summary>
/// Converts a managed type <see cref="T:WinRTWrapper.Test.Simple"/> to a wrapper type <see cref="T:WinRTWrapper.Test.SimpleWrapper"/>.
/// </summary>
/// <param name="managed">The managed type to convert.</param>
/// <returns>The converted wrapper type.</returns>
internal static global::WinRTWrapper.Test.SimpleWrapper ConvertToWrapper(global::WinRTWrapper.Test.Simple managed)
{
return (global::WinRTWrapper.Test.SimpleWrapper)new global::WinRTWrapper.Test.SimpleWrapper(managed);
}
/// <summary>
/// Converts a wrapper type <see cref="T:WinRTWrapper.Test.SimpleWrapper"/> to a managed type <see cref="T:WinRTWrapper.Test.Simple"/>.
/// </summary>
/// <param name="wrapper">The wrapper type to convert.</param>
/// <returns>The converted managed type.</returns>
internal static global::WinRTWrapper.Test.Simple ConvertToManaged(global::WinRTWrapper.Test.SimpleWrapper wrapper)
{
return (global::WinRTWrapper.Test.Simple)((global::WinRTWrapper.Test.SimpleWrapper)wrapper).target;
}
}
It will be generated automatically when both WinRTWrapperMarshaller
and GenerateWinRTWrapper
attributes are applied to the same class.
Then you can use the WinRTWrapperMarshalUsing
attribute to specify the marshaller for a method parameter or return value.
You can apply it to the managed type:
/// <summary>
/// Gets a new instance of the <see cref="Simple"/> class with the specified value.
/// </summary>
/// <param name="self">The value to initialize the instance with.</param>
/// <returns>The new instance of <see cref="Simple"/>.</returns>
[return: WinRTWrapperMarshalUsing(typeof(SimpleWrapper))]
public static Simple GetSelf([WinRTWrapperMarshalUsing(typeof(SimpleWrapper))] Simple self)
{
return self;
}
Or you can apply it to the wrapper type:
[return: WinRTWrapperMarshalUsing(typeof(SimpleWrapper))]
public static partial SimpleWrapper GetSelf([WinRTWrapperMarshalUsing(typeof(SimpleWrapper))] SimpleWrapper self);
You can also use the WinRTWrapperMarshalling
attribute to specify the marshaller for a class.
[WinRTWrapperMarshalling(typeof(SimpleWrapper))]
internal class Simple;
Since C#/WinRT is not IL/WinRT, you should define every members in the wrapper class. So that the C#/WinRT can generate the correct WinRT metadata for the wrapper class.