修改外部DLL在运行时使用的应用程序设置

本文关键字:应用程序 设置 运行时 外部 DLL 修改 | 更新日期: 2023-09-27 18:06:21

我正在尝试构建一个简单的客户端来测试内部身份验证服务。主要思想是只需要一个简单的测试工具,以确保该服务在每个环境中都是可调用的(并成功地进行身份验证(
在任何调用此服务的客户端应用程序中,都需要在App.config中定义一些设置。主要使用的是AuthenticationService,它包含一个值,该值是此远程身份验证服务的URL,由客户端中包含的DLL读取。每个环境(开发、QA、生产(的URL都不同。

例如:

  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=12345" >
      <section name="Authenticator.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=12345" requirePermission="false"/>
      <section name="RemoteAuthenticator.TokenCache.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=12345" requirePermission="false" />
    </sectionGroup>
  </configSections>
  <applicationSettings>
    <RemoteAuthenticator.Properties.Settings>
      <setting name="AuthenticationService" serializeAs="String">
        <value>https://environment-url</value>
      </setting>
    </RemoteAuthenticator.TokenCache.Properties.Settings>
  </applicationSettings>

我遇到的问题是构建一个能够在不同环境中调用服务进行测试的客户端。当用户向不同的环境发出请求时,需要在运行时更改设置AuthenticationService中URL的值,但我一直找不到这样做的方法。

用于调用此服务的客户端DLL提供了以下方式将身份验证令牌添加到HttpClient:

new AuthorizationHelper().AddAuthorization(httpClient);

DLL中的这些方法调用服务(在客户端App.config中指定的URL处(,并将返回的auth令牌添加到所提供的HttpClient中的标头中

这就是如何调用此服务的范围,因为当调用此服务时,DLL会读取App.config中的目标环境URL。DLL中的这些方法对我来说基本上是一个黑盒,因为我无法修改URL的检索方式。

到目前为止,我已经尝试过直接访问此设置并通过ConfigurationManager访问,但两种方式都显示为零设置。在处理了一段时间之后,我尝试创建多个App.config文件,每个环境一个,并尝试在运行时根据需要重新加载它们(如本文所述:https://stackoverflow.com/a/6151688/1428743)。最后一个解决方案在某种程度上有效,但仅适用于第一次调用。对不同环境的任何连续调用都不使用重新加载的配置中的URL,只使用第一次调用服务之前加载的配置的URL。

有没有任何方法可以在运行时修改此设置,以便根据需要使用此工具调用不同的环境?

修改外部DLL在运行时使用的应用程序设置

经过一番挖掘,我发现在我的情况下,这实际上是不可能的。我将概述我的发现,因为这可能会对以后的人有所帮助。

首先,我找不到修改app.config中加载的程序集使用的应用程序设置值的方法。我解决这个问题的方法是为我测试的每个环境创建一个不同的app.config,并动态加载它们。这可以使用以下方法实现:在运行时更改默认的app.config

其次,加载程序集后,如果不卸载该程序集,就不可能更改其读取的app.config。实际上不可能单独卸载程序集,因此必须卸载整个AppDomain。获得这种所需行为的最佳方法(从我所看到的(是创建自己的AppDomain,并从该域中的程序集中创建所需对象的实例。

例如:

AppDomain newDomain = AppDomain.CreateDomain("newDomain", null, AppDomain.CurrentDomain.SetupInformation);
YourObject obj = newDomain.CreateInstanceAndUnwrap("Your.Assembly.Name", typeof(YourObject).FullName) as YourObject;
// Do stuff
AppDomain.Unload(newDomain);

这将允许您从newDomain中调用YourObject实例所需的任何方法。这样可以卸载其他域,修改/重新加载app.config,然后使用新的app.config在其他域中重新加载程序集。请注意,在上面的代码中,您必须传递完整的类型名称作为CreateInstanceAndUnwrap的第二个参数,如下所示:https://stackoverflow.com/a/20918117/1428743.此外,您必须打开调用CreateInstance返回的值的包装。CreateInstanceAndUnwrap只是在一个镜头中为你们所有人做到这一点。有关展开的更多详细信息,以及为什么需要展开:https://stackoverflow.com/a/13366528/1428743.此外,您可能想知道为什么我们不创建一个新域并卸载现有的默认域。默认域无法卸载,在无法立即停止的线程中运行的任何域也无法卸载。在这些情况下,您最终会得到一个CannotUnloadAppDomainException:https://msdn.microsoft.com/en-us/library/system.cannotunloadappdomainexception(v=vs.110(.aspx

最后,这可能对您有效,但仅限于特定情况。为了使CreateInstanceCreateInstanceAndUnwrap工作,您试图从程序集中加载的类必须继承自MarshalByRefObject或标记为Serializable。如果这两种情况都不是(就像我一样(,您将无法创建一个实例来从另一个AppDomain调用。关于这两种情况之间差异的更多详细信息,请点击此处:https://stackoverflow.com/a/7047153/1428743

因此,最终我的解决方案是简单地禁止用户在加载程序集后修改他们的环境设置。虽然这不是最好的用户体验(因为它需要重新启动应用程序(,但在我的情况下,这是实现的唯一方法,因为我无法访问程序集中类的代码。