如果我有同一个进程的多个实例访问某个DLL,有没有办法让这些进程之一获得它的锁,删除它,replace它,并继续?
为了能够替换正在使用的dll,使用它们的程序必须在“ Shadow Copy ”模式下拥有其应用程序域。 这样做不是直接使用该文件,而是使用该程序集复制文件并将该文件加载到内存中。 这使您可以替换或删除当前正在运行的应用程序的DLL,当应用程序下一次重新启动时,它将获取当前版本的新副本。 这就是IIS如何让您更新正在使用的网站,它会复制它的程序集,当它检测到目录的更改时,它将重新启动加载新版本的网站的网站。
Shadow Copy设置是AppDomain级别的设置 ,但是一旦AppDomain启动,您将无法更改设置。
启用Shadow Copy的两种方法是使用在程序之前启动的一个小的“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,而不是手动运行加载程序并将其指向程序集。 要做到这一点首先做一个单独的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(存储在应用程序目录或其子目录中)将被加载到一个影子复制的应用程序域中,并且可以在使用时被删除/替换。