Using Reflection To Dynamically Load DLLs

Have you ever wanted to add a plugin system to your application, perhaps a web browser or a photo editor, to allow your users to extend and improve on its functionality? Extensibility is very important, and allowing plugins to be made for your program can have many advantages in this respect. Why allow plugins?

  • Plugins are a convenient way to upgrade your application with features without doing a complete upgrade. You can package new functionality (though not always bug fixes) in a small DLL and distribute it as a plugin, quickly and easily.
  • Plugins are the easiest way to add user extensibility to your program. Programmers can build on your program, and user satisfaction is increased if waiting for the next update is made unnecessary by the acceptance of plugins in your application.
  • They're fun and quick to add.
To load DLLs dynamically, we use reflection. Most of the methods used in reflection come from the System.Reflection namespace, so all of the code examples below assume that you've added it as a project-wide import or added Imports System.Reflection at the top of your code file.

Okay. To load a DLL, first of all (obviously) you will need to know its file path. For some reason, file paths are restricted to absolute paths so if you only have a relative path for some reason, a quick call to IO.Path.GetFullPath will fix that for you.

Next, we need to load an Assembly object from the DLL. To do that, all you need to do is call Assembly.LoadFile with the path of your DLL.

Dim dllPath As String = "C:\example.dll"
Dim a As Assembly = Assembly.LoadFile(dllPath)

Now that that's done, we can load any class or module we like from the assembly. Remember to use the fully qualified name of the class or module that you would like to load (including the root namespace of the DLL). Usually, projects' namespaces are created based on the name of the project, and there are ways to just load the root project namespace from the DLL, but for simplicity, we will just assume that the root namespace for our example.dll file is "Test". We will now load a module called "Example" from the Test namespace:

Dim t As Type = a.GetType("Test.Example")

Okay, so now we've got a Type object. We will now attempt to call a method named "GetMessage" in the Example module. All we have to do is get a MethodInfo object and call the object's Invoke() method. Here, all we do is call t.GetMethod() with the name of the method we want (GetMessage) and that's all. However, if there are overloaded variants of the same method, you will need to pass the types of the arguments you intend to pass later in an array as the second argument of GetMethod(). Just keep this in mind.

Dim m As MethodInfo = t.GetMethod("GetMessage")

Now we have the MethodInfo object. All that's left is to call Invoke() with the proper parameters and cast the result to the type it actually returns:

MessageBox.Show(DirectCast(m.Invoke(Nothing, New Object() {}), String)

A bit of explanation now. m.Invoke takes two arguments (in this case, at least); the first is the "Me" object that the method will be invoked on, which must be of the correct type. In this case, we are calling a static (Shared) method so there is no "Me" object and so we leave this parameter as Nothing. The second argument is an array of the arguments to pass to the method. Our GetMessage method takes no arguments and returns a String, so we pass an empty array. Finally, we cast the return value to a String and display it in a message box. And that's all there is to it! Now you (and your users) can enjoy plugins.

Code tip of the day:
Use MessageBox.Show instead of MsgBox!

No comments:

Post a Comment