2009-08-05 57 views
1

好的,所以我有一個使用插件的.NET項目。這些插件被實現爲類庫(DLL)項目,每個項目都建立在它們自己的文件夾中。主項目不需要運行插件DLL,但如果它們可用,它們將用於各種可選功能。來自插件的類由Type.GetType()加載。但是,在測試軟件時,爲了我自己的目的,我希望能夠同時開發插件和主應用程序。我創建了一個引用所有項目的「主」解決方案文件,因此我可以設置斷點並跨越裝配邊界跨步。然而,問題是插件建立到他們自己的目錄中,所以我不得不以某種方式將插件DLL文件放在某處,其中Type.GetType()可以找到它們。我應該如何「可選地」在另一個.NET項目中引用一個類庫項目?

編輯:爲了澄清,我的代碼已經列舉了同一個目錄中的.exe DLL文件和(使用Assembly.LoadFile()Assembly.GetExportedTypes()Type.IsAssignableFrom())查找匹配給定的接口的類。然後用戶選擇插件啓用,並使用Type.AssemblyQualifiedName將這些選擇保存到數據庫。最後,當我需要使用插件提供的功能時,我使用Type.GetType()加載插件類。所有這些完全按照預期工作。只是當我構建應用程序時,我得到了一個沒有插件的.exe副本。我正在尋找一種方法來構建項目和解決方案文件,以便我可以將主應用程序和插件項目分開,同時仍然可以在Visual Studio中一起調試它們。那有意義嗎?

到目前爲止,這些都是我的想法:

  • 到插件項目添加從主體工程引用。
    這樣做的好處是它可以在我的調試環境中按照預期工作,但是當我的應用程序部署時沒有插件時會導致問題嗎?我使用Dependency Walker進行了檢查,似乎沒有任何由項目引用創建的直接DLL依賴項。這裏有什麼隱藏的問題要知道嗎?

  • 更改所有要構建到相同目標目錄的插件項目
    這似乎工作得不錯,但它也混淆了我的構建樹,並且它會使開發一個項目時沒有簽出其他項目(現在它們都是Subversion中的所有兄弟文件夾)更困難。

  • 添加後期構建腳本以將插件複製到另一個目錄
    這實際上是我現在正在做的,它運作得很好,但它似乎非常駭人而脆弱。我想切換到另一種方法。

  • 找到一種方法爲Type.GetType()搜索其他目錄中的DLL
    這是類似我如何在Unix系統上使用LD_LIBRARY_PATH。顯然,我只想爲Debug版本啓用此功能,因爲在發佈模式下,這可能會在用戶系統上導致大量細微問題。但這甚至可能嗎?如果是這樣,怎麼樣?

有趣的是,在this tutorial關於這個問題,它說:

做的第一件事是參考我們剛剛創建的類庫,並設置生成輸出到同一目錄。

這對我來說似乎並不理想。有沒有更好的辦法?

+0

在GAC中註冊他們? – 2009-08-05 19:33:28

回答

1

設置您的項目,以便將可執行文件編譯爲1個位置,並將您的「插件」編譯到相同的位置。你可以使用連接來使「bin」成爲你最終位置的路徑。

也在您的插件項目中設置您的可執行文件作爲調試主機。

如果你還沒有這樣做,確保你的接口沒有得到重新編譯你的每個編譯的exe(如果它是強命名),因爲這會迫使你必須重新編譯每個插件當你重新編譯你的主機exe文件。

+0

是的,這是我上面的第二個選項。不是最優的,因爲它意味着在我的源代碼樹中移動,但它確實看起來像一個可操作的選項,另外,+1指出插件也依賴於接口的版本。 – 2009-08-05 23:46:40

1

對於插件模型,我會看看Activator.CreateInstanceActivator.CreateInstanceFrom方法,甚至看看Assembly.LoadFrom。這些方法允許您從「傳統」輸出目錄(bin, debug, release)以外的目錄中加載程序集中的類型。

強制Type.GetType找到您的「插件」程序集確實取決於您如何修改加載程序搜索程序集的策略。例如,您可以設置AppDomain.PrivateBinPath屬性來更改裝載程序將尋找參考程序集的子目錄。

關於第一點「添加對插件項目的引用」。那麼,這會讓人陷入一個困難的境地,以阻止未來的擴展。如果想要生成一個新的插件?哎呀,現在你沒有對它的引用,所以我猜我的插件不會工作呢?這就是爲什麼需要另一種方法。我們可以「探測」插件程序集。


在插件模型中,所有的插件應反對的通用接口(或抽象類),這真的是實施你正在尋找任何新的插件加載。例如:

private void LoadAllPlugins(string path) 
{ 
    foreach(string file in Directory.GetFiles(path, "*.dll")) 
    { 
     Assembly a = Assembly.LoadFrom(file); 
     foreach(Type t in a.GetTypes()) 
     { 
      if (t.GetInterface("IMyPluginInterface", true)) 
      { 
       // we now have a potential plugin type that implements required functionality 
      } 
     } 
    } 
} 

最後,.NET 3.5有一個插件模型的支持(System.AddIn),並檢查了來自微軟的託管擴展框架版本,允許開發者構建簡單組合的插件(http://www.codeplex.com/MEF

+0

感謝您的回覆。事實上,我用於加載插件的代碼與您的代碼示例非常相似。 (我使用'typeof(IMyInterface).IsAssignableFrom(t)'而不是't.GetInterface(「IMyInterface」)',但這個想法是相似的。)我的問題不是如何加載插件程序集,而是如何來構建我的項目和解決方案文件,使它們更容易使用(特別是在插件和應用程序代碼之間來回調用)。那有意義嗎? – 2009-08-05 21:54:14

+0

啊。它是有道理的,我可以提供我已經嘗試過的插件模型:簡單地構建每個插件(無論是腳本還是一些後期構建事件),並將其輸出設置爲與主應用程序相同的輸出(或約定的輸出目錄)。 從主項目進行調試時,只要使用類型,Visual Studio就有足夠的智能來查找每個插件的調試信息 – 2009-08-06 08:45:38

+0

調試信息文件(.PDB)至少應該可以從主項目訪問,否則調試插件將無法工作。在大多數情況下,如果調試信息沒有通過,可以嘗試將它們複製到主應用程序的輸出目錄。 – 2009-08-06 08:48:29

1

只需在解決方案中包含插件項目,但插件僅依賴於「應用程序」項目。保持一個重複的解決方案,但只有應用程序項目並確保構建乾淨,從而強化這一點。您可能還需要考慮一個後期構建步驟,該步驟會嘗試單獨構建每個插件項目而沒有其他項目。

另外一個檢查是,如果你想強制向後兼容,或者至少知道什麼時候發生了明顯的中斷。如果你有一個測試項目,通過dll引用而不是項目引用(測試項目中的一個小問題)來引用插件,這是最簡單的。然後通過在項目設置中修改您的引用路徑,您可以指向舊版本的插件。作爲腳本測試的一部分正確執行此操作也相當簡單。

我會第二次使用MEF系統的建議你的插件在編譯時有效地知道,只是沒有你計劃加載的dll的特定版本。

1

我前段時間做了一個plugin system使用反射。該機制類似於在drupal中使用的機制,我發現它非常靈活。

我有兩篇文章解釋它:Sistema de plugins con C#. Parte I. ConceptosSistema de plugins con C#. Parte II. El código explicado但我還沒有時間翻譯它們(所以它們是西班牙文)。代碼仍然用英語評論(如果我沒記錯的話)。

我會盡力在這裏解釋我的方法。

基本上你有插件,因爲它們通常是定義的類,它們實現了一個簡單的接口。該接口描述了用於加載,卸載,安裝,卸載和配置插件的方法。

然後你有兩種插件的方面。

首先,插件可以通過實現公共(已知)接口並使用「ServiceAttribute」將其自身標記爲服務來爲其他類提供服務。然後,插件系統將註冊該服務並將其公開給任何有興趣使用它的人。例如:

ICrypt crypto = Plugins.Service["Crypt"] as ICrypt; 
if (crypto != null) 
    crypto.Crypt(data, key, etc) 

另一方面,你有鉤。掛鉤是插件事件。你有兩個屬性HookAttribute和HookableAttribute。

當你聲明一個方法爲Hookable時,你會說你期望它被其他插件攔截。例如

[Hookable] 
public event TextChanged OnTextChanged; 
//..... 
if (OnTextChanged != null) 
    OnTextChanged(ref myText); 

並且在其它插件

[Hooks("OnTextChanged")] 
public bool MyTextChanged(ref myText) 
{ 
    myText = "Hello from the plugin"; 
    return true; 
} 

鉤簽名(委託)可以只要簽名匹配是未知的。插件系統再次處理這些匹配,並將其帶到最後。

它還包括一些屬性來表示插件之間的依賴關係以及自動檢測和加載以及那種事情。該項目本身是LGPL,所以你可能會發現它的一些想法,甚至更好...貢獻:P

順便說一下,我使用插件子文件夾,我放置插件。只要插件項目是我正在開發的解決方案的一部分,我從VS中調試它們就沒有問題。我不需要引用它們或任何其他的事情,如果插件是解決方案的一部分,那麼我可以調試它。

+0

這看起來很有趣。現在將這個用於我目前的項目已經有點晚了,但我會記住它以備將來使用。對於任何感興趣的人,Google翻譯都是可以理解的:http://translate.google.com/translate?u=http://www.stackframe.net/es/content/01-2009/sistema-de-plugins -CON-C-單方面-I-CONCEPTOS&SL = ES&TL = EN&HL = EN&即= UTF-8。 (儘管我喜歡「utilizado unalibrería」如何被翻譯爲「take a used bookstore」.. – 2009-08-05 23:43:09

相關問題