2010-03-01 80 views
6

我想寫一些代碼,這將允許我動態加載DLL到我的應用程序,具體取決於應用程序設置。這個想法是,要訪問的數據庫在應用程序設置中設置,然後加載適當的DLL並將其分配給接口的實例,供我的應用程序訪問。.Net動態加載DLL

這是我的時刻代碼:

 Dim SQLDataSource As ICRDataLayer 
    Dim ass As Assembly = Assembly. _ 
    LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll") 

    Dim obj As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True) 
    SQLDataSource = DirectCast(obj, ICRDataLayer) 

    MsgBox(SQLDataSource.ModuleName & vbNewLine & SQLDataSource.ModuleDescription) 

我有我的接口(ICRDataLayer)和SQLServer.dll包含此接口的實現。我只想加載程序集並將其分配給SQLDataSource對象。

上述代碼不起作用。沒有例外拋出,即使Msgbox沒有出現。 我會期望至少該消息框沒有任何內容出現,但即使這樣也不會發生!

有沒有一種方法來確定加載的程序集是否實現了特定的接口。我嘗試了下面的內容,但這似乎也沒有做任何事情!

 For Each loadedType As Type In ass.GetTypes 
     If GetType(ICRDataLayer).IsAssignableFrom(loadedType) Then 
      Dim obj1 As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString, True) 
      SQLDataSource = DirectCast(obj1, ICRDataLayer) 
     End If 
    Next 

編輯:從弗拉德的例子新的代碼:

Module CRDataLayerFactory 
    Sub New() 
    End Sub 
    ' class name is a contract, 
    ' should be the same for all plugins 
    Private Function Create() As ICRDataLayer 
     Return New SQLServer() 
    End Function 
End Module 

以上是在每個DLL,從弗拉德的C#示例轉換模塊。

下面是我的代碼在DLL帶來:

Dim SQLDataSource As ICRDataLayer 
    Dim ass As Assembly = Assembly. _ 
    LoadFrom("M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\SQLServer.dll") 

    Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True) 
    Dim t As Type = factory.GetType 
    Dim method As MethodInfo = t.GetMethod("Create") 
    Dim obj As Object = method.Invoke(factory, Nothing) 
    SQLDataSource = DirectCast(obj, ICRDataLayer) 

編輯:實現基於保羅·科勒的代碼

Dim file As String 
     For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly) 
      Dim assemblyType As System.Type 
      For Each assemblyType In Assembly.LoadFrom(file).GetTypes 

       Dim s As System.Type() = assemblyType.GetInterfaces 
       For Each ty As System.Type In s 

        If ty.Name.Contains("ICRDataLayer") Then 
         MsgBox(ty.Name) 
         plugin = DirectCast(Activator.CreateInstance(assemblyType), ICRDataLayer) 
         MessageBox.Show(plugin.ModuleName) 
        End If 
       Next 

我得到這個代碼以下錯誤:

無法投射'SQLServer.CR'類型的對象DataSource.SQLServer'鍵入'DynamicAssemblyLoading.ICRDataLayer'。

實際的DLL位於與我的實現代碼相同的解決方案中的另一個名爲SQLServer的項目中。 CRDataSource是一個名稱空間,SQLServer是該DLL的實際類名稱。 SQLServer類實現ICRDataLayer,所以我不明白爲什麼它無法投射它。 這裏的命名很重要,我不會想到它會是。 PluginUtility的


最後的工作代碼

內容:

enter code here Public Shared Function GetInstances1(Of Type)(ByVal baseDir As String, ByVal searchPattern As String) As System.Type() 
    Dim tmpInstances As New List(Of Type) 
    Try 
     Dim file As String 
     For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly) 
      Dim assemblyType As System.Type 
      For Each assemblyType In Assembly.LoadFrom(file).GetTypes 

       Dim s As System.Type() = assemblyType.GetInterfaces 
       Return s.ToArray() 

      Next 
     Next 
    Catch exp As TargetInvocationException 
     If (Not exp.InnerException Is Nothing) Then 
      Throw exp.InnerException 
     End If 
    End Try 
End Function 

代碼加載DLL:

enter code here 
    Dim basedir As String = "M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer\bin\Debug\" 
    Dim searchPattern As String = "*SQL*.dll" 
    Dim plugin As CRDataLayer.ICRDataLayer 

    Try 
     Dim file As String 
     For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly) 
      Dim assemblyType As System.Type 
      For Each assemblyType In Assembly.LoadFrom(file).GetExportedTypes 

       If assemblyType.GetInterface("CRDataLayer.ICRDataLayer") IsNot Nothing Then 
        plugin = DirectCast(Activator.CreateInstance(assemblyType), CRDataLayer.ICRDataLayer) 
        MessageBox.Show(plugin.ModuleDescription) 
       End If 

      Next 
     Next 
    Catch exp As TargetInvocationException 
     If (Not exp.InnerException Is Nothing) Then 
      Throw exp.InnerException 
     End If 
    Catch ex As Exception 
     MsgBox(ex.Message) 
     Clipboard.SetText(ex.Message) 
    End Try 
+0

不要使用'ty.Name.Contains( 「ICRDataLayer」)' - 檢查它 「實現類型」 與'assemblyType.GetInterface( 「命名空間etc.ICRDataLayer」) '(seehttp://msdn.microsoft.com/en-us/library/tcctb9t8.aspx)如果它沒有被當前類型實現,它會給你'Nothing'。此外,異常似乎表明接口未實現,您是否使用共享DLL - 它需要是同一個DLL。 PK – 2010-03-08 23:51:21

+0

再次檢查下面的解決方案... – 2010-03-09 03:00:13

+1

它的工作原理! 我最終使用了你的代碼Paul,它的工作原理與我需要的完全一樣。 對於任何未來可能會遇到此線程的人,我會將我的最終代碼添加到原始帖子中,希望他們能夠比我更容易地使用它。 謝謝你的幫助 – hermiod 2010-03-10 23:44:27

回答

2

版本2 - 此示例從它當前目錄加載一個DLL。 有2個項目,1個控制檯應用程序項目和一個「模塊」項目(模塊將其DLL複製到控制檯應用程序的工作目錄)。

下面的示例簡單地演示了動態加載實現接口的DLL。 IModule接口只是報告它的名字。 PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll")將創建在以「.Module.dll」結尾的當前目錄中的DLL中找到的任何IModule實例的實例。這是直接從Mini SQL Query中反射出來的VB.NET版本。

考慮到這一點類似:

Dim modules As IModule() = PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory, "*.Server.dll") 

應滿足您的要求。那麼你只需要選擇執行哪一個!

的代碼:

在 「VB.LoaderDemo Colsole應用」

' IModule.vb 
Public Interface IModule 
    Property ModuleName() As String 
End Interface 

' PlugInUtility.vb 
Imports System.IO 
Imports System.Reflection 
Public Class PlugInUtility 
    Public Shared Function GetInstances(Of T)(ByVal baseDir As String, ByVal searchPattern As String) As T() 
     Dim tmpInstances As New List(Of T) 
     Try 
      Dim file As String 
      For Each file In Directory.GetFiles(baseDir, searchPattern, SearchOption.TopDirectoryOnly) 
       Dim assemblyType As Type 
       For Each assemblyType In Assembly.LoadFrom(file).GetTypes() 
        If (Not assemblyType.GetInterface(GetType(T).FullName) Is Nothing) Then 
         tmpInstances.Add(DirectCast(Activator.CreateInstance(assemblyType), T)) 
        End If 
       Next 
      Next 
     Catch exp As TargetInvocationException 
      If (Not exp.InnerException Is Nothing) Then 
       Throw exp.InnerException 
      End If 
     End Try 
     Return tmpInstances.ToArray() 
    End Function 
End Class 

' MainModule.vb 
Module MainModule 
    Sub Main() 
     Dim plugins As IModule() = PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll") 
     Dim m As IModule 
     For Each m In plugins 
      Console.WriteLine(m.ModuleName) 
     Next 
    End Sub 
End Module 

在 「樣本DLL」(引用 'VB.LoaderDemo' 的的IModule)

Imports VB.LoaderDemo 

Public Class MyModule1 
    Implements IModule 

    Dim _name As String 

    Public Sub New() 
     _name = "Sample 1, Module 1" 
    End Sub 

    Public Property ModuleName() As String Implements IModule.ModuleName 
     Get 
      Return _name 
     End Get 
     Set(ByVal value As String) 
      _name = value 
     End Set 
    End Property 

End Class 

輸出是:

> Sample 1, Module 1 
+0

嗨保羅,我試圖實現你的代碼,我的努力已被附加到主要帖子以及錯誤信息,因爲沒有足夠的空間來適應它的評論,你也沒有在評論中格式化。 至少我現在得到一個錯誤,我只是不知道爲什麼! – hermiod 2010-03-08 22:43:42

+0

完成了一個重寫... – 2010-03-09 02:59:50

3

是在你的DLL中定義的類型ICRDataLayer要加載?如果是這樣,你似乎已經在你的項目設置中引用了DLL。

您需要使用只是反映工作:

Dim obj As Object = ass.CreateInstance("ICRDataLayer", True) 
Dim t as Type = obj.GetType() 
Dim method as MethodInfo = t.GetMethod("DoSomething") 
method.Invoke(obj, ...) 

編輯:如果ICRDataLayer在應用程序中實現,而該插件僅僅實現了接口,你需要的插件爲您提供一個工廠(對不起,C#代碼,我不熟悉VB.NET的語法)

// in each of plugins: 
static class CRDataLayerFactory // class name is a contract, 
{        // should be the same for all plugins 
    static ICRDataLayer Create() 
    { 
     return new CRDataLayerImplementation(); 
    } 
} 

應用程序的代碼應該是這樣的:

Dim factory As Object = ass.CreateInstance("CRDataLayerFactory", True) 
Dim t as Type = factory.GetType() 
Dim method as MethodInfo = t.GetMethod("Create") 
Dim obj as Object = method.Invoke(factory, null) 

SQLDataSource = DirectCast(obj, ICRDataLayer) 
+0

感謝你的代碼弗拉德,它現在有了更多的意義。我會盡可能地嘗試。 – hermiod 2010-03-04 11:43:15

+0

@hermiod:不客氣 – Vlad 2010-03-04 14:32:57

+0

我試過你的代碼Vlad。有趣的是,如果我調試了它,它會到達't'類型'行並且不會在它後面執行任何代碼。沒有例外,沒有錯誤,它只是停止處理該行後面的任何代碼並提交表單。 即使我把一些簡單的東西放在MessageBox.Show(「Hello」)中,它也不會被執行。 正如我在對nobugz的回覆中所述,我認爲VS可能會被打破!將嘗試修復安裝,然後再次嘗試您的代碼。 – hermiod 2010-03-05 21:31:44

1

有幾件事情,尋找在你的代碼

  • 調試通過,並檢查 組件正確加載,萬一 它失敗,因爲依賴而不是使用的GetType的檢查
  • ,使用GetExportedType,所以你有更小的子集迭代通過
  • CreateInstance應該使用你的loadedType而不是接口(你不能從一個接口創建對象)
  • 個人而言,我不喜歡命名我的變量屁股,我會縮短它來組裝,而不是:)