如何不重启主程序更新DLL?附Updater类实现及相关问询
Let's tackle your two questions and address your server dependency concern clearly, with practical code adjustments:
1. Can we check the DLL before it's used?
Absolutely—this is actually the optimal time to run your version check. By doing this before the DLL is loaded into the application domain, you avoid file lock issues that happen once the CLR has the DLL in use.
How to implement it:
Move your updater logic to the very start of your application, before any code that references types from the target DLL executes. For example, in your Program.Main method:
static void Main(string[] args) { const string LOCAL_DLL_PATH = @"C:\MyApp\MyFeature.dll"; const string SERVER_DLL_PATH = @"\\Server\AppUpdates\MyFeature.dll"; // Run update check FIRST—before loading the DLL Updater updater = new Updater(LOCAL_DLL_PATH, SERVER_DLL_PATH); if (updater.IsAvailableNewerVersion) { updater.Update(); // If you're using static references, you'll still need a restart here // But we'll cover how to skip that in the next section } // Now proceed to initialize your app and use the DLL StartMainApplication(); }
This ensures the DLL hasn't been locked yet, making the File.Copy operation in Update() far more reliable.
2. How to update the DLL without restarting the main program?
The core problem here is that once the CLR loads a DLL into an AppDomain, it locks the file and can't unload the assembly directly. To get around this, you need to use dynamic loading with isolated execution contexts (AppDomains for .NET Framework, or AssemblyLoadContext for .NET Core/.NET 5+).
Step-by-Step Solution:
a. Decouple with an Interface
First, create a shared assembly (like MyApp.Interfaces.dll) that only contains the interface for your DLL's functionality. Your main app references this interface assembly, not the actual DLL—this eliminates direct runtime dependency.
// In MyApp.Interfaces.dll public interface IFeatureService : MarshalByRefObject // Required for cross-AppDomain calls (Framework only) { void ExecuteFeature(); string GetVersion(); }
b. Implement the Interface in Your DLL
Both your local and server-side DLLs implement this interface:
// In MyFeature.dll public class FeatureService : IFeatureService { public void ExecuteFeature() { MessageBox.Show("Running feature from DLL!"); } public string GetVersion() { return Assembly.GetExecutingAssembly().Version.ToString(); } }
c. Use Isolated Contexts for Loading
For .NET Framework: Use AppDomains
Create a loader class that manages a separate AppDomain for the DLL—this lets you unload the domain to release the file lock when updating:
public class FeatureLoader : IDisposable { private AppDomain _featureDomain; private IFeatureService _featureInstance; public IFeatureService LoadFeature(string dllPath) { var domainSetup = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(dllPath) }; _featureDomain = AppDomain.CreateDomain("FeatureDomain", null, domainSetup); // Load DLL and create instance _featureInstance = (IFeatureService)_featureDomain.CreateInstanceFromAndUnwrap( dllPath, "MyFeature.FeatureService" // Full type name of your implementation ); return _featureInstance; } public void UnloadFeature() { if (_featureDomain != null) { AppDomain.Unload(_featureDomain); _featureDomain = null; _featureInstance = null; } } public void Dispose() => UnloadFeature(); }
For .NET Core/.NET 5+: Use AssemblyLoadContext
This is the modern alternative to AppDomains, designed specifically for dynamic assembly loading/unloading:
public class FeatureLoadContext : AssemblyLoadContext { public FeatureLoadContext() : base(isCollectible: true) { } protected override Assembly Load(AssemblyName assemblyName) { // Load dependencies here if needed, or return null to use the default context return null; } }
d. Integrate with Your Updater
Now you can update and reload the DLL without restarting:
static void Main(string[] args) { const string LOCAL_DLL_PATH = @"C:\MyApp\MyFeature.dll"; const string SERVER_DLL_PATH = @"\\Server\AppUpdates\MyFeature.dll"; while (true) // Example loop for periodic checks { // Check for updates Updater updater = new Updater(LOCAL_DLL_PATH, SERVER_DLL_PATH); if (updater.IsAvailableNewerVersion) { // Unload existing DLL to release file lock using (var loader = new FeatureLoader()) // Use FeatureLoadContext for .NET Core { loader.UnloadFeature(); } // Update the DLL safely updater.Update(); MessageBox.Show("DLL updated! Reloading feature..."); } // Load and use the updated DLL using (var loader = new FeatureLoader()) { var feature = loader.LoadFeature(LOCAL_DLL_PATH); MessageBox.Show($"Loaded DLL version: {feature.GetVersion()}"); feature.ExecuteFeature(); } // Wait before next check (adjust timing as needed) Thread.Sleep(60000); } }
Addressing Your Server Dependency Concern
Using the dynamic loading approach above means you never need to directly reference the server-side DLL. You only download the latest version locally once, then use the local copy for runtime execution. The server is only contacted for version checks and updates, not for ongoing runtime dependency—keeping your app functional offline after the latest DLL is downloaded.
内容的提问来源于stack exchange,提问作者HelloWorld

