應用程式域(AppDomain)是 .NET 執行環境的核心隔離單位。一個進程可容納多個 AppDomain,各自擁有獨立的記憶體、型別系統與安全策略,實現「進程內隔離」。本文深入探討 AppDomain 在插件框架中的應用:建立與卸載、載入優化、異常隔離、跨域 UI 組合。
// 建立新域
AppDomain domain = AppDomain.CreateDomain("Plugin Domain");
// 在新域執行程式碼
domain.DoCallBack(new CrossAppDomainDelegate(() =>
{
Window win = new Window
{
Width = 300,
Height = 100,
Content = AppDomain.CurrentDomain.FriendlyName
};
win.Show();
}));
// 卸載域(釋放所有資源)
AppDomain.Unload(domain);
預設每個 AppDomain 獨立載入參考組件,造成效能損耗。透過 LoaderOptimization 屬性可啟用「域中立組件」共享:
[STAThread]
[LoaderOptimization(LoaderOptimization.MultiDomainHost)]
public static void Main()
{
App app = new App();
app.InitializeComponent();
app.Run();
}
三種模式:SingleDomain(預設)、MultiDomain(所有組件共享)、MultiDomainHost(僅強名稱組件共享)。插件框架建議使用 MultiDomainHost 平衡隔離與效能。
新域內的未處理例外會冒泡至預設域導致進程崩潰。解決方案:在主域註冊 Dispatcher.UnhandledException,標記 Handled = true 並主動卸載故障域:
AppDomain domain = AppDomain.CreateDomain("Plugin Domain");
Dispatcher.CurrentDispatcher.UnhandledException += (s, e) =>
{
e.Handled = true;
MessageBox.Show(e.Exception.Message);
AppDomain.Unload(domain); // 隔離故障,主域存活
};
domain.DoCallBack(new CrossAppDomainDelegate(() =>
{
Window win = new Window { ... };
win.Loaded += (o, a) => throw new Exception("Plugin crash!");
win.Show();
}));
UI 元素無法跨域序列化傳遞。WPF Add-in 模型提供 FrameworkElementAdapters 透過視窗控制代碼(HWND)實現跨域視覺樹投影:
// 新域建立控件並轉換為合約
domain.DoCallBack(() =>
{
Button btn = new Button { Content = "Cross Domain" };
INativeHandleContract contract = FrameworkElementAdapters.ViewToContractAdapter(btn);
AppDomain.CurrentDomain.SetData("pluginUI", contract);
});
// 主域取得合約並還原為 FrameworkElement
INativeHandleContract contract = domain.GetData("pluginUI") as INativeHandleContract;
FrameworkElement ui = FrameworkElementAdapters.ContractToViewAdapter(contract);
MainWindow.Content = ui; // 成功顯示來自其他域的 UI
此技術讓插件 UI 在獨立域運行,卻能無縫呈現於主應用程式視窗中。
AppDomain 提供進程級隔離的輕量替代方案,是插件框架實現「故障隔離、熱插拔、權限沙箱」的關鍵基礎設施。結合載入優化、異常捕獲與跨域 UI 技術,可構建健壯、可擴充的插件生態系統。