.Net动态加载DLL

时间:2010-03-01 23:35:41

标签: vb.net reflection dll assemblies

我正在尝试编写一些代码,允许我根据应用程序设置动态地将DLL加载到我的应用程序中。我们的想法是在应用程序设置中设置要访问的数据库,然后加载相应的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

编辑:Vlad示例的新代码:

    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中的模块,从Vlad的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)

编辑:基于Paul Kohler代码的实施

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.CRDataSource.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

3 个答案:

答案 0 :(得分:4)

版本2 - 此示例从当前目录加载DLL。 有2个项目,1个控制台应用程序项目和一个“模块”项目(模块'将其DLL'coppies'到控制台应用程序的工作目录。)

下面的示例演示了如何动态加载实现接口的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

在“Sample1 DLL”中(引用IModule的'VB.LoaderDemo')

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

答案 1 :(得分: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)

答案 2 :(得分:1)

您的代码中需要注意的一些事项

  • 通过调试并检查 装配正确加载,以防万一 由于依赖性检查而失败
  • 使用GetExportedType代替使用GetType,因此您可以使用较小的子集来迭代
  • CreateInstance应该使用您的loadedType而不是界面(您无法从界面创建对象)
  • 就个人而言,我不喜欢命名我的变量屁股,我会缩短它来代替组装:)