.Net动态加载DLL

her*_*iod 8 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)
Run Code Online (Sandbox Code Playgroud)

我有我的接口(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
Run Code Online (Sandbox Code Playgroud)

编辑: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
Run Code Online (Sandbox Code Playgroud)

上面是每个DLL中的Module,从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)
Run Code Online (Sandbox Code Playgroud)

编辑:基于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
Run Code Online (Sandbox Code Playgroud)

我用以下代码得到以下错误:

无法将类型为"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
Run Code Online (Sandbox Code Playgroud)

加载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
Run Code Online (Sandbox Code Playgroud)

Pau*_*ler 4

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

下面的示例简单演示了动态加载实现接口的 DLL。该IModule接口仅报告其名称。将创建当前目录中以“.Module.dll”结尾的 DLL 中找到的PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll")任何实例的实例。IModule它是直接从 Mini SQL 查询中反映出来的 VB.NET 版本。

考虑到这一点,例如:

Dim modules As IModule() = PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory, "*.Server.dll")
Run Code Online (Sandbox Code Playgroud)

应该可以满足你的要求。然后你只需要选择执行哪一个即可!

代码:

在“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
Run Code Online (Sandbox Code Playgroud)

在“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
Run Code Online (Sandbox Code Playgroud)

输出是:

> Sample 1, Module 1
Run Code Online (Sandbox Code Playgroud)