主程序通過 Assembly.LoadFile 載入插件 DLL 僅讀取元數據(如 FullName),但 DLL 文件會被進程獨佔,導致無法重新編譯/更新插件。Assembly 無卸載方法,必須卸載整個 AppDomain。
主程序 (Default AppDomain)
│
├── LocalLoader (代理)
│ │
│ └── 創建 New AppDomain
│ │
│ └── RemoteLoader (MarshalByRefObject)
│ │
│ └── Assembly.LoadFrom(插件路徑)
│ └── 暴露屬性/方法供主程序調用
│
└── 使用完畢 → AppDomain.Unload(New AppDomain) → 釋放文件鎖定
public class RemoteLoader : MarshalByRefObject {
private Assembly _assembly;
public void LoadAssembly(string fullPath) {
_assembly = Assembly.LoadFrom(fullPath);
}
public string FullName => _assembly?.FullName;
}
public class LocalLoader {
private AppDomain _appDomain;
private RemoteLoader _remoteLoader;
public LocalLoader() {
var setup = new AppDomainSetup {
ApplicationName = "PluginDomain",
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
ShadowCopyFiles = "true", // 啟用影子複製,避免鎖定原文件
ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory
};
_appDomain = AppDomain.CreateDomain("PluginDomain", null, setup);
string assemblyName = Assembly.GetExecutingAssembly().GetName().FullName;
_remoteLoader = (RemoteLoader)_appDomain.CreateInstanceAndUnwrap(
assemblyName, typeof(RemoteLoader).FullName);
}
public void LoadAssembly(string path) => _remoteLoader.LoadAssembly(path);
public string FullName => _remoteLoader.FullName;
public void Unload() {
AppDomain.Unload(_appDomain);
_appDomain = null;
}
}
private void button1_Click(object sender, EventArgs e) {
using (var loader = new LocalLoader()) {
loader.LoadAssembly(@"C:\Plugins\MyPlugin.dll");
label1.Text = loader.FullName;
// loader.Unload() 在 Dispose 或這裡顯式調用
}
}