Dynamically load a DLL from a runtime specified path


When running VB.Net or C# code, often it’s useful to call native code. One way is using PInvoke. (for other ways, see How fast is interop code?)


 


For example, you can call GetWindowText to get the title of a window. To get it’s managed signature, you can use this code from http://www.pinvoke.net/default.aspx/user32/GetWindowText.html


 


    <DllImport(“User32.dll”, SetLastError:=True, CharSet:=CharSet.Auto)> _


    Shared Function GetWindowText(ByVal hwnd As IntPtr, _


                       ByVal lpString As StringBuilder, _


                       ByVal cch As Integer) As Integer


    End Function


 


 


This code works because “User32.dll” is a well known system Dll and can be found along the PATH. However, many times the Dll you want may not be on the path.  Sometimes it’s in a known location relative to where your code is executing (like a subdirectory).


 


The string name of the Dll specified in the DllImport can include a path name, like “c:\windows\system32\user32.dll”, but it must be constant at compile time.  This limitation contradicts the name for Dll: “Dynamic Link Library”


 


However, sometimes a DLL may not live in a known location on a disk, but you still want to call it. Perhaps the location can be specified at runtime.


 


A simple way to work around this problem is to explicitly call LoadLibrary (which accepts a full path) first. Then the call will work.


 


Run the sample code below.


 


Start Visual Studio. File->New->Project->VB ->Windows Application.


Double click the form and replace the code with the sample below.


 


The sample creates two buttons on a form and tries to invoke GetWindowText from a specified DLL.


 


The 1st button invokes the one in the Windows System32 directory.


 


The 2nd button copies the system one to a different folder and a different name (“User32Copy.dll”), then calls it.


 


 


 


 


See also:


How fast is interop code?


Create a .Net UserControl that calls a web service that acts as an ActiveX control to use in Excel, VB6, Foxpro


 


 


 


<Sample Code>


 


 


Imports System.Runtime.InteropServices


Imports System.IO


Imports System.Text


 


Public Class Form1


 


 


    Dim WithEvents b32 As New Button


    Dim WithEvents b32Copy As New Button


 


    Dim TargDir = IO.Path.GetTempPath


    Dim SysDir = Path.Combine(My.Application.GetEnvironmentVariable(“windir”), “System32”) ‘ C:\Windows\System32


 


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load


 


        Me.Text = “Form1 “ + DateTime.Now.ToLongTimeString


        b32.Text = “User32”


        Me.Controls.Add(b32)


 


        b32Copy.Text = “User32Copy”


        b32Copy.Top = 30


        Me.Controls.Add(b32Copy)


    End Sub


    Sub btn32_click() Handles b32.Click


        CallGetWindowText(New Wrap32, Path.Combine(SysDir, Wrap32.cDllName))


    End Sub


    Sub btn32Copy_click() Handles b32Copy.Click


        Dim cFullName = Path.Combine(TargDir, Wrap32Copy.cDllName) ‘ D:\user32Copy.dll


        If Not File.Exists(cFullName) Then


            File.Copy(Path.Combine(SysDir, Wrap32.cDllName), cFullName)


        End If


        Using ldr = New DynamicDllLoader(cFullName)


            CallGetWindowText(New Wrap32Copy, cFullName)


        End Using


 


    End Sub


    Sub CallGetWindowText(ByVal wrap As Object, ByVal cFullName As String)


        Dim sbStr As New StringBuilder(255)


        wrap.GetWindowText(Me.Handle.ToInt32, sbStr, sbStr.Capacity)


        Dim ParentWind = Me.Handle.ToInt32 ‘sets the owner of the Msgbox. also try Me.Handle.ToInt32 and 0 to observe Alt-Tab behavior


        MessageBox.Show(“From “ + cFullName + vbCrLf + “Form caption = “ + sbStr.ToString, “From “ + cFullName, 0)


 


    End Sub


End Class


 


Friend Class Wrap32


    Friend Const cDllName = “user32.dll”


    <DllImport(cDllName, SetLastError:=True, CharSet:=CharSet.Auto)> _


    Shared Function GetWindowText(ByVal hwnd As IntPtr, _


                       ByVal lpString As StringBuilder, _


                       ByVal cch As Integer) As Integer


    End Function


End Class


 


 


Friend Class Wrap32Copy


    Friend Const cDllName = “user32Copy.dll”


    <DllImport(cDllName, SetLastError:=True, CharSet:=CharSet.Auto)> _


    Shared Function GetWindowText(ByVal hwnd As IntPtr, _


                       ByVal lpString As StringBuilder, _


                       ByVal cch As Integer) As Integer


    End Function


End Class


 


 


Friend Class DynamicDllLoader


    Implements IDisposable


    <DllImport(“kernel32.dll”, _


        CallingConvention:=CallingConvention.Winapi, _


        CharSet:=CharSet.Auto, _


        EntryPoint:=“LoadLibrary”, _


        PreserveSig:=True)> _


    Friend Shared Function LoadLibrary(<[In]()> ByVal DllPath As String) As Integer


 


    End Function


    <DllImport(“kernel32.dll”, _


        CallingConvention:=CallingConvention.Winapi, _


        EntryPoint:=“FreeLibrary”, _


        PreserveSig:=True)> _


    Friend Shared Function FreeLibrary(<[In]()> ByVal Handle As Integer) As Integer


 


    End Function


    Private _HandleDll As Integer


    Sub New(ByVal FullPath As String, Optional ByVal fChangeDir As Boolean = False)


        ‘must change folder to loc of MSVBIDE so VSAssert and MSVBIDEui can be found


        Dim OrigCurdir = System.IO.Directory.GetCurrentDirectory


        If fChangeDir Then


            System.IO.Directory.SetCurrentDirectory(Path.GetDirectoryName(FullPath))


        End If


        Try


            If IO.File.Exists(FullPath) Then


                _HandleDll = LoadLibrary(FullPath)


                If _HandleDll = 0 Then


                    Throw New FileNotFoundException(“couldn’t load “ + FullPath)


                End If


            Else


                Throw New FileNotFoundException(FullPath)


            End If


        Catch ex As Exception


        Finally


            If fChangeDir Then


                Directory.SetCurrentDirectory(OrigCurdir)


            End If


        End Try


    End Sub


    Sub UnLoadDll()


        If _HandleDll Then


            FreeLibrary(_HandleDll)


            _HandleDll = 0


        End If


    End Sub


 


    Private disposedValue As Boolean = False    ‘ To detect redundant calls


 


    ‘ IDisposable


    Protected Overridable Sub Dispose(ByVal disposing As Boolean)


        If Not Me.disposedValue Then


            If disposing Then


                ‘ TODO: free other state (managed objects).


            End If


            UnLoadDll()


 


            ‘ TODO: free your own state (unmanaged objects).


            ‘ TODO: set large fields to null.


        End If


        Me.disposedValue = True


    End Sub


 


#Region ” IDisposable Support “


    ‘ This code added by Visual Basic to correctly implement the disposable pattern.


    Public Sub Dispose() Implements IDisposable.Dispose


        ‘ Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.


        Dispose(True)


        GC.SuppressFinalize(Me)


    End Sub


#End Region


 


End Class


 


 


 


 


</Sample Code>


 

Comments (0)