t e m p o r a l 
 d o o r w a y 

Calling A DLL Function Dynamically

 

Introduction

There are many reasons to load a DLL and call its functions at run time, rather than at compile time. As the described in my article on A Basic DLL and its Test Harness, it is simple to set up the call to a DLL at compile time, but when you do that, the DLL is loaded when your program is, and is not unloaded until your program is. If the DLL is optional (may not exist on the target system), or is specified by entries in a text file or a database, or is so large or resource intensive that you want to only have it loaded when you are using it, then you need another way to work with it.

How To Do It

To work with a DLL dynamically, you need to do a number of things that are normally handled by the code the compiler produces...

  • Load the DLL
  • Find the address of the function entry point, and store it in a typedefed function pointer variable.
  • Call the function
  • Unload the DLL

The Code

Based on the test harness from A Basic DLL and its Test Harness, this test harness loads, calls, and unloads...

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "MainFormUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;

extern "C"
{
   int __declspec(dllimport) TestDLLInterfaceFunction(int theParameter);
}

//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------
int __fastcall CallTestDLLInterfaceFunction(int theNumber)
{
   int Result = 0;

   HANDLE DLLHandle = LoadLibrary("SimpleDLL"); // Define the path string

   if (DLLHandle != NULL)
   {
      typedef (*aTestDLLInterfaceFunction)(int); // This is a function pointer

      aTestDLLInterfaceFunction TestDLLInterfaceFunction =
         (aTestDLLInterfaceFunction)
            GetProcAddress(DLLHandle,"_TestDLLInterfaceFunction");
            
// Leading underscore required by DLL and caller compiler settings

      if (TestDLLInterfaceFunction != NULL)
         Result = TestDLLInterfaceFunction(theNumber);

      FreeLibrary(DLLHandle);
   };

   return Result;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::InputChange(TObject *Sender)
{
   try
   {
      int InputNumber = Input->Text.ToInt();

      if (CallDynamicCheckBox->Checked)
      {
         Result->Caption = String(CallTestDLLInterfaceFunction(InputNumber));
      }
      else
      {
         Result->Caption = String(TestDLLInterfaceFunction(InputNumber));
      };
   }
   catch (...) // Fails if the input is not a number
   {
   };
}
//---------------------------------------------------------------------------

The form allows you to toggle between calling the DLL function through the .lib and dynamically:

Form for testing the DLL

object ResultLabel: TLabel
    Left = 39
    Top = 52
    Width = 30
    Height = 13
    Caption = 'Result'
end
object InputLabel: TLabel
    Left = 13
    Top = 26
    Width = 63
    Height = 13
    Caption = 'Input To DLL'
end
object Result: TLabel
    Left = 85
    Top = 52
    Width = 3
    Height = 13
end
object Input: TEdit
    Left = 85
    Top = 20
    Width = 98
    Height = 21
    TabOrder = 0
    OnChange = InputChange
end
object CallDynamicCheckBox: TCheckBox
    Left = 200
    Top = 24
    Width = 97
    Height = 17
    Caption = 'Call Dynamic'
    TabOrder = 1
end


Thanks to Oskar Norin for pointing out a flaw in the earlier version of this example.
Copyright © 2004 by Mark Cashman (unless otherwise indicated), All Rights Reserved