如果我有多个同一进程的实例访问某个dll,是否有办法让其中一个进程获取锁定,删除它,替换它并继续?
答案 0 :(得分:3)
为了能够替换正在使用的dll,使用它们的程序必须在" Shadow Copy"模式。这样做不是直接使用文件,而是程序集生成文件的副本并将其复制到内存中。这允许您替换或删除当前运行的应用程序的DLL,当下次重新启动应用程序时,它将获取当前版本的新副本。这就是IIS如何使您可以更新正在使用的网站,它阴影复制其程序集以及当它检测到对目录的更改时,它会重新启动网站加载新版本的程序集。
卷影复制设置为AppDomain level setting,但是一旦启动AppDomain,您就无法更改设置。
启用卷影复制的两种方法是使用小型的#34; Loader"在您的程序之前启动的应用程序域,此加载程序启动一个启用了Shadow Copy的新AppDomain,然后开始组装。
private static void Main(string[] args)
{
if (AppDomain.CurrentDomain.ShadowCopyFiles == false)
{
var assembly = Assembly.GetEntryAssembly();
var currentAppDomain = AppDomain.CurrentDomain;
AppDomain newDomain = AppDomain.CreateDomain("ShadowedDomain",
null,
currentAppDomain.BaseDirectory,
currentAppDomain.RelativeSearchPath,
true); //<-- This true is what enables Shadow Copy on the AppDomain.
//This calls Main again in the new AppDomain and blocks till the call to Main exits.
newDomain.ExecuteAssembly(assembly.Location, args);
}
else
{
RealMain(args);
}
}
private static void RealMain(string[] args)
{
//Your code here.
}
一个缺点是您的主EXE仍将被锁定,但任何DLL的EXE加载都将使用Shadow Copy加载。
另一个选项与第一个类似,但您可以告诉自己的程序集使用自定义加载程序启用Shadow Copy,而不是手动运行加载程序并将其指向程序集。要做到这一点,首先创建一个单独的dll作为加载器并使其包含一个派生自AppDomainManager
的类,此文件不会被复制阴影。
using System;
namespace DomainManager
{
public class ShadowDomainManager : AppDomainManager
{
public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
{
base.InitializeNewDomain(appDomainInfo);
appDomainInfo.ShadowCopyFiles = "true";
}
}
}
然后在程序集的app.config
中,您可以告诉它使用您的加载程序dll。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<runtime>
<appDomainManagerType value="DomainManager.ShadowDomainManager" />
<appDomainManagerAssembly
value="DomainManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</runtime>
</configuration>
现在您的exe及其加载的任何DLL(存储在应用程序目录或其子目录中)将被加载到Shadow Copied App Domain中,并且可以在使用时删除/替换。