Custom Control Library in Islands and Frame.Navigate - AccessViolationException

51 views Asked by At

I have a WinUI 3 Class Library containing a page with a NavigationView (MyFabulousPage.xaml), where clicking on any item navigates the frame to 'SamplePageA'. When I use the class library from a WinUI unpackaged application, all works as expected.

However, when I try to use the library from a WinForms application which hosts MyFabulousPage in an island, navigation to SamplePageA crashes with AccessViolationException somewhere in the depths of WinRT.

I tried to add an app.manifest file as suggested in this article, but it didn't resolve the issue.

Full source is available here: https://github.com/nikolayvpavlov/Session_WindowsDesktop2024

1

There are 1 answers

1
Simon Mourier On BEST ANSWER

For a XAML application, there can be multiple IXamlMetadataProvider instances. By default all this is generated by the tooling and the application defers all XAML metadata resolving to the providers.

When you write your own custom application, the system will still ask at runtime for various information. In your project, since it's a Winforms application, the XAML metadata corresponding the the WinUI3 project is not exposed automatically by the tooling so the app crashes when trying to get it (error handling in UWP/WinUI3/XAML is currently very poor and that's a pitty...).

This XAML metadata provider resides in a file XamlTypeInfo.g.cs that you can have a look at and is located usually in the temp/generated obj\Debug\net7.0-windows10.0.19041.0 (version can vary) folder.

This is what this XamlTypeInfo.g.cs file looks like:

namespace MyWinUILibrary.MyWinUILibrary_XamlTypeInfo
{
    /// <summary>
    /// Main class for providing metadata for the app or library
    /// </summary>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.UI.Xaml.Markup.Compiler"," 3.0.0.2312")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    public sealed class XamlMetaDataProvider : global::Microsoft.UI.Xaml.Markup.IXamlMetadataProvider
    {
        ...
    }

The trick is you cannot reference it the MyWinUILibrary.MyWinUILibrary_XamlTypeInfo.XamlMetaDataProvider class directly, as there's a chicken vs egg problem (the project will not compile before the file is generated and it will only be generated when the project compiles...).

So what you can do is load it by reflection at startup. Here is a modified version of your app that works better:

public class DummyApp : Application, IXamlMetadataProvider
{
    private readonly XamlControlsXamlMetaDataProvider provider = new();
    private readonly IXamlMetadataProvider _myLibProvider;

    public DummyApp()
    {
        // find the generated IXamlMetadataProvider for this lib
        var type = GetType().Assembly.GetTypes()
            .FirstOrDefault(t =>
                typeof(IXamlMetadataProvider).IsAssignableFrom(t) &&
                t.GetCustomAttribute<GeneratedCodeAttribute>() != null);
        _myLibProvider = (IXamlMetadataProvider)Activator.CreateInstance(type);
    }

    public IXamlType GetXamlType(Type type)
    {
        var ret = provider.GetXamlType(type);
        ret ??= _myLibProvider.GetXamlType(type);
        return ret;
    }

    public IXamlType GetXamlType(string fullName)
    {
        var ret = provider.GetXamlType(fullName);
        ret ??= _myLibProvider.GetXamlType(fullName);
        return ret;
    }

    public XmlnsDefinition[] GetXmlnsDefinitions()
    {
        var ret = provider.GetXmlnsDefinitions();
        ret ??= _myLibProvider.GetXmlnsDefinitions();
        return ret;
    }

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        Resources.MergedDictionaries.Add(new XamlControlsResources());
        base.OnLaunched(args);
    }
}