【问题标题】:How to make settings provider inside a dynamically loaded assembly available for reflection?如何使动态加载的程序集中的设置提供程序可用于反射?
【发布时间】:2013-03-01 08:24:50
【问题描述】:

this question 之后,我在我们正在开发的旧版 C# 应用程序中成功创建了我的自定义设置提供程序。它通过SettingsProvider 属性引用:

public sealed class MySettings : SettingsProvider
{
    ...
}

[SettingsProvider(typeof(MySettings))]
internal sealed partial class Settings {}

但是,现在我遇到了另一个问题。

我们的客户端应用程序包含一个自动更新工具,它的实现使得大部分客户端(包括上面的类)都内置到一个 DLL 中(我们在这里称之为 client.dll),然后由 EXE 使用。 EXE 首先检查更新并在需要时从更新服务器下载最新的更新,将所有 DLL 等替换为其较新的版本(包括 client.dll)。为了能够在运行时替换 DLL,它不能静态链接到它们。所以更新后,它会加载 client.dll 并像这样运行它:

Assembly assy = Assembly.LoadFile(
        AppDomain.CurrentDomain.BaseDirectory + "client.dll");
object frm = assy.CreateInstance("Client.Forms.MainForm");
Application.Run((Form)frm);

这样做的不幸后果是框架在动态加载的程序集中找不到我的自定义设置提供程序类。我尝试使用LoadFrom 而不是上面的LoadFile,但它没有帮助。到目前为止,我发现的唯一可行的解​​决方案是在加载器 exe 中实现一个与实际设置提供程序同名的代理类,框架可以找到它。然后代理从客户端程序集实例化真正的设置提供程序并将所有调用委托给它。

这似乎可行,但我对此并不满意。有没有办法帮助框架直接在动态加载的程序集中找到我的类?

更新

我得到的错误信息:

System.Configuration.ConfigurationErrorsException: Failed to load provider type: Client.Properties.MySettings, Client, Version=4.0.1341.0, Culture=neutral, PublicKeyToken=null.
   at System.Configuration.ApplicationSettingsBase.get_Initializer()
   at System.Configuration.ApplicationSettingsBase.CreateSetting(PropertyInfo propInfo)
   at System.Configuration.ApplicationSettingsBase.EnsureInitialized()
   at System.Configuration.ApplicationSettingsBase.get_Properties()
   at System.Configuration.SettingsBase.GetPropertyValueByName(String propertyName)
   at System.Configuration.SettingsBase.get_Item(String propertyName)
   at System.Configuration.ApplicationSettingsBase.GetPropertyValue(String propertyName)
   at System.Configuration.ApplicationSettingsBase.get_Item(String propertyName)
   at Client.Properties.Settings.get_SomeConfigSetting()
   at ...

通过调试和日志消息,我确定永远不会调用该类的初始化方法,因此永远不会实例化该类。也就是说,上述异常背后似乎没有隐藏的初始化错误。

还有一点可能很重要:客户端目前在 .NET 2.0 上运行,并且在可预见的未来没有升级计划。

更新 2

我开始按照@jwddixon 的回答建议调查 AppDomain。首先,我想检查 Client.dll 是否真的在与调用者 EXE 不同的应用程序域中结束。所以我列出了当前应用程序域中的程序集,并看到 Client 实际上在那里。但令我惊讶的是,我注意到列表中实际上有两个名为 Client 的程序集 - EXE 和 DLL 具有相同的程序集名称,但版本不同(EXE 为 4.0.0.0,4.0 .1352.0 目前用于 DLL)。到目前为止,我还没有完全意识到这一点,这可能很重要。接下来我将尝试更改 EXE 程序集名称...

更新 3

...这实际上解决了问题! Aaargh...对于我不为人知的前辈发明了这个不知道为什么的扭曲方案,我现在有非常不喜欢的想法...但是感谢你们所有人提出的问题和想法,最终导致了解决方案!

【问题讨论】:

  • 尝试使用 Activator 类创建“Client.Forms.MainForm”。 msdn.microsoft.com/en-us/library/system.activator.aspx
  • 当您说“框架找不到我的自定义设置提供程序类”时,是否有错误?你能再扩大一点吗?
  • @SimonMourier,请查看我的更新。
  • 所以这更像是一个程序集负载问题,对吧?您是否尝试将提供程序类型名称指定为不同的名称,例如“Client.Properties.MySettings, Client”w/o 版本等。
  • 哦,好的,我想现在明白了 :-) 如何在任何操作之前挂钩 AppDomain.CurrentDomain.AssemblyResolve 事件,并在请求后指向正确的程序集?

标签: c# reflection .net-assembly settingsprovider


【解决方案1】:

我还没有尝试过,但是像这样的东西怎么样:

AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
AppDomain domain = AppDomain.CreateDomain("myAppDomain", null, setup)
setup.ApplicationBase = file;

Assembly assy = domain.Load(AssemblyName.GetAssemblyName(
AppDomain.CurrentDomain.BaseDirectory +
 "client.dll"));

object frm = assy.CreateInstance("Client.Forms.MainForm");
Application.Run((Form)frm);

【讨论】:

  • 听起来很有希望(我也在考虑这一点,添加一些关于从何处获取装配信息等的信息),但可以详细说明吗?它有什么作用?
  • 您实际上想要访问当前应用程序域之外的类似乎是合理的。 Microsoft 使用应用程序域来确保您不会偏离分配的边界。为了获得域外的完全访问权限,您首先需要自己制作,然后将 DLL 加载到其中。此时(只要您已正确设置)您可以创建外部对象的本地实例。您可能还需要手动将您无权访问的类加载到本地内存中。
  • file 变量在您的示例中被引用但从未定义 - 您可以添加它吗?
  • 虽然您没有给出确切的解决方案,但您的回答为找到根本原因提供了重要提示,因此我奖励您赏金(否则无论如何都会丢失)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 2021-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多