加载主软件的插件时,AppDomain.CurrentDomain.AssemblyResolve不会触发。为什么?

时间:2013-10-04 09:10:31

标签: c#-4.0 assembly-resolution

我正在开发的插件(我们称之为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

我认为这是可能的,如Is it possible to replace a reference to a strongly-named assembly with a "weak" reference?所述,但由于一些奇怪的原因,它在我的申请中失败了。

编辑#4

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

2 个答案:

答案 0 :(得分:0)

我很乐意发表评论,所以我把它作为答案。

我仍然不明白为什么要加载已加载的PARENT程序集或者如何更新&#34; PARENT ...

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

所以我看到它,你运行你的PARENT,它加载PLUGIN,然后附加事件。

根据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方法重载,用于实例化另一个应用程序域中的对象。

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

所以我相信&#34;更新&#34;父母在这里至关重要。你怎么做呢?

修改

根据您的修改,我相信您的答案可以在这里找到:If I rebuild a dll that my project references, do I have to rebuild the project also?

This link还提到严格强制引用程序集的版本以及如何避免它。

答案 1 :(得分:0)

如编辑#4中所述,装配事件未能很快注册。

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