Skip to content

wherewhere/WinRTWrapper

Repository files navigation

WinRTWrapper

A wrapper source generator for WinRT

Issues Stargazers

Support Language

  • C# ISO-2 or later

How to use

  1. Add the NuGet package WinRTWrapper to your project.
  2. 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;
    }
  3. Add the GenerateWinRTWrapper attribute to your class or interface that you want to wrap.
    [GenerateWinRTWrapper(typeof(Simple))]
    public sealed partial class SimpleWrapper;
  4. 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;
            }
        }
    }

Advanced Usage

Generate Member

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;

Marshaller

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;

Notice

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.

About

A wrapper source generator for WinRT

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published

Languages