.Net动态加载DLL [英] .Net Dynamically Load DLL
问题描述
我正在尝试编写一些代码,这将允许我根据应用程序设置将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不出现。
我至少会发现消息框中没有显示任何内容,但即使这样也不会发生!
有没有办法确定装载组件实现了一个特定的接口我试过下面,但这似乎也没有做任何事情!
对于每个loadedType作为类型在ass.GetTypes
如果GetType(ICRDataLayer).IsAssignableFrom(loadedType)然后
Dim obj1 As Object = ass.CreateInstance(GetType(ICRDataLayer).ToString,True)
SQLDataSource = DirectCast(obj1,ICRDataLayer)
结束如果
下一个
编辑:Vlad的例子的新代码
模块CRDataLayerFactory
Sub New()
End Sub
'类名是一个合同,
'应该是相同的所有插件
私有函数Create()作为ICRDataLayer
返回新的SQLServer()
结束功能
结束模块
以上是每个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方法As MethodInfo = t.GetMethod(Create)
Dim obj As Object = method.Invoke工厂,没有)
SQLDataSource = DirectCast(obj,ICRDataLayer)
编辑:基于保罗·科勒的代码执行
Dim file As String
对于每个文件在Directory.GetFiles(baseDir,searchPattern,SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
对于每个assemblyType在Assembly.LoadFrom(文件).GetTypes
Dim s As System.Type()= assemblyType.GetInterfaces
对于每个ty As System.Type在s
如果ty.N ame.Contains(ICRDataLayer)然后
MsgBox(ty.Name)
plugin = DirectCast(Activator.CreateInstance(assemblyType),ICRDataLayer)
MessageBox.Show(plugin.ModuleName)
结束如果
下一个
我收到以下错误代码:
无法将类型为SQLServer.CRDataSource.SQLServer的对象转换为键入DynamicAssemblyLoading.ICRDataLayer。
实际DLL在与我的实现代码相同的解决方案中与另外一个名为SQLServer的项目。 CRDataSource是一个命名空间,SQLServer是DLL的实际类名。
SQLServer类实现ICRDataLayer,所以我不明白为什么它不能转换它。
这里的命名很重要,我不会以为会是这样的。
工作代码
PluginUtility的内容:
enter公共共享函数GetInstances1(Of Type)(ByVal baseDir As String,ByVal searchPattern As String)As System.Type()
Dim tmpInstances As New List(Of Type)
尝试
Dim文件As String
对于Directory.GetFiles(baseDir,searchPattern,SearchOption.TopDirectoryOnly)中的每个文件
Dim assemblyType As System.Type
对于Assembly.LoadFrom(file).GetTypes
Dim s As System.Type()= assemblyType.GetInterfaces
返回s.ToArray()
下一个
下一个
Catch exp As TargetInvocationException
如果(不是exp.InnerException Is Nothing)然后
抛出exp.InnerException
结束如果
结束尝试
结束函数
加载DLL的代码: / p>
在此输入代码
Dim basedir As String =M:\MyProgs\WebService\DynamicAssemblyLoading\SQLServer \bin\Debug\
Dim searchPattern As String =* SQL * .dll
Dim plugin As CRDataLayer.ICRDataLayer
尝试
Dim文件作为String
对于每个文件在Directory.GetFiles(baseDir,searchPattern,SearchOption.TopDirectoryOnly)
Dim assemblyType As System.Type
对于每个assemblyType在Assembly.LoadFrom(文件).GetExportedTypes
如果assemblyType.GetInterface(CRDataLayer.ICRDataLayer)IsNot Nothing然后
plugin = DirectCast(Activator.CreateInstance(assemblyType),CRDataLayer.ICRDataLayer)
MessageBox.Show(plugin.ModuleDescription )
结束如果
Nex t
下一个
Catch exp As TargetInvocationException
如果(不是exp.InnerException is Nothing)然后
抛出exp.InnerException
结束如果
捕获ex As Exception
MsgBox(ex.Message)
Clipboard.SetText(ex.Message)
结束尝试
版本2 - 此示例从当前目录加载一个DLL。
有2个项目,1个控制台应用程序项目和一个模块项目(模块'coppies'的DLL到控制台应用程序的工作目录)。
下面的示例简单地演示了动态加载实现接口的DLL。 IModule
界面刚刚报告其名称。 PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory,* .Module.dll)
将创建任何 IModule
在当前目录中以.Module.dll结尾的DLL中找到的实例。这是一个反映的VB.NET版本直接从Mini SQL查询。
考虑到这一点:
Dim modules As IModule()= PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory,* .Server.dll)
pre>
应满足您的要求。那么你只需要选择执行哪一个!
代码:
在VB.LoaderDemo Colsole App中
'IModule。 vb
公共接口IModule
属性ModuleName()As String
结束接口
'PlugInUtility.vb
导入System.IO
导入系统.Reflection
公共类PlugInUtility
公共共享函数GetInstances(Of T)(ByVal baseDir As String,ByVal searchPattern As String)As T()
Dim tmpInstances As New List(Of T)
尝试
Dim file As String
对于Directory.GetFiles(baseDir,searchPattern,SearchOption.TopDirectoryOnly)中的每个文件
Dim assemblyType作为类型
对于每个assemblyType在程序集中。 LoadFrom(file).GetTypes()
If(not assemblyType.GetInterface(GetType(T).FullName)Is Nothing)Then
tmpI nstances.Add(DirectCast(Activator.CreateInstance(assemblyType),T))
End If
Next
下一个
Catch exp As TargetInvocationException
如果(不是exp.InnerException是没有)然后
抛出exp.InnerException
结束如果
结束尝试
返回tmpInstances.ToArray()
结束函数
结束类
'MainModule.vb
模块MainModule
Sub Main()
Dim plugins As IModule()= PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory,* .Module。 dll)
Dim m As IModule
对于每个m在插件
Console.WriteLine(m.ModuleName)
下一个
End Sub
结束模块
在Sample1 DLL(引用IModule的VB.LoaderDemo
导入VB.LoaderDemo
公共类MyModule1
I补充IModule
Dim _name As String
Public Sub New()
_name =Sample 1,Module 1
End Sub
公共属性ModuleName()As String实现IModule.ModuleName
获取
返回_name
结束获取
设置(ByVal值As String)
_name = value
结束集
结束属性
结束类
输出是:
>样本1,模块1
I am trying to write some code that will allow me to dynamically load DLLs into my application, depending on an application setting. The idea is that the database to be accessed is set in the application settings and then this loads the appropriate DLL and assigns it to an instance of an interface for my application to access.
This is my code at the moment:
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)
I have my interface (ICRDataLayer) and the SQLServer.dll contains an implementation of this interface. I just want to load the assembly and assign it to the SQLDataSource object.
The above code just doesn't work. There are no exceptions thrown, even the Msgbox doesn't appear. I would've expected at least the messagebox appearing with nothing in it, but even this doesn't happen!
Is there a way to determine if the loaded assembly implements a specific interface. I tried the below but this also doesn't seem to do anything!
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
EDIT: New code from Vlad's examples:
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
Above is Module in each DLL, converted from Vlad's C# example.
Below is my code to bring in the 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)
EDIT: Implementation based on Paul Kohler's code
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
I get the following error with this code:
Unable to cast object of type 'SQLServer.CRDataSource.SQLServer' to type 'DynamicAssemblyLoading.ICRDataLayer'.
The actual DLL is in a different project called SQLServer in the same solution as my implementation code. CRDataSource is a namespace and SQLServer is the actual class name of the DLL. The SQLServer class implements ICRDataLayer, so I don't understand why it wouldn't be able to cast it. Is the naming significant here, I wouldn't have thought it would be.
Final Working code
Contents of 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
Code to load the 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
解决方案Version 2 - This sample loads up a DLL from it current directory. There are 2 projects, 1 console application project and a "module" project (the module 'coppies' its DLL to the working directory of the console app).
The sample below simply demonstrates dynamically loading a DLL that implements an interface. The
IModule
interface just reports its name.PlugInUtility.GetInstances(Of IModule)(Environment.CurrentDirectory, "*.Module.dll")
will create an instance of anyIModule
instance found in a DLL within the current directory ending with ".Module.dll". It's a reflected VB.NET version straight out of Mini SQL Query.With that in mind something like:
Dim modules As IModule() = PlugInUtility.GetInstances(Of ICRDataLayer)(Environment.CurrentDirectory, "*.Server.dll")
Should satisfy your requirement. Then you just need to chose which one to execute!
The code:
In "VB.LoaderDemo Colsole App"
' 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
In "Sample1 DLL" (references 'VB.LoaderDemo' for 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
The output is:
> Sample 1, Module 1
这篇关于.Net动态加载DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!