我正在开发的插件(我们称之为 PLUGIN)正在使用它所编写的主软件(称之为 PARENT)中的两个汇编文件。一旦 PARENT 更新到新版本(并且每周发生几次),我希望我的 PLUGIN 动态加载依赖项的新版本,而不会强迫我重新编译。
PARENT 将其插件加载为源代码文件并及时编译它们。由于我希望我的代码位于 DLL 中,因此我的 Loader.cs 文件通过反射从我的 DLL 调用函数。
下面是Loader.cs的代码。
// error handling removed for better readability
public Loader()
{
assembly = Assembly.LoadFile(dllPath);
type = assembly.GetType("PLUGIN.PLUGINStarter");
instance = Activator.CreateInstance(type);
}
public override void Dispose()
{
type.InvokeMember("Dispose", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, instance, null);
base.Dispose();
}
public override void OnButtonPress()
{
type.InvokeMember("ShowForm", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, instance, null);
}
现在 PLUGIN 命名空间中的 PLUGINStarter 类如下所示。
class PLUGINStarter
{
private PLUGIN plugin = null;
/// <summary>
/// This is loading PARENT.exe and PARENTSomeOtherFile.dll dependencies.
/// </summary>
public PLUGINStarter()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) =>
{
var fullAssemblyName = new AssemblyName(eventArgs.Name);
// this is not executed when PARENT version changes
MessageBox.Show(fullAssemblyName.Name, "Loading assembly...");
if (fullAssemblyName.Name.Equals("PARENT"))
{
// AppDomain.CurrentDomain.FriendlyName will handle the case where PARENT.exe is re-named
var found = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, AppDomain.CurrentDomain.FriendlyName));
return found;
}
else if (fullAssemblyName.Name.Equals("PARENTSomeOtherFile"))
{
var found = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, "PARENTSomeOtherFile.dll"));
return found;
}
else
{
return null;
}
};
Initialize();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void Initialize()
{
// the PARENT's assemblies are referenced in the PLUGIN class
plugin = new PLUGIN();
}
public void ShowForm()
{
plugin.ShowForm();
}
public void Dispose()
{
plugin.Dispose();
}
}
当 PARENT 更新到新版本时,不会触发该事件。为什么?
编辑#1
澄清一下: PARENT 加载(及时编译)Loader.cs,它加载 PLUGIN.dll,它依赖于来自 PARENT(主要是PARENT.exe本身)的程序集。
编辑#2
PARENT 软件是手动更新的。用户从互联网(产品的网站)下载。然后用户将我的 PLUGIN.dll 复制到 PARENT 的“插件”目录中。
然后我可以在 Loader.cs 中捕获以下异常。
[07:55:19.822 D] [PLUGIN] Loading DLL 'C:\PNT\PARENTNew\Plugins\PLUGIN\PLUGIN.dll'.
[07:55:19.826 D] [PLUGIN] Exception loading assembly 'System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IO.FileLoadException: Could not load file or assembly 'PARENT, Version=6.2.8113.191, Culture=neutral, PublicKeyToken=21a554ab5c01ae50' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
at PLUGIN.PLUGINStarter..ctor()
--- End of inner exception stack trace ---
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at PLUGINLoader.Loader..ctor() in c:\PNT\PARENTNew\Plugins\PLUGIN\Loader.cs:line 42'.
编辑#3
我相信这是可能的,如是否可以用“弱”引用替换对强命名程序集的引用?但由于某种奇怪的原因,它在我的应用程序中失败了。
编辑#4
我通过删除 PLUGINStarter 并将程序集解析代码移动到 Loader.cs 的构造函数来解决了这个问题。现在,尽管程序集版本错误,但一切都很好地解决了。