Dynamic DLL Loading

When linking your application to a regular DLL, you can do it with either static linking or dynamic linking. Static linking is easier but if a DLL does not exist, your application cannot be started. Dynamic linking is a little bit harder but offers much more flexibility. If a DLL is not found, you can show a dialog box to inform a user and disable some features in your application that use a missing DLL. Missing DLL does not prevent your application to run normally.

This class offers a very easy way to dynamically load a DLL and use it afterwards. The class as it is should not be instantiated. You should derive a class of your own.

Step 1:

Class TBaseModule is a base class for all derived classes. Each derived class corresponds to one DLL.

#define BM_OK                   0
#define BM_DLLNOTFOUND          1
#define BM_INVALIDEXPORT        2
class TBaseModule {
    protected:
        int ErrorCode;        // One of BM_xxx defines
        int FunctionErrorCode;
        CString DLLName;
        HINSTANCE DLLHandle;
    public:
        TBaseModule(CString name);
        virtual ~TBaseModule();
        virtual BOOL Create(void);
        virtual void Destroy(void);
        int GetErrorCode(void)                  { return ErrorCode; }
        int GetFunctionErrorCode(void)          { return FunctionErrorCode; }
        CString& GetDLLName(void)               { return DLLName; }
};
Step 2:

Following is an implementation of this class:

TBaseModule::TBaseModule(CString name)
{
    DLLName = name;
    DLLHandle = NULL;
    ErrorCode = BM_OK;
    FunctionErrorCode = BM_OK;
}
TBaseModule::~TBaseModule()
{
    Destroy();
}
BOOL TBaseModule::Create(void)
{
    DLLHandle = ::LoadLibrary(DLLName);
    if (DLLHandle == NULL) {
        ErrorCode = BM_DLLNOTFOUND;
        return FALSE;
    }
    return TRUE;
}
void TBaseModule::Destroy(void)
{
    if (DLLHandle) {
        ::FreeLibrary(DLLHandle);
        DLLHandle = NULL;
    }
}
Step 3:

Now, let's assume that you want to dynamically load a DLL which exports the following functions and has a name "TEST.DLL" (DLL has a standard calling convention):

void ExportedFunction1(int code, const char *str);
WORD ExportedFunction2(DWORD *data);
char *ExportedFunction3(int code);
Step 4:

You have to define a type for each exported function and then derive a class TTestModule from base class TBaseModule.
 

typedef void (__stdcall *FExportedFunction1)(int code, const char *str);
typedef WORD (__stdcall *FExportedFunction2)(DWORD data);
typedef char * (__stdcall FExportedFunction3)(int code);
class TTestModule : public TBaseModule {
private:
    FExportedFunction1 FunctionExportedFunction1;
    FExportedFunction2 FunctionExportedFunction2;
    FExportedFunction3 FunctionExportedFunction3;
public:
    TTestModule(CString dllname);
    virtual BOOL Create(void);
    virtual void Destroy(void);
    void Function1(int code, const char *str);
    WORD Function2(DWORD data);
    char *Function3(int code);
};
Step 5:

Implementation of this derived class is the following:
 

TTestModule::TTestModule(CString dllname)
            :TBaseModule(dllname)
{
    FunctionExportedFunction1 = NULL;
    FunctionExportedFunction2 = NULL;
    FunctionExportedFunction3 = NULL;
}
BOOL TTestModule::Create(void)
{
    if (TBaseModule::Create()) {
        FunctionExportedFunction1 = (FExportedFunction1)::GetProcAddress(DLLHandle,_T"ExportedFunction1");
        FunctionExportedFunction2 = (FExportedFunction2)::GetProcAddress(DLLHandle,_T"ExportedFunction2");
        FunctionExportedFunction3 = (FExportedFunction3)::GetProcAddress(DLLHandle,_T"ExportedFunction3");
        if (FunctionExportedFunction1 && FunctionExportedFunction2 && FunctionExportedFunction3)
            return TRUE;
        ErrorCode = BM_INVALID_EXPORT;
        return FALSE;
    }
    return FALSE;
}
void TTestModule::Destroy(void)
{
    TBaseModule::Destroy();
    FunctionExportedFunction1 = NULL;
    FunctionExportedFunction2 = NULL;
    FunctionExportedFunction3 = NULL;
}
void TTestModule::Function1(int code, const char *str)
{
    if (DLLHandle == NULL)
        TRACE0(_T("TEST.DLL is not loaded - Use Create()\n"));
    FunctionErrorCode = BM_OK;
    if (FunctionExportedFunction1)
        FunctionExportedFunction1(code,str);
    else
        FunctionErrorCode = BM_INVALIDEXPORT;
}
WORD TTestModule::Function2(DWORD data)
{
    if (DLLHandle == NULL)
        TRACE0(_T("TEST.DLL is not loaded - Use Create()\n"));
    FunctionErrorCode = BM_OK;
    if (FunctionExportedFunction2)
        return FunctionExportedFunction1(data);
    FunctionErrorCode = BM_INVALIDEXPORT;
    return 0;
}
char *TTestModule::Function3(int code)
{
    if (DLLHandle == NULL)
        TRACE0(_T("TEST.DLL is not loaded - Use Create()\n"));
    FunctionErrorCode = BM_OK;
    if (FunctionExportedFunction3)
        return FunctionExportedFunction3(code);
    FunctionErrorCode = BM_INVALIDEXPORT;
    return NULL;
}
Step 6:

All you have to do to use this class is to declare it (yu can do it in several different ways: (1) as a data member of your CWinApp derived class, (2) as a local object within a function, (3) as a global object and (4) as a data member of any class). You have to call Create() and check return code. If it is FALSE, you can check error code and decide to abandon using DLL (since it is not loaded) or use it (since some functions are not loaded because they do not exist within a DLL).
 

TTestModule testDll(_T("TEST.DLL"));
....
BOOL retcode = testDll.Create();
if (retcode == FALSE) {
    if (testDll.GetErrorCode() == BM_INVALIDEXPORT)
        ::AfxMessageBox(_T("TEST.DLL does not contain all functions"));
    else if (testDll.GetErrorCode() == BM_DLLNOTFOUND)
        ::AfxMessageBox(_T("TEST.DLL is not found"));
}
....
testDll.Function1(0,NULL);
if (testDll.GetFunctionErrorCode() == BM_INVALIDEXPORT)
    ::AfxMessageBox(_T("ExportedFunction1 does not exist in a TEST.DLL"));
....
Use this class for your own DLLs or for system DLLs which are not part of the operating system.

That's all there is to it!
 

Last updated: 17 May 1998.



Comments

  • Easier binding with a factory object with virtual member functions

    Posted by Legacy on 03/04/1999 12:00am

    Originally posted by: Ernst Versteeg

    If you have a lot of functions you need for each function a
    GetProcAddress. In this case it is easier to import with GetProcAddress
    only one function which returns an object with virtual member
    functions.

    If the dll is build with the same development system as the application
    you can use the virtual member functions of this returned object
    without prior binding with GetProcAddress.

    The base class of the returned object is abstract and serves as
    protocoll which describes which functions should be implemented
    by the dll.

    So the dll exports only a create function which returns an object
    derived from the abstract base class.

    Reply
  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • This paper introduces IBM Java on the IBM PowerLinux 7R2 server and describes IBM's implementation of the Java platform, which includes IBM's Java Virtual Machine and development toolkit.

  • A modern mobile IT strategy is no longer an option, it is an absolute business necessity. Today's most productive employees are not tied to a desk, an office, or a location. They are mobile. And your company's IT strategy has to be ready to support them with easy, reliable, 24/7 access to the business information they need, from anywhere in the world, across a broad range of communication devices. Here's how some of the nation's most progressive corporations are meeting the many needs of their mobile workers …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds