Option Strict Off in compiler generated Reference.vb file - vb.net

What causes the vb.net compiler to add Option Strict Off to the Reference.vb file created for asmx web references? Sometimes it's there and sometimes it's not, and it never seems to be necessary.

As the file is has a header similar (your version number may differ) to this:
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
and the first to statements are:
Option Strict Off
Option Explicit On
I would say that the tool that generates the file is using the VBCodeProvider Class to produce the code using the CodeCompileUnit Class and that author of the tool did not override the defaults for the UserData items AllowLateBound and RequireVariableDeclaration.
The code for the Microsoft.VisualBasic.VBCodeGenerator.GenerateCompileUnitStart method obtained using Reflector is as follows.
Protected Overrides Sub GenerateCompileUnitStart(ByVal e As CodeCompileUnit)
MyBase.GenerateCompileUnitStart(e)
MyBase.Output.WriteLine("'------------------------------------------------------------------------------")
MyBase.Output.Write("' <")
MyBase.Output.WriteLine(SR.GetString("AutoGen_Comment_Line1"))
MyBase.Output.Write("' ")
MyBase.Output.WriteLine(SR.GetString("AutoGen_Comment_Line2"))
MyBase.Output.Write("' ")
MyBase.Output.Write(SR.GetString("AutoGen_Comment_Line3"))
MyBase.Output.WriteLine(Environment.Version.ToString)
MyBase.Output.WriteLine("'")
MyBase.Output.Write("' ")
MyBase.Output.WriteLine(SR.GetString("AutoGen_Comment_Line4"))
MyBase.Output.Write("' ")
MyBase.Output.WriteLine(SR.GetString("AutoGen_Comment_Line5"))
MyBase.Output.Write("' </")
MyBase.Output.WriteLine(SR.GetString("AutoGen_Comment_Line1"))
MyBase.Output.WriteLine("'------------------------------------------------------------------------------")
MyBase.Output.WriteLine("")
If Me.AllowLateBound(e) Then
MyBase.Output.WriteLine("Option Strict Off")
Else
MyBase.Output.WriteLine("Option Strict On")
End If
If Not Me.RequireVariableDeclaration(e) Then
MyBase.Output.WriteLine("Option Explicit Off")
Else
MyBase.Output.WriteLine("Option Explicit On")
End If
MyBase.Output.WriteLine()
End Sub
...
Protected Function AllowLateBound(ByVal e As CodeCompileUnit) As Boolean
Dim obj2 As Object = e.UserData.Item("AllowLateBound")
If ((Not obj2 Is Nothing) AndAlso TypeOf obj2 Is Boolean) Then
Return CBool(obj2)
End If
Return True
End Function
Protected Function RequireVariableDeclaration(ByVal e As CodeCompileUnit) As Boolean
Dim obj2 As Object = e.UserData.Item("RequireVariableDeclaration")
If ((Not obj2 Is Nothing) AndAlso TypeOf obj2 Is Boolean) Then
Return CBool(obj2)
End If
Return True
End Function

Related

Type mismatch trying to set data in an object in a collection

I am getting Runtime Error 13 when trying to update an object stored in a collection. Here is a minimal example.
The class (Class2) of the objects to be stored in the collection.
Option Explicit
Private pHasA As Boolean
Private pHasB As Boolean
Private pSomeRandomID As String
Property Get HasA() As Boolean
HasA = pHasA
End Property
Property Get HasB() As Boolean
HasB = pHasB
End Property
Property Let HasA(propValue As Boolean)
pHasA = propValue
End Property
Property Let HasB(propValue As Boolean)
pHasB = propValue
End Property
Property Let RandomID(propValue As String)
pSomeRandomID = propValue
End Property
Sub SetHasValues(key As String)
Select Case key
Case "A"
pHasA = True
Case "B"
pHasB = True
End Select
End Sub
Minimal code that reproduces the error:
Option Explicit
Private Sub TestCollectionError()
Dim classArray As Variant
Dim classCollection As Collection
Dim singleClass2Item As Class2
Dim iterator As Long
classArray = Array("A", "B", "C")
Set classCollection = New Collection
For iterator = LBound(classArray) To UBound(classArray)
Set singleClass2Item = New Class2
singleClass2Item.RandomID = classArray(iterator)
classCollection.Add singleClass2Item, classArray(iterator)
Next iterator
Debug.Print "Count: " & classCollection.Count
singleClass2Item.SetHasValues "A" ' <-- This code works fine.
Debug.Print "New Truth values: " & singleClass2Item.HasA, singleClass2Item.HasB
For iterator = LBound(classArray) To UBound(classArray)
classCollection(classArray(iterator)).RandomID = classArray(iterator)
classCollection(classArray(iterator)).SetHasValues classArray(iterator) '<-- Type mismatch on this line.
Next iterator
'***** outputs
'''Count: 3
'''New Truth values: True False
' Error dialog as noted in the comment above
End Sub
While the code above appears a little contrived, it is based on some real code that I am using to automate Excel.
I have searched for answers here (including the following posts), but they do not address the simple and non-ambiguous example that I have here. The answers that I have found have addressed true type mismatches, wrong use of indexing or similar clear answers.
Retrieve items in collection (Excel, VBA)
Can't access object from collection
Nested collections, access elements type mismatch
This is caused by the fact, that the parameter of your procedure SetHasValues is implicitely defined ByRef.
Defining it ByVal will fix your problem.
#ADJ That's annoying, but perhaps the example below will allow you to start making a case for allowing RubberDuck.
I've upgraded your code using ideas and concepts I've gained from the rubberduck blogs. The code now compiles cleanly and is (imho) is less cluttered due to fewer lookups.
Key points to note are
Not relying on implicit type conversions
Assigning objects retrieved from collections to a variable of the type you are retrieving to get access to intellisense for the object
VBA objects with true constructors (the Create and Self functions in class2)
Encapsulation of the backing variables for class properties to give consistent (and simple) naming coupled with intellisense.
The code below does contain Rubberduck Annotations (comments starting '#)
Updated Class 2
Option Explicit
'#Folder("StackOverflowExamples")
'#PredeclaredId
Private Type Properties
HasA As Boolean
HasB As Boolean
SomeRandomID As String
End Type
Private p As Properties
Property Get HasA() As Boolean
HasA = p.HasA
End Property
Property Get HasB() As Boolean
HasB = p.HasB
End Property
Property Let HasA(propValue As Boolean)
p.HasA = propValue
End Property
Property Let HasB(propValue As Boolean)
p.HasB = propValue
End Property
Property Let RandomID(propValue As String)
p.SomeRandomID = propValue
End Property
Sub SetHasValues(key As String)
Select Case key
Case "A"
p.HasA = True
Case "B"
p.HasB = True
End Select
End Sub
Public Function Create(ByVal arg As String) As Class2
With New Class2
Set Create = .Self(arg)
End With
End Function
Public Function Self(ByVal arg As String) As Class2
p.SomeRandomID = arg
Set Self = Me
End Function
Updated test code
Private Sub TestCollectionError()
Dim classArray As Variant
Dim classCollection As Collection
Dim singleClass2Item As Class2
Dim my_item As Variant
Dim my_retrieved_item As Class2
classArray = Array("A", "B", "C")
Set classCollection = New Collection
For Each my_item In classArray
classCollection.Add Item:=Class2.Create(my_item), key:=my_item
Next
Debug.Print "Count: " & classCollection.Count
Set singleClass2Item = classCollection.Item(classCollection.Count)
Debug.Print "Initial Truth values: " & singleClass2Item.HasA, singleClass2Item.HasB
singleClass2Item.SetHasValues "A" ' <-- This code works fine.
Debug.Print "New Truth values: " & singleClass2Item.HasA, singleClass2Item.HasB
For Each my_item In classArray
Set my_retrieved_item = classCollection.Item(my_item)
my_retrieved_item.RandomID = CStr(my_item)
my_retrieved_item.SetHasValues CStr(my_item)
Next
End Sub
The 'Private Type Properties' idea comes from a Rubberduck article encapsulating class variable in a 'This' type. My take on this idea is to use two type variable p and s (Properties and State) where p holds the backing variables to properties and s hold variables which represent the internal state of the class. Its not been necessary to use the 'Private Type State' definition in the code above.
VBA classes with constructors relies on the PredeclaredID attribute being set to True. You can do this manually by removing and saving the code, using a text editor to set the attributer to 'True' and then reimporting. The RUbberDuck attribute '#PredeclaredId' allows this to be done automatically by the RubberDuck addin. IN my own code the initialiser for class2 would detect report an error as New should not be used when Classes are their own factories.
BY assigning and intermediate variable when retrieving an object from a class (or even a variant) you give Option Explicit the best change for letting you n=know of any errors.
An finally the Rubberduck Code Inspection shows there are still some issues which need attention

No default member found for type '' when using Activator.Instance

I'm writing a project where this will be extended with further modules later on, and as such i want as much of the code as possible to be independent, so I'm using various methods to keep everything as generic as possible, and not require hard-coding lists of types, classes etc.
As a Proof of concept, I've come up with the following code in a class;
Public Class clsAddition
Public Function DoAction(Value1 As String, Value2 As String) As String
Dim dblTmpRet As Double
Dim strTmpRet As String = ""
If IsNumeric(Value1) = False Then strTmpRet += "Argument 'Value1' is not numeric. "
If IsNumeric(Value2) = False Then strTmpRet += "Argument 'Value2' is not numeric. "
If strTmpRet = "" Then
dblTmpRet = CDbl(Value1) + CDbl(Value2)
strTmpRet = CStr(dblTmpRet)
End If
Return strTmpRet
End Function
Private Function IsNumeric(Input As String) As Boolean
Dim blnTmpRet As Boolean
Try
Dim dblTest As Double = CDbl(Input)
blnTmpRet = True
Catch ex As Exception
blnTmpRet = False
End Try
Return blnTmpRet
End Function
Public Sub New()
MyBase.New()
End Sub
End Class
And the following code in a windows form;
Private objObjectClass As Object
Private Sub cmdCreateObject_Click(sender As Object, e As EventArgs) Handles cmdCreateObject.Click
Dim assem As Assembly = GetType(Form1).Assembly
Dim typChosen As Type
Try
typChosen = assem.GetType(Me.comObjectType.SelectedItem.ToString, True)
Catch ex As Exception
typChosen = Nothing
End Try
If Not IsNothing(typChosen) Then
'found it
objObjectClass = Activator.CreateInstance(typChosen, True)()
Else
'didn't find it
Throw New Exception("Unable to locate type in assembly")
End If
End Sub
The issue is, that on the "Activator.CreateInstance(typChosen, True)()" line, the following error is thrown
An unhandled exception of type 'System.MissingMemberException'
occurred in Microsoft.VisualBasic.dll Additional information: No
default member found for type 'clsAddition'
The aim is for me to be able to create instances of classes, then call a known name function on them. In this example, I'd create another class called "Divide", or another one that was called "Concatinate" etc, however in the real program these classes will all perform very different actions, have different properties, methods etc, with this one function ("DoAction" in the above example) sharing a syntax and return type.
Please help! been banging my head on this for a while now!
TIA!
As Per Hans Passant comment, this is due to the extra parenthesis on the following line;
Activator.CreateInstance(typChosen, True)()
Changing this to ;
Activator.CreateInstance(typChosen, True)
resolves the issue.

How can I allow all subroutines within my program to access a certain variable?

In the code below, I want to be able to access the enteredusername and enteredpassword variables from any sub routine. How would I accomplish this?
Using rdr As New FileIO.TextFieldParser("f:\Computing\Spelling Bee\stdnt&staffdtls.csv")
rdr.TextFieldType = FieldType.Delimited
rdr.Delimiters = New String() {","c}
item = rdr.ReadFields()
End Using
Console.Write("Username: ")
enteredusername = Console.ReadLine
Console.Write("Password: ")
Dim info As ConsoleKeyInfo
Do
info = Console.ReadKey(True)
If info.Key = ConsoleKey.Enter Then
Exit Do
End If
If info.Key = ConsoleKey.Backspace AndAlso enteredpassword.Length > 0 Then
enteredpassword = enteredpassword.Substring(0, enteredpassword.Length - 1)
Console.Write(vbBack & " ")
Console.CursorLeft -= 1
Else
enteredpassword &= info.KeyChar
Console.Write("*"c)
End If
Loop
Dim foundItem() As String = Nothing
For Each line As String In File.ReadAllLines("f:\Computing\Spelling Bee\stdnt&staffdtls.csv")
Dim item() As String = line.Split(","c)
If (enteredusername = item(0)) And (enteredpassword = item(1)) Then
foundItem = item
Exit For
End If
Next
To allow ALL classes within your program access the variable, you need to make it class-level and define it with Public and Shared.
Demonstration:
Public Class MainClass
Public Shared enteredusername As String
Public Shared enteredpassword As String
Private Sub SomeSub()
' Some Code ...
' You can access it here:
enteredusername = "something"
enteredpassword = "something else"
' ... More Code ...
End Sub
End Class
Public Class AnotherClass
'Also, please note, that this class can also be in another file.
Private Sub AnotherSub()
' Some Code ...
' You can also access the variable here, but you need to specify what class it is from, like so:
Console.WriteLine(MainClass.enteredusername)
Console.WriteLine(MainClass.enteredpassword)
' ... More Code ...
End Sub
End Class
Also, on a separate note, the Public and Shared modifiers can also be used on methods. If you make a method Private or don't specify anything, the method will only be accessible from methods in the same class. If you use only Public, other classes can access the method, but they will need to create a instance of the class, like so:
Dim AC As New AnotherClass
AC.AnotherSub()
If you use both the Public and the Shared modifiers, other classes will be able to access the method directly, without creating a new instance. But, you must note, that Shared methods cannot access non-Shared methods or variables. Other classes can access Public Shared methods like so:
AnotherClass.AnotherSub()
It depends on the scope. If you want all of the subroutines in the current class to be able to access them then make them a field of the class
Class TheClassName
Dim enteredusername As String
Dim enteredpassword As String
...
End Class
If you want all subroutines in all classes and modules to be able to access them then make them a module level field
Module TheModuleName
Dim enteredusername As String
Dim enteredpassword As String
...
End Module
I recommend against this approach though. Sure it's easier in the short term because it requires less ceremony and thought on the uses of the values. But long term it serves to reduce the maintainability of your code base

Verify that an object has a certain property

I found C# code for it here
So I tried
Public Function checkProperty(ByVal objectt As Object, ByVal propertyy As String) As Boolean
Dim type As Type = objectt.GetType
Return type.GetMethod(propertyy)
End Function
But it throws an error at type.GetMethod(propertyy) saying "Value of type 'System.Reflection.MethodInfo' cannot be converted to 'Boolean'."
What to do?
First, C# code checks for presence of a method, not a property. Second, C# code compares return to null:
Public Function checkProperty(ByVal objectt As Object, ByVal propertyy As String) As Boolean
Dim type As Type = objectt.GetType
Return type.GetProperty(propertyy) IsNot Nothing
End Function
EDIT To check for fields, change the method as follows:
Public Function checkField(ByVal objectt As Object, ByVal fieldName As String) As Boolean
Dim type As Type = objectt.GetType
Return type.GetField(fieldName) IsNot Nothing
End Function
it is returning the MethodInfo instead and you can just change it as follow:
Public Function checkProperty(ByVal objectt As Object, ByVal propertyy As String) As Boolean
Dim type As Type = objectt.GetType
Return type.GetMethod(propertyy) IsNot Nothing
End Function
You're trying to return type.GetMethod(propertyy), where your example code is returning the result of evaluating if that method is null or not.
try Return type.GetMethod(propertyy) isnot nothing
By splitting the line:
Return type.GetMethod(propertyy) IsNot Nothing
of dasblinkenlight into the three lines;
Dim info As System.Reflection.PropertyInfo = type.GetProperty(propertyy)
Dim reallyExists As Boolean = info IsNot Nothing
Return reallyExists
the function checkProperty will return true on existing properties.
I cannot comment to the answer of dasblinkenlight,
since my reputation is still less than 50,
so send my above improvement on his answer as a new answer.
I am not happy with this, since it makes the pages on stackoverflow less readable.
In order to make this a true answer I include a module I created,
in which the lines
Public Function propertyExists( _
through
End Function
are improvements to the code of function checkProperty of natli and dasblinkenlight.
See Verify that an object has a certain property
for the question of natli, that I am answering to.
See https://stackoverflow.com/posts/9399928/revisions and the question of natli
for the answer of dasblinkenlight, that I am commenting to.
By the way: "As you will see below I prefer not to use system namespaces,
so that I can directly see what system functions are referenced."
The module with the name net2Module I created to use this follows;
''' <summary>
''' This module with the name net2Module contains
''' tools that need at least .NET Framework 2.0.
''' This module needs System.Reflection.
''' </summary>
Public Module net2Module
''' <summary>
''' If one of the tools fails, the property exceptionMessage will
''' not be nothing, but contain an exception.
''' Each tool will set exceptionMessage to nothing or an exception.
''' </summary>
Public exceptionMessage As System.Exception = Nothing
''' <summary>
''' Checks if a property with some name exist in an object.
''' This function needs System.Reflection.
''' </summary>
''' <param name="objectt">The object.</param>
''' <param name="propertyy">The name of the property.</param>
''' <returns>True if the property exists.</returns>
Public Function propertyExists( _
objectt As Object, _
ByVal propertyy As String _
) As Boolean
Try
exceptionMessage = Nothing
Dim type As System.Type = objectt.GetType
Dim info As System.Reflection.PropertyInfo = _
type.GetProperty(propertyy)
Dim reallyExists As Boolean = info IsNot Nothing
Return reallyExists
Catch ex As System.Exception
exceptionMessage = ex
Return False
End Try
End Function ' propertyExists
End Module ' net2Module
In the following code I succesfully use my function propertyExists in order
to close or hide child forms recursively;
Friend Module sharedEnums
Friend Enum objectNamesEnum
formHandlingClass
calledForms
ownedForms
End Enum ' objectNamesEnum
Friend Enum recursiveFormTypesEnum
calledForms
ownedForms
End Enum ' recursiveFormTypesEnum
Friend Enum recursiveActionsEnum
hideForms
closeForms
End Enum ' recursiveActionsEnum
End Module ' sharedEnums
Friend Class recursiveClass
Friend recursiveFormTypes As New recursiveFormTypesEnum
Friend recursiveActions As New recursiveActionsEnum
Friend Sub hideOrCloseFormsRecursively( _
formsToHandle As System.Windows.Forms.Form())
If Not formsToHandle Is Nothing Then
Dim formToHandle As System.Windows.Forms.Form = Nothing
Dim propertyToExist As String = String.Empty
If Me.recursiveFormTypes = recursiveFormTypesEnum.calledForms Then
propertyToExist = objectNamesEnum.calledForms.ToString
Else ' Me.recursiveFormTypes = recursiveFormTypesEnum.ownedForms
propertyToExist = objectNamesEnum.ownedForms.ToString
End If
For Each formToHandle In formsToHandle
Try ' Recurse through the forms to handle
Dim formObject As Object = formToHandle
If net2Module.propertyExists(formObject, _
objectNamesEnum.formHandlingClass.ToString) Then
If net2Module.propertyExists( _
formObject.formHandlingClass, propertyToExist) Then
If Me.recursiveFormTypes = _
recursiveFormTypesEnum.calledForms Then
Call Me.hideOrCloseFormsRecursively( _
formObject.formHandlingClass.calledForms.ToArray)
Else ' Me.recursiveFormTypes = recursiveFormTypesEnum.ownedForms
Call Me.hideOrCloseFormsRecursively( _
formObject.formHandlingClass.ownedForms)
End If
End If
End If
If net2Module.propertyExists(formObject, propertyToExist) Then
If Me.recursiveFormTypes = _
recursiveFormTypesEnum.calledForms Then
Call Me.hideOrCloseFormsRecursively( _
formObject.calledForms.ToArray)
Else ' Me.recursiveFormTypes = recursiveFormTypesEnum.ownedForms
Call Me.hideOrCloseFormsRecursively( _
formObject.ownedForms)
End If
End If
Catch
End Try
Try ' Take the action to take on each found form
If Me.recursiveActions = _
recursiveActionsEnum.hideForms Then
Call formToHandle.Hide()
Else ' Me.recursiveActions = recursiveActionsEnum.closeForms
Call formToHandle.Close()
End If
Catch
End Try
Next
End If
End Sub ' hideOrCloseFormsRecursively
End Class ' recursiveClass
I would be delighted to read from you if you have been helped by me or not.
I am Dutch, so would also like comments on my Engish, so that I can improve it.

.Net Dynamically Load DLL

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 any IModule 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
Is the type ICRDataLayer defined in the DLL you are going to load? If so, you seem to already reference the DLL in your project settings.
You need to work with just reflection:
Dim obj As Object = ass.CreateInstance("ICRDataLayer", True)
Dim t as Type = obj.GetType()
Dim method as MethodInfo = t.GetMethod("DoSomething")
method.Invoke(obj, ...)
Edit: If ICRDataLayer is implemented in the application, and the plugin just implements the interface, you need the plugin to provide a factory for you: (sorry for C# code, I am not familiar with VB.NET's syntax)
// 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();
}
}
The application's code should look like this:
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)
A few things to look for in your code
Debug through and check that the
assembly is loaded correctly, in case
it fails due to dependency checking
Instead of using GetType, use GetExportedType so you have smaller subset to iterate through
The CreateInstance should use your loadedType rather than the interface (you cant create object from an interface)
Personally, I dont like naming my variable ass, I would shorten it to assem instead :)