如何安全替换Windows共享文件夹中C# .NET应用正在使用的DLL
方案1:侧载(Side-by-Side)程序集部署(改造成本极低,原生支持)
这是Windows自带的标准热更新方案,完全避免操作正在被占用的文件:
- 给所有需要更新的DLL添加强命名,每次发布补丁时升级DLL的版本号
- 不同版本的DLL可以共存于共享文件夹,不需要删除、重命名旧版本DLL
- 在主程序的
app.config中配置程序集绑定规则,让新启动的程序实例自动加载最高版本的对应DLL,已经运行的旧实例不受任何影响,也不会出现懒加载时的IL不匹配问题
方案2:内存加载程序集(解决你提到的反射依赖问题)
通过全量内存加载的方式完全避免锁定文件,同时解决多层依赖加载问题:
- 订阅当前AppDomain的
AssemblyResolve全局事件,接管所有程序集的解析逻辑 - 所有程序集都先读取为
byte[]内存流,再调用Assembly.Load(byte[])方法加载,该方式加载的程序集完全不会锁定磁盘源文件,且加载后所有逻辑都基于内存中的IL数据,不会再读取磁盘文件,从根源上避免懒加载异常 - 多层依赖会自动触发
AssemblyResolve事件,你只需要在事件处理逻辑中按照请求的程序集名称、版本号到共享目录中匹配对应DLL,同样读成字节数组返回即可,不需要手动处理复杂的依赖链 - 注意强命名程序集需要保证加载的文件版本、公钥Token和请求的参数一致,避免加载失败
方案3:本地代理启动器(兼容性最高,几乎无环境依赖)
完全不需要修改现有业务程序的逻辑,仅需新增一个轻量启动入口:
- 用户不再直接启动共享文件夹中的主程序,而是启动一个100KB以内的
Launcher.exe - Launcher启动后首先校验共享目录中的程序文件版本,将最新版本的所有DLL、主程序文件复制到当前用户的本地临时目录(
%TEMP%下的专属子目录) - 从本地临时目录启动主程序,共享目录中的文件不会被任何运行实例锁定,你可以随时替换共享目录中的文件,新启动的实例会自动拉取最新版本,旧实例运行不受任何影响
- 该方案对客户环境权限要求极低,普通用户默认拥有临时目录读写权限,完全规避了卷影复制需要的高权限问题
选型建议
- 不想修改现有业务代码优先选启动器方案,改造成本最低,兼容所有Windows环境
- 不想增加启动时的文件复制开销优先选侧载方案,仅需要调整打包规则和配置文件
- 已有插件化扩展需求的场景选内存加载方案,灵活度最高,后续可以支持动态插件加载等扩展能力
内容的提问来源于stack exchange,提问作者user17331111
相关产品推荐
相关产品推荐

