本文章是由機器翻譯。

WPF

建置容錯的複合應用程式

Ivan Krivyakov

下载代码示例

複合應用程式中,普遍認為需但容錯要求會發生變化。在某些情況下,它可能沒有外掛程式來降低整個應用程式的單個確定。在其他情況下,這不是可以接受的。在本文中,我描述了一個容錯的複合桌面應用程式的體系結構。這項擬議的體系結構將通過運行在其自己的 Windows 進程中每個外掛程式提供高水準的隔離。考慮以下設計目標建:

  • Strong主機和外掛程式之間的隔離
  • 外掛程式控制項完全視覺融入宿主視窗
  • 很容易的新外掛程式開發
  • 現有的應用程式對外掛程式的相當輕鬆轉換
  • 外掛程式使用由主機、 和反之亦然提供的服務的能力
  • 合理方便地增加新的服務和介面

附帶的原始程式碼 (msdn.microsoft.com/magazine/msdnmag0114) 包含兩個Visual Studio2012年解決方案:WpfHost.sln 和 Plugins.sln。首先,編譯該主機,然後編譯外掛程式。主要的可執行檔是 WpfHost.exe。外掛程式程式集,載入在需求上。圖 1 顯示已完成的應用程式。


圖 1 主機視窗與無縫集成進程外外掛程式

架構概觀

主機顯示索引標籤控制項和一個"+"按鈕中顯示的可用的外掛程式清單的左上角。從命名為 plugins.xml,該 XML 檔中讀取的外掛程式清單但替代目錄實現是可能的。每個外掛程式在它自己的進程中執行和沒有外掛程式程式集載入到主機。該體系結構的高級視圖所示圖 2


圖 2 應用程式體系結構的高級視圖

在內部,外掛程式主機是如下的模型-視圖-ViewModel (MVVM) 范式的常規Windows Presentation Foundation(WPF) 應用程式。由 PluginController 類,該類持有的已載入的外掛程式的集合表示的模型部分。每個已載入外掛程式是由代表為持有一個外掛程式控制和會談到一個外掛程式進程的外掛程式類的一個實例。

主機系統由組成的四個程式集,組織中所示的圖 3


圖 3 主機系統程式的程式集

WpfHost.exe 是宿主應用程式。PluginProcess.exe 是外掛程式的過程。這一過程的一個實例載入一個外掛程式。Wpf­Host.Interfaces.dll 包含由主機、 外掛程式進程和外掛程式使用的公共介面。PluginHosting.dll 包含的外掛程式的過程和主機用於外掛程式託管類型。

載入外掛程式涉及必須在 UI 執行緒執行的一些調用並可以在任何執行緒執行的一些調用。若要使應用程式的回應,我只阻止 UI 執行緒嚴格必要時。因此,為外掛程式類的程式設計介面分為負載與 CreateView 兩種方法:

class Plugin
{
  public FrameworkElement View { get; private set; }
  public void Load(PluginInfo info); // Can be executed on any thread
  public void CreateView();          // Must execute on UI thread
}

Plugin.Load 方法啟動一個外掛程式進程,並創建了一個基礎的外掛程式進程一邊。 它是在輔助執行緒上執行。 Plugin.CreateView 方法將本地視圖連接到遠端 FrameworkElement。 您需要以避免異常等檢查在 UI 執行緒上執行此。

外掛程式類最終調用使用者定義的外掛程式類裡面的外掛程式的過程。 為該使用者類的唯一要求是它實現了從 WpfHost.Interfaces 大會的 IPlugin 介面:

public interface IPlugin : IServiceProvider, IDisposable
{
  FrameworkElement CreateControl();
}

返回從外掛程式的框架元素可能是任意複雜。它可能是一個文字方塊或一個精心製作的使用者控制項,實現一些業務線 (LOB) 應用程式。

複合應用程式需要

過去的幾年中,我的客戶數目表示同一業務需要:桌面應用程式可以載入外部外掛程式,從而結合幾個 LOB 應用程式下一個"屋頂"。這項規定背後的理由可能會有所不同。多個團隊可能會制定不同的時程表上的應用程式的不同部分。不同的企業用戶可能需要不同的功能集。或者也許用戶端想要確保穩定的"核心"應用程式,同時保持靈活性。一種方式或另一種,對主機的協力廠商外掛程式的要求提出了不止一次在不同的組織內。

有此問題的幾個傳統的解決方案:經典的複合應用程式塊 (CAB)、 託管外接程式框架 (MAF)、 託管可擴充性框架 (MEF) 和棱鏡。另一個解決方案發表在 2013 年 8 月的 MSDN 我的前同事根納季 · Slobodsky 和 Levi Haskell (在看到這篇文章,"體系結構為託管協力廠商.NET 外掛程式," msdn.microsoft.com/magazine/dn342875)。這些解決方案都是極大的價值,並創造了許多有用的應用程式有使用它們。我的這些框架,以及活動使用者,但保持相當一段時間纏著我的一個問題:穩定。

應用程式崩潰。這是一種生活現實。Null 引用、 未處理的異常、 鎖定的檔和損壞的資料庫不會很快消失。一個好的主機應用程式必須能夠生存外掛程式崩潰,繼續前進。A 故障外掛程式必須不允許採取下主機或其他外掛程式。這種保護不需要防彈 ; 我不想防止惡意駭客入侵企圖。然而,簡單的錯誤,如在輔助執行緒中未處理的異常不應該降低主機。

隔離等級

Microsoft.NET 框架的應用程式可以處理協力廠商外掛程式,至少三種不同方式:

  • 沒有隔離:在單個進程與單個應用程式域中運行主機和所有外掛程式。
  • 介質隔離:每個外掛程式在它自己的 AppDomain 中的負載。
  • Strong隔離:載入每個外掛程式在它自己的進程中。

沒有隔離需要最少的保護和最不控制。所有的資料是全域可訪問、 沒有故障保護和有沒有辦法卸載有問題的代碼。最典型的應用程式崩潰的原因是在通過外掛程式創建一個工作執行緒中未處理的異常。

你可以試著保護宿主執行緒與 try/catch 塊,但當它來到外掛程式中所創建的執行緒,就不准了。從.NET Framework 2.0 開始,任何執行緒中未處理的異常將終止進程,和你不能阻止這。有這種看似殘酷的理由:未處理的異常意味著應用程式可能已成為不穩定的並讓它繼續下去是危險的。

介質隔離提供一個插頭的安全和配置的更多控制。您還可以卸載外掛程式,至少當事情不順利和沒有線程正在忙於執行非託管代碼。然而,主機進程仍然不保護從外掛程式崩潰,如所示我的文章,"Appdomain 不會保護主機從外掛程式故障"(bit.ly/1fO7spO)。設計可靠的錯誤處理策略是困難的如果不是不可能的和卸載 AppDomain 不保證的故障。

Appdomain 被發明主辦ASP.NET應用程式作為羽量級的替代辦法的進程。請參見ChrisBrumme 2003 博客發佈後,"Appdomain ("應用程式域"),"在 bit.ly/PoIX1r。ASP.NET適用于容錯能力的相對不幹預的做法。崩潰的 Web 應用程式可以輕鬆地打倒具有多個應用程式的整個工作進程。在這種情況下,ASP.NET簡單地重新開機工作進程,並補發任何掛起的 Web 請求。這是一項合理的設計決定,與沒有面向使用者的 windows 伺服器進程的自己,但它可能無法工作,以及為桌面應用程式。

Strong隔離提供終極的針對故障的保護層級。因為每個外掛程式運行在它自己的進程,這個外掛程式不能崩潰主機,並且他們可以在將終止。同時,此解決方案需要一個相當複雜的設計。應用­陽離子有要處理大量的進程間通信和同步。它還必須封送的 WPF 控制項跨進程邊界,這不是小事。

與在軟體發展中的其他東西一樣選擇隔離等級是一種交換。隔離給你更多的控制和更大的靈活性,但你為它付出與增加應用程式的複雜性和速度較慢的性能更強。

一些框架選擇忽略容錯能力並"不孤立"一級的工作。MEF 和棱鏡是這種做法的好例子。在容錯和微調外掛程式配置不是問題的情況下,這是最簡單的解決方案,工程,因此,是一個正確使用。

很多外掛程式的體系結構中,其中包括一個由 Slobodsky 和哈斯克爾,提出使用介質隔離。他們實現通過 Appdomain 隔離。Appdomain 給宿主開發人員很大程度的控制外掛程式配置和安全。我個人在過去幾年建造了一些基於 AppDomain 的解決方案。如果應用程式需要卸載代碼、 沙箱和配置控制 — 假如容錯能力不是一個問題 — 然後 Appdomain 肯定是要走的路。

MAF 隨時添加在框架之間因為它允許宿主開發人員選擇任何三個隔離等級。在它自己的進程使用 AddInProcess 類,它可以運行外接程式中。不幸的是,AddInProcess 不適合開箱可視元件。有可能將延長 MAF 封可視元件送跨進程,但這將意味著將另一個圖層添加到已經複雜的框架。創建 MAF 外接程式並不容易,和在農林部的另一層,與複雜性很可能變得難以控制。

我建議的體系結構的目的是填補這一空白,並提供一個穩健的宿主解決方案,載入外掛程式在自己的進程中,提供了視覺集成外掛程式和主機之間。

Strong可視元件的隔離

當請求外掛程式的負載時,主機進程生成一個新的子進程。子進程然後載入一個使用者外掛程式類,創建顯示在主機 FrameworkElement (見圖 4)。


圖 4 FrameworkElement 外掛程式進程和主機進程之間的封送處理

FrameworkElement 不能直接進程之間進行封送。它不從 MarshalByRefObject,繼承,也不它標記為 [],可序列化,以便.NET 遠端處理不會封送它。它不被標記該 [ServiceContract] 屬性,以便 Windows 通信基礎 (WCF) 不會封它,要麼送。為了克服這個問題,我使用 System.Addin.FrameworkElementAdapters 類從農林部的一部分的 System.Windows.Presentation 程式集。此類定義的兩種方法:

  • ViewToContractAdapter 方法將 FrameworkElement 轉換為 INativeHandleContract 介面,它可以使用.NET 遠端處理時進行封送處理。裡面的外掛程式進程調用此方法。
  • ContractToViewAdapter 方法將一個 INativeHandleContract 實例轉換回 FrameworkElement。在主機進程內調用此方法。

不幸的是,這兩種方法的簡單組合不會工作難點框。顯然,MAF 旨在 WPF 元件之間 Appdomain 和進程之間不封送。ContractToViewAdapter 方法將失敗並出現以下錯誤的用戶端:

System.Runtime.Remoting.RemotingException:
Permission denied: cannot call non-public or static methods remotely

根本原因是類的,ContractToViewAdapter 方法調用的建構函式中,MS。除­宇。Controls.AddInHost,嘗試強制轉換要鍵入 AddInHwndSourceWrapper 的 INativeHandleContract 遠端處理代理。如果轉換成功,然後它調用內部方法 RegisterKeyboardInputSite 的遠端處理代理上。對代理伺服器跨進程調用內部方法不被允許。這裡是 AddInHost 類的建構函式裡面發生了什麼:

// From Reflector
_addInHwndSourceWrapper = contract as AddInHwndSourceWrapper;
if (_addInHwndSourceWrapper != null)
{
  _addInHwndSourceWrapper.RegisterKeyboardInputSite(
    new AddInHostSite(this)); // Internal method call!
}

若要消除此錯誤,我創建的 NativeContractInsulator 類。此類生活上的伺服器 (外掛程式) 端。它通過從視圖返回到原始的 INativeHandleContract 的所有調用轉發來實現 INativeHandleContract 介面­到­ContractAdapter 方法。然而,與原始的實施,不同的是它不能轉換為 AddInHwndSourceWrapper。因此,端 (主機) 上的鑄造不成功,禁止內部方法調用不會發生。

檢查更詳細的外掛程式體系結構

Plugin.Load 和 Plugin.CreateView 方法創建所有必要的移動部件,實現外掛程式集成。

圖 5 顯示產生的物件圖。它已有些複雜,但每個部分是負責一個特定的角色。他們在一起,確保主機外掛程式系統的無縫和穩健運作。


圖 5 物件圖的載入外掛程式

外掛程式類表示單個外掛程式實例在主機中。它保存視圖屬性,它是插頭中的視覺化的表示形式內的主機進程。外掛程式類創建一個 PluginProcessProxy 的實例,並從它檢索 IRemotePlugin。IRemotePlugin 包含遠端外掛程式控制項的 INativeHandleContract 形式。外掛程式類然後採用該合同,並將它轉換為 FrameworkElement,如下所示 (與為了簡便起見,省略了一些代碼):

public interface IRemotePlugin : IServiceProvider, IDisposable
{
  INativeHandleContract Contract { get; }
}
class Plugin
{
  public void CreateView()
  {
    View = FrameworkElementAdapters.ContractToViewAdapter(
      _remoteProcess.RemotePlugin.Contract);
  }}

PluginProcessProxy 類控制外掛程式進程生命週期從主機內。 它負責啟動外掛程式進程、 創建遠端處理通道和外掛程式進程健康監測。 它還從事 PluginLoader 服務,並從那檢索 IRemotePlugin。

PluginLoader 類的外掛程式進程內運行,並實現外掛程式進程生命週期。 它建立一個遠端處理通道、 啟動 WPF 消息調度程式、 載入外掛程式的使用者、 創建一個 RemotePlugin 實例和手到主機側 PluginProcessProxy 的此實例。

RemotePlugin 類使使用者外掛程式控制跨進程邊界封送。 它將使用者的 FrameworkElement 轉換為 INativeHandleContract,然後環繞此合約,與 NativeHandleContractInsulator 工作圍繞著前面所述的非法方法調用問題。

最後,使用者的外掛程式類實現的 IPlugin 介面。 其主要工作是創建外掛程式在外掛程式進程內部控制。 通常,這將是 WPF 使用者控制項,但它可以是任何 FrameworkElement。

當請求外掛程式的負載時,PluginProcessProxy 類生成一個新的子進程。 子進程可執行檔或者是 PluginProcess.exe 或者 PluginProcess64.exe,根據外掛程式是 32 位還是 64 位。 每個外掛程式的進程在命令列中,以及外掛程式的基目錄收到一個唯一的 GUID:

PluginProcess.exe
  PluginProcess.0DAA530F-DCE4-4351-8D0F-36B0E334FF18
  c:\plug-in\assembly.dll

外掛程式進程設置遠端處理類型的服務的 IPluginLoader 和引發命名"準備好"事件,在這種情況下,PluginProcess.0DAA530F-DCE4-4351-8D0F-36B0E334FF18.Ready。主機然後可以使用 IPluginLoader 方法來載入的外掛程式。

一種替代解決方案將已調入主機一旦準備好的外掛程式進程。這將消除需要準備好的事件,但它將使錯誤處理複雜得多。如果在"載入外掛程式"操作起源于外掛程式的過程,資訊的錯誤還保留在外掛程式的過程。如果出了什麼差錯主機可能從來沒有發現它。因此,我選擇的設計與準備好的事件。

設計的另一個問題是,是否以容納不部署在 WPF 主機目錄下的外掛程式。一方面,在.NET 框架中,不位於應用程式目錄內的程式集載入造成一定的困難。另一方面,我承認外掛程式可能會有自己的部署問題,並不總是有可能部署在 WPF 主機目錄下的外掛程式。此外,一些複雜的應用程式不要時表現正常他們不從其基目錄運行。

出於這些考慮,WPF 主機允許從任何位置載入的外掛程式在本地檔案系統上。要實現這一目標,外掛程式進程在其應用程式基目錄設置為插頭中的基目錄中學 AppDomain 中執行幾乎所有的操作。這將創建 WPF 主機程式集的 AppDomain 中載入的問題。這可以實現至少四種方式:

  • 將 WPF 主機程式集放在全域組件快取 (GAC)。
  • 在外掛程式進程的 app.config 檔中使用程式集重定向。
  • 將重寫負載 WPF 主機程式集使用的 LoadFrom CreateInstanceFrom 之一。
  • 使用非託管宿主 API 來啟動 CLR 在外掛程式過程中與所需的配置。

這些解決方案的每個有利弊。將 WPF 主機程式集放在 gac 中需要管理存取權限。雖然 GAC 是清潔的解決方案,為安裝需要管理員許可權可以是頭痛在企業環境中,所以我竭力想避免這種情況。程式集重定向也是有吸引力的但設定檔然後將取決於 WPF 主機的位置。這使 xcopy 安裝是不可能的。創建非託管宿主專案似乎是大維護風險。

於是使用 LoadFrom 方法。此方法的大缺點是 WPF 主機程式集最終會在 LoadFrom 上下文中的 (參閱博客文章,在"選擇綁定上下文,"由蘇珊 · 庫克在 bit.ly/cZmVuz)。為了避免任何綁定問題,需要重寫外掛程式的 AppDomain 中的 AssemblyResolve 事件,所以插頭的代碼可以更容易地找到 WPF 主機程式集。

發展中國家的外掛程式

你可以實現一個外掛程式作為類庫 (DLL) 或可執行檔 (EXE)。在 DLL 的情況下,步驟如下:

  1. 建立新的類別庫專案。
  2. 引用 WPF 程式集 PresentationCore、 PresentationFramework、 System.Xaml 和 WindowsBase。
  3. 添加到 WpfHost.Interfaces 程式集的引用。請確保"複製本地"設置為 false。
  4. 創建一個新的 WPF 使用者控制項,例如 MainUserControl。
  5. 創建一個名為外掛程式從 IKriv.WpfHost.Interfaces.PluginBase 派生的類。
  6. 向主機的 plugins.xml 檔中添加一個條目,您的外掛程式。
  7. 編譯您的外掛程式並運行主機。

最小的外掛程式類看起來像這樣:

public class Plugin : PluginBase
{
  public override FrameworkElement CreateControl()
  {
    return new MainUserControl();
  }
}

或者,一個外掛程式可以作為實現一個可執行檔。在這種情況下的步驟如下:

  1. 建立 WPF 應用程式
  2. 創建一個 WPF 使用者控制項,例如,MainUserControl。
  3. 將 MainUserControl 添加到應用程式的主視窗。
  4. 添加到 WpfHost.Interfaces 程式集的引用。請確保"複製本地"設置為 false。
  5. 創建一個名為外掛程式從 IKriv.WpfHost.Interfaces.PluginBase 派生的類。
  6. 向主機的 plugins.xml 檔中添加一個條目的您的外掛程式。

您的外掛程式類的樣子就像前面的示例中,和主視窗 XAML 應包含什麼但對 MainUserControl 的引用:

<Window x:Class="MyPlugin.MainWindow"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:MyProject"
  Title="My Plugin" Height="600" Width="766" >
  <Grid>
    <local:MainUserControl />
  </Grid>
</Window>

實施這樣的外掛程式可以作為獨立的應用程式或運行在主機內。這簡化了調試不與相關的主機集成外掛程式代碼。這種"雙頭"外掛程式的類別圖表所示圖 6


圖 6 雙頭外掛程式的類別圖表

這種技術還提供途徑,為現有的應用程式對外掛程式的快速轉換。你需要做的唯一事情是轉換為使用者控制項的應用程式的主視窗。如前所示,然後具現化外掛程式類中的使用者控制項。太陽能系統外掛程式附帶的代碼下載中是這種轉換的一個例子。整個轉換過程花了不到一個小時。

因為該外掛程式不是一個獨立的應用程式,但相反由主機發起,調試不可能直截了當。您可以開始調試主機,但Visual Studio無法尚未自動附加的子進程。你可以要麼手動調試器附加到該外掛程式進程一旦它正在運行或通過更改 PluginProcess app.config 到第 4 行拆分為上啟動調試器的外掛程式進程:

<add key="BreakIntoDebugger" value="True" />

另一種方法是創建您的外掛程式作為獨立的應用程式,如前面所述。 然後可以調試外掛程式作為獨立的應用程式,的大多數只會定期檢查與 WPF 主機集成工作正常。

如果外掛程式進程中斷到調試器啟動時,要準備好事件超時時間增加通過更改 WpfHost app.config 檔中,第 4 行,如下所示:

<add key="PluginProcess.ReadyTimeoutMs" value="500000" />

示例外掛程式附帶的代碼下載中可用的清單和說明他們要做的所示圖 7

圖 7 示例外掛程式,這在附帶的代碼下載中可用

外掛程式專案 它做什麼
BitnessCheck 演示如何一個外掛程式可以運行 32 位或 64 位
星系 演示老 WPF 演示應用程式轉換為一個外掛程式
TestExceptions 演示如何為使用者執行緒異常處理和工作者執行緒異常
UseLogServices 演示如何使用主機服務和外掛程式服務

主機服務和外掛程式服務

在現實世界中,外掛程式通常需要使用由主機提供的服務。我演示這種情況下在外掛程式代碼下載中的 UseLogService。外掛程式的類可能具有一個預設建構函式或採用 IWpfHost 類型的一個參數的建構函式。在後者的情況下,外掛程式載入程式會通過 WPF 主機對外掛程式的實例。介面 IWpfHost 的定義如下:

public interface IWpfHost : IServiceProvider
{
  void ReportFatalError(string userMessage,
     string fullExceptionText);
  int HostProcessId { get; }
}

我在我的外掛程式中使用的 IServerProvider 部分。 IServiceProvider 是一個標準的.NET 框架介面在 mscorlib.dll 中定義:

public interface IServiceProvider
{
  object GetService(Type serviceType);
}

我會使用它在我的外掛程式來從主機獲得 ILog 服務:

class Plugin : PluginBase
{
  private readonly ILog _log;
  private MainUserControl _control;
  public Plugin(IWpfHost host)
  {
    _log = host.GetService<ILog>();
  }
  public override FrameworkElement CreateControl()
  {
    return new MainUserControl { Log = _log };
  }
}

該控制項然後可以使用 ILog 主機服務寫入主機的日誌檔。

主機還可以使用所用的外掛程式提供的服務。我定義了一種叫做 IUnsavedData,事實證明,在現實生活中很有用的服務。通過實現此介面,外掛程式可以定義未保存的工作項的清單。如果外掛程式或整個宿主應用程式已關閉,主機會詢問使用者,是否他想放棄未保存的資料,如中所示圖 8


圖 8 使用 IUnsavedData 服務

IUnsavedData 介面定義,如下所示:

public interface IUnsavedData
{
  string[] GetNamesOfUnsavedItems();
}

外掛程式作者不需要顯式實現 IServiceProvider 介面。 它足以在外掛程式中實現 IUnsavedData 介面。 PluginBase.GetService 方法將照顧將其返回到主機。 我的 UseLogService 專案代碼下載中提供示例 IUnsavedData 實現,與相關的代碼所示:

class Plugin : PluginBase, IUnsavedData
{
  private MainUserControl _control;
  public string[] GetNamesOfUnsavedItems()
  {
    if (_control == null) return null;
    return _control.GetNamesOfUnsavedItems();
  }
}

日誌記錄和錯誤處理

WPF 主機和外掛程式程式的 %TMP%\WpfHost 目錄中創建日誌。 WPF 主機寫入 WpfHost.log 和每個外掛程式主機進程寫入 PluginProcess.Guid.log ("Guid"不是一部分的文本的名稱,但擴展到實際的 Guid 值)。 定制的日誌服務。 避免使用 log4net 或 NLog 等熱門日誌記錄服務,為了使該示例自包含的事。

一個外掛程式的過程還將結果寫入其主控台視窗,您可以通過更改到 WpfHost app.config 的第 3 行顯示:

<add key="PluginProcess.ShowConsole" value="True" />

我採取了非常謹慎,向主機報告的所有錯誤和優雅地處理它們。 主機監視外掛程式進程並將關閉外掛程式視窗,如果一個外掛程式進程死了。 同樣,一個外掛程式的過程監視其主機並將關閉如果主機死了。 記錄所有錯誤,所以檢查日誌檔以進行故障排除説明很大。

它是重要的是要記住在主機和外掛程式之間傳遞的一切必須要麼 [可序列化] 或從 MarshalByRefObject 派生的類型。 否則,.NET 遠端處理不能封送的物件在各方之間。 類型和介面必須已知這兩個當事方,所以通常只有內置類型和類型從 WpfHost.Interfaces 或 PluginHosting 程式集是安全的封送處理。

版本控制

WpfHost.exe、 PluginProcess.exe 和 PluginHosting.dll 緊密耦合,並應同時釋放。 幸運的是,外掛程式代碼不依賴于任何這些三個程式集,因此可以在幾乎任何方式修改它們。 例如,你可以輕鬆地更改同步機制或準備好的事件的名稱而不影響外掛程式。

WpfHost.Interfaces.dll 元件必須極端小心進行版本控制。 它應引用,但不是包含在外掛程式代碼 (CopyLocal = false),因此,為此程式集始終二進位僅來自主機。 因為我特別不想通過並存執行,我沒有給此程式集強式名稱。 只有一個版本的 WpfHost.Interfaces.dll 應該是在整個系統中存在。

一般情況下,應將外掛程式視為協力廠商代碼不在主機作者的控制之下。 修改或甚至一次重新編譯所有外掛程式可能很難或不可能。 因此,新版本的介面程式集必須是二進位相容與以前的版本,與許多重大更改舉行到絕對最低限度。

添加新類型和程式集的介面通常是安全。 任何其他的修改,其中包括介面或新值的枚舉,添加新的方法可能會破壞二進位相容性,應避免。

即使託管程式集沒有強式名稱,它是重要的是要遞增的版本號進行任何更改後,然而小,所以沒有兩個程式集具有相同的版本號有不同的代碼。

一個好的開始點

我參考體系結構提供了在這裡不是外掛程式的主機集成、 生產品質的框架,但它相當接近,可以作為寶貴的起始點,您的應用程式。

該體系結構照顧的樣板檔尚未困難的考慮,如外掛程式進程生命週期中,整個流程、 交流與主機和外掛程式,除其他外之間的服務發現機制的封送處理外掛程式控制項。 大多數設計的解決方案和替代方法不是任意的。 他們基於建設為 WPF 的複合應用程式的實際經驗。

你最有可能就會想要修改的可視外觀,主機、 日誌記錄機制替換為您的企業中使用的標準一、 添加新的服務和可能的更改外掛程式被發現的方式。 許多其他的修改和改進是可能的。

即使你不創建 WPF 的複合應用程式,您可能仍然享受作為示範如何強大而靈活的.NET 框架可以和如何可以將熟悉元件組合中有趣的、 意外的和富有成效的方式審查此體系結構。

Ivan Krivyakov 是湯姆森路透集團的技術線索。他是一個動手開發商和建築師的專業建設和提高複雜的業務線 (LOB)Windows Presentation Foundation應用程式。

感謝以下 Microsoft 技術專家對本文的審閱:博士。 James麥卡弗裡、 丹尼爾 · 佩雷斯特和Kevin贖金
Kevin贖金在微軟工作了 14 年對若干專案,其中包括:公共語言運行庫、Microsoft Business Framework、Windows Vista和 Windows 7,託管可擴充性框架和基地的類庫。他目前正在在管理上以語言 Visual FSharp。

博士。 James麥卡弗裡為微軟在華盛頓州雷德蒙德,校園的工作。 他曾對幾個微軟的產品,包括互聯網資源管理器和 MSN 搜索。 他是作者的".NET 測試自動化食譜"(Apress,2006 年),並可以在達成 jammc@microsoft.com

自 2008 年加入微軟,丹尼爾 · 佩雷斯特曾在託管可擴充性框架 (MEF)、 可擕式類圖書館 (PCL) 和 Microsoft.NET 框架為 Windows 應用程式商店。 他提出了在 MS TechEd、 生成和各種本機群組、 代碼營地和會議。 在他的閒置時間,他喜歡的電腦遊戲,閱讀、 徒步旅行、 雜耍和 footbagging (沒)。他的博客可以發現在 blogs.msdn.com/b/dsplaisted/ 他可以達成和 daplaist@microsoft.com."