我正在开发一个程序,在给定目录中搜索“plugins”(即.DLL文件),然后加载它们(使用Reflection)。到目前为止一切正常。但是,如果我现在在插件项目中创建一个Dependency属性并想要使用它,程序崩溃时会显示一条错误消息:
抛出异常:PresentationFramework.dll中的“System.Windows.Markup.XamlParseException”未实现方法或操作。
只有在插件项目中创建DP时才会发生这种情况。如果我使用现有的(也添加了自己,但已经存在,因为它包含在API中)一切正常,没有错误或其他任何东西。顺便说一下,至少根据Visual Studio,在Activator.CreateInstance(t)中发生错误。
有趣的是,如果我将一个WPF项目添加到插件的项目(这是一个类库),链接到插件和API,并包含在主窗口中使用附加属性的UserControl,一切正常。所以它必须与我动态加载插件有关,但我不知道它可能是什么。特别是因为,正如我所说,以前创建的DP工作正常,只有我在插件中创建的DP没有。
我想写一个“重现步骤”代码,但我担心它不会那么容易。该程序现在已经相当复杂,只是为了隔离这个问题可能很困难。下面是加载插件的代码,附加属性,附加属性的基类以及我想要使用它的.xaml文件。
加载插件:
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);
}
}
}
}
我附加属性的基类:
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) { }
}
附属物本身:
public class SubHeading : BaseAttachedProperty<SubHeading, string> { }
SystemStatsUI.xaml中的Textblock(我想使用该属性的UserControl):
<TextBlock local:SubHeading.Value="Test" />
Visual Studio编译没有问题,没有警告或其他任何东西。但是一旦我启动主程序并尝试加载插件,它就会崩溃。但是如果我现在注释掉我使用DP的TextBlock,那么一切都会再次起作用,除了不使用DP。但它确实存在,或者说它至少是创造出来的。正如我所说,它只在动态加载时才起作用。如果我将WPF项目添加到插件项目并绑定到用户控件中,我可以毫无问题地使用DP。但不是在主程序中,我只能使用主程序的预定义DP而不会出现问题。
实施杰夫的想法:
MainWindowViewModel构造函数:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
....
}
CurrentDomain_AssemblyResolve:
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var path = Directory.GetCurrentDirectory() + "\\plugins\\";
return Assembly.LoadFrom(path + args.Name);
}
它跳转到回调函数,但要求一个.resources文件,它在plugins文件夹中找不到。但我不知道它应该在哪里找到它,它也不存在于我的插件文件夹中。或者我应该只通过将.dll作为文件末尾的请求?
我相信问题可能是动态加载程序集的加载上下文。它位于LoadFrom上下文中,因此xaml解析器无法正确解析它,因为它在默认的加载上下文中运行。
您将需要添加一个自定义程序集解析程序来处理xaml解析器的运行时解析。
asm = Assembly.LoadFrom(plugin); // your current code...just add the below line of code
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
new AssemblyName(args.Name).Name == asm.GetName().Name ? asm : null;
另外 - 绝对确保你的插件dll目录和bin目录之间没有任何重复的dll - 你会得到“组件没有资源标识符”你提到的那种错误。