0

我正在开发的插件(我们称之为 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 的构造函数来解决了这个问题。现在,尽管程序集版本错误,但一切都很好地解决了。

4

2 回答 2

0

评论太久了,所以我把它作为答案。

我仍然不明白你为什么要加载已经加载的 PARENT 程序集,或者你如何“更新” PARENT...

如果加载了 dll,则通常无法卸载它。您必须创建一个新的 AppDomain 才能使其工作。

所以正如我所见,你运行你的父母,它加载插件,然后附加事件。

根据http://msdn.microsoft.com/en-us//library/ff527268.aspx

为 AssemblyResolve 事件注册处理程序时,只要运行时无法按名称绑定到程序集,就会调用处理程序。例如,从用户代码调用以下方法可能会引发 AssemblyResolve 事件:

一个 AppDomain.Load 方法重载或 Assembly.Load 方法重载,其第一个参数是一个字符串,表示要加载的程序集的显示名称(即 Assembly.FullName 属性返回的字符串)。

一个 AppDomain.Load 方法重载或 Assembly.Load 方法重载,其第一个参数是一个 AssemblyName 对象,该对象标识要加载的程序集。

Assembly.LoadWithPartialName 方法重载。

实例化另一个应用程序域中的对象的 AppDomain.CreateInstance 或 AppDomain.CreateInstanceAndUnwrap 方法重载。

这意味着您必须显式加载程序集(并且无法自动执行)才能触发事件。

所以我相信 PARENT 的“更新”在这里至关重要。你怎么做呢?

编辑

根据您的编辑,我相信您的答案可以在这里找到:如果我重建我的项目引用的 dll,我是否也必须重建项目?

该链接还提到了有关严格强制引用程序集版本以及如何避免它的内容。

于 2013-10-04T09:41:05.220 回答
0

如 EDIT #4 中所述,装配事件没有足够快地注册。

我通过删除 PLUGINStarter 并将程序集解析代码移动到 Loader.cs 的构造函数来解决了这个问题。现在,尽管程序集版本错误,但一切都很好地解决了。

于 2013-10-06T10:04:54.997 回答