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

  • Lenovo recommends Windows 8 Pro. "I dropped my laptop getting out of the taxi." This probably sounds familiar to most IT professionals. If your employees are traveling, you know their devices are in for a rough go. Whether it's a trip to the conference room or a convention out of town, any time equipment leaves a user's desk it is at risk of being put into harm's way. Stay connected at all times, whether at the office or on the go, with agile, durable, and flexible devices like the Lenovo® …

  • U.S. companies are desperately trying to recruit and hire skilled software engineers and developers, but there's simply not enough quality talent to go around. In response, companies often resort to inferior solutions -- hiring substandard developers and engineers, recruiting talent on a part-time or temporary basis, poaching people from competitors, or burdening an already stressed IT staff for more of their labor. Fortunately, there's a better solution. Read this white paper to learn the business value of …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date