Bloomberg 應用程式入口網站承載數十萬用戶使用的第三方 WPF 應用程式,其核心架構採用 獨立進程插件宿主 + AppDomain 隔離 + .NET Remoting 進程間通訊。本文解析此架構關鍵設計決策,並提供可直接參考的原始碼實作(含示範插件 SAPP/DEMO)。

PluginController (預設 AppDomain) 與 PluginContainer (插件專屬 AppDomain)。Metadata.xml 及組件。| 優點 | 缺點 |
|---|---|
| 主進程效能/穩定性不受插件拖累 | 需自行實作 IPC 機制與版本控管 |
| 降低 UI 執行緒被插件阻塞風險 | 需管理宿主進程生命週期 |
| 記憶體洩漏、未處理例外不波及主進程 | 安全性架構複雜度提升 |
| 32 位進程獲得更大虛擬記憶體空間 | |
| 可擴充非 .NET 主應用程式 |
每個插件載入獨立 AppDomain,具備:
MarshalByRefObject 或序列化。AppDomain.Unload 釋放插件所有組件(除非以 LoaderOptimization.MultiDomain 載入)。
AppRoot/
├── Main.exe
├── PluginHost.exe
├── Hosting.dll
├── PAC/
│ ├── Bloomberglp.Blpapi.dll
│ └── WPFToolkit.dll
└── Plugins/
├── SAPP/
│ ├── Metadata.xml
│ └── SimpleApp.dll
└── DEMO/
├── Metadata.xml
└── DapiSample.dll
Metadata.xml 定義進入點:
<Plugin>
<Title>Simple App</Title>
<MainAssembly>SimpleApp</MainAssembly>
<MainClass>SimpleApp.Main</MainClass>
</Plugin>
PluginHost.exe 啟動,預設 AppDomain 建立 PluginController (繼承 MarshalByRefObject,覆寫 InitializeLifetimeService 回傳 null 防止 5 分鐘回收)。Main.exe 透過 .NET Remoting 呼叫 PluginController.Launch("DEMO IBM Equity")。PluginController 依助碼鍵 (Mnemonic) 組合插件路徑,建立新 AppDomainSetup:
var appPath = Path.Combine(_appsRoot, mnemonic);
var setup = new AppDomainSetup { ApplicationBase = appPath };
// 動態產生組件解析設定,指定 Hosting.dll 與 PAC 組件 codeBase
setup.SetConfigurationBytes(Encoding.UTF8.GetBytes(GenerateConfigXml()));
var domain = AppDomain.CreateDomain(mnemonic, null, setup);
CreateInstanceAndUnwrap (避免 load-from 上下文) 在新域建立 PluginContainer 並呼叫 Launch(args)。PluginContainer 建立 STA 執行緒 (ApartmentState.STA) 啟動 WPF Application.Run(window) 訊息迴圈。MainAssembly、尋找 MainClass.CreateRootElement(string[]) 靜態方法,取回 UIElement 包裝為 Window 顯示。AppDomain.Unload 清理資源。直接用 CreateInstanceFromAndUnwrap 會導致組件載入到 load-from 上下文,造成原生映像無法使用、啟動變慢。正確做法:CreateInstanceAndUnwrap + <codeBase> 設定。
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="PluginHost.Hosting" publicKeyToken="..." culture="neutral"/>
<codeBase version="1.0.0.0" href="Hosting.dll"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Bloomberglp.Blpapi" publicKeyToken="..." culture="neutral"/>
<codeBase version="3.6.1.0" href="PAC/Bloomberglp.Blpapi.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Bloomberglp.Blpapi 連接 DAPI,請求 HistoricalDataRequest (PX_LAST 欄位)。DataPoint 序列。Chart/LineSeries 繪製價格走勢圖 (MS-PL 授權,支援部分信任環境)。
本文未涵蓋完整沙箱實作 (如 Chromium 沙箱、Code Access Security、AppDomain 權限集)。生產環境需額外實作:
「獨立進程 + 多 AppDomain + Remoting」是承載不受信任 WPF 插件的成熟模式,兼顧隔離性、擴充性與開發體驗。隨著 .NET Core/.NET 5+ 推出 AssemblyLoadContext 取代 AppDomain,跨平台插件架構正在演進,但核心設計哲學——**最小權限、失敗隔離、版本獨立**——始終不變。
下載完整原始碼 (archive.msdn.microsoft.com/mag201308Plugins) 即刻啟動你的插件生態系統建設。