jeudi 11 avril 2019

Using custom attached properties within a plugin leads to an error with Reflection/CreateInstance

I'm working on a program that searches for "plugins" (i.e. .DLL files) in a given directory and then loads them (with Reflection). So far everything works fine. But if I now create a Dependency Property in a plugin project and want to use it, the program crashes with an error message:

Exception thrown: "System.Windows.Markup.XamlParseException" in PresentationFramework.dll The method or operation is not implemented.

This only happens if I create a DP in the plugin project. If I use an existing one (also added myself, but already present in the sense that it is included in the API) everything works fine, no errors or anything else. By the way, the error occurs, at least according to Visual Studio, at Activator.CreateInstance(t).

What's also interesting is that if I add a WPF project to the plugin's project (which is a class library), link to the plugin and API, and include the UserControl that uses the attached property in the mainwindow, everything works fine. So it must have something to do with me loading the plugin dynamically, but I don't know what it could be. Especially since, as I said, previously created DPs work fine, only the ones I create in the plugin don't.

I would like to write a "Steps to reproduce" code, but I'm afraid it won't be that easy. The program is already quite complex now, cutting it off just to isolate the one problem could be difficult. Here is the code that loads the plugins, the attached property, the base class for my attached property and the .xaml file I want to use it in.

Loading plugins:

public void LoadPlugins()
{
    var path = Directory.GetCurrentDirectory() + "\\plugins\\";

    Assembly asm;
    foreach (var plugin in Directory.GetFiles(path, "*.dll"))
    {
        asm = Assembly.LoadFrom(plugin);
        foreach (var t in asm.GetTypes())
        {
            if (t.GetInterface("RemotyAPI") != null && (t.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract)
            {
                var pluginInstance = (RemotyAPI)Activator.CreateInstance(t);

                var p = new Plugin
                {
                    Name = t.Name,
                    Title = pluginInstance.Name,
                    Version = pluginInstance.Version,
                    LogoPath = pluginInstance.LogoPath,
                    Instance = pluginInstance
                };

                p.Instance.SendMessage += Server.GetInstance().SendMessage;

                Plugins.Add(p);
            }
        }
    }
}

Base class for my attached properties:

public abstract class BaseAttachedProperty<Parent, Property>
    where Parent : new()
{
    public event Action<DependencyObject, DependencyPropertyChangedEventArgs> ValueChanged = (sender, e) => { };

    public static Parent Instance { get; private set; } = new Parent();

    public static readonly DependencyProperty mValueProperty = DependencyProperty.RegisterAttached("Value", typeof(Property), typeof(BaseAttachedProperty<Parent, Property>), new UIPropertyMetadata(new PropertyChangedCallback(OnValuePropertyChanged)));

    private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (Instance as BaseAttachedProperty<Parent, Property>)?.OnValueChanged(d, e);

        (Instance as BaseAttachedProperty<Parent, Property>)?.ValueChanged(d, e);
    }

    public static Property GetValue(DependencyObject d)
    {
        return (Property)d.GetValue(mValueProperty);
    }

    public static void SetValue(DependencyObject d, Property value)
    {
        d.SetValue(mValueProperty, value);
    }

    public virtual void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { }
}

The attached property itself:

public class SubHeading : BaseAttachedProperty<SubHeading, string> { }

The Textblock in SystemStatsUI.xaml (The UserControl where I want to use the property):

<TextBlock local:SubHeading.Value="Test" />

Visual Studio compiles without problems, no warning or anything else. But as soon as I start the main program and it tries to load the plugin, it crashes. But if I now comment out the TextBlock where I use the DP, everything works again, except that the DP is not used. But it does exist, or let's say it was at least created. And as I said, it only doesn't work when loading dynamically. If I add a WPF project to the plugin project and bind in the user control, I can use the DP without any problems. But not in the main program, there I can only use predefined DP of the main program without problems.





Aucun commentaire:

Enregistrer un commentaire