Click here to Skip to main content
15,881,027 members
Articles / Programming Languages / C++
Article

LateLoad DLL Wrapper

Rate me:
Please Sign up or sign in to vote.
4.83/5 (23 votes)
29 Feb 2004CPOL3 min read 111.8K   2.3K   49   19
Automate and manage your GetProcAddress code with these handy wrapper macros.

Sample image

Sample image

Introduction

LateLoad is a series of macros for managing LoadLibrary / GetProcAddress calls by creating a class that contains DLL exported functions as member functions.

The attached sample uses LoadLoad to access the GradientFill() function exported from MSIMG32.DLL. This is not a sample on how to use GradientFill(), but I've found that people respond better to arbitrary demos if they either have blinking lights, pretty colors or are interactive in some way. Since this has 2 out of 3, maybe it will hold everyone's attention though it's my first CodeProject submission ;-)

Background

I was getting sick & tired of constantly rewriting simple LoadLibrary / GetProcAddress calls for dynamically loading various DLLs that may (or may not) be on my users' systems. I looked at using /DELAYLOAD, but that still needed me to have a .LIB file for the DLL. Unfortunately, I didn't always have the lib - so that was out of the question. Then I thought - "I'll use TEMPLATES!", but that didn't work. After a little web searching, I came across a MSDN article by MicHael Galkovsky called DLLs the Dynamic Way [^]. It was great, it covered everything that I wanted to do. :)

Unfortunately, it didn't come with full source :( So LateLoad was created!

Using the code

Couldn't be easier. You may notice more than a passing resemblance to various BEGIN_<Something>_MAP and END_<something>_MAP statements in the MFC and ATL libraries, that's perfectly normal. I like the style.

The LateLoad macros create a class derived from CLateLoadBase, whose sole responsibility is to manage LoadLibrary, FreeLibrary & GetProcAddress functions.

The following sample will create the class CMsImg32Wrapper from which we can access the GradientFill() exported function from MSIMG32.DLL.

#include "LateLoad.h"

LATELOAD_BEGIN_CLASS(CMsImg32Wrapper,msimg32,FALSE,FALSE)
    LATELOAD_FUNC_6(FALSE,BOOL,STDAPICALLTYPE,GradientFill,HDC,PTRIVERTEX,
        ULONG,PVOID,ULONG,ULONG)
    LATELOAD_FUNC_0(FALSE,BOOL,STDAPICALLTYPE,BadFunctionName)
LATELOAD_END_CLASS()

The generated class will be functionally equivalent to:

class CMsImg32Wrapper : public CLateLoadBase
{
public:
    CMsImg32Wrapper();
    BOOL Is_GradientFill();
    BOOL STDAPICALLTYPE GradientFill(HDC a,PTRIVERTEX b,ULONG c,
                                     PVOID d, ULONG e,ULONG f);
    BOOL Is_BadFunctionName();
    BOOL STDAPICALLTYPE BadFunctionName();
};

With the above code, you can instantiate CMsImg32Wrapper & use Is_GradientFill to determine if GradientFill was loaded from the DLL, &/or call GradientFill() directly without fear of a null pointer.

Since BadFunctionName() does not exist in MSIMG32.DLL, calling it will always return FALSE (the first param in LATELOAD_FUNC_n is the return value to use if the function pointer could not be loaded).

So - what's going on here?

Macros are the key.

LATELOAD_BEGIN_CLASS declares the class name to create, as well as the DLL to use. LATELOAD_END_CLASS, finishes it up. Let's look at their definitions...

//
// Start, Declares the name of the class
//
// ClassName   = the name of the class
// ModuleName  = the DLL name
// bLoadDllNow = if true, the DLL will be loaded in the constructor.  
//               if false, it will ONLY be loaded when any bound function 
//               is first used
// bManaged    = if true, FreeLibrary will be called in the destructor
//
#define LATELOAD_BEGIN_CLASS(ClassName,ModuleName,bLoadDllNow,bManaged) \
class ClassName : public CLateLoadBase \
{ \
public:\
    ClassName()\
    {\
        /*Automagicaly blank out all the function pointers and */ \
        /*ImportedProcState member vars that will be declared */ \
        /*in following LATELOAD_FUNC_* declarations, very handy. */ \
        ZeroMemory(static_cast<ClassName*>(this),sizeof(ClassName)); \
        m_bManaged = bManaged; \
        /*and load the DLL*/ \
        dll_LoadLibrary(#ModuleName,bLoadDllNow); \
    }

//
// End of the class
//
#define LATELOAD_END_CLASS()  };

Not much to it, blanks out ALL the member variables that we don't know about yet (function pointers & state variables) but will be declared in the LATELOAD_FUNC_n statements (n is the number of parameters from 0 to 9 the function takes). And finally, attempts to load the DLL.

The LATELOAD_FUNC_n statements do the work of:

  1. specifying the return value if the function is not imported
  2. typedef'ing the function prototype
  3. declaring the function pointer member var
  4. declaring an ImportedProcState to keep track of the function pointer
  5. declaring the member function BOOL Is_<FuncName>() to determine if the function pointer is loaded & valid
  6. declaring a member function with the same signature as the function loaded from the DLL
  7. offloading the important LoadLibrary/FreeLibrary/GetProcAddress code to the base class. Because, let's be honest - who wants to debug/step into macro code (which is invisible in DevStudio6)? I know I don't ;-)
//
// Function Declaration, Zero Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & 
//              it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
//
// A function prototype that looked like...
//   typedef BOOL (CALLBACK* SOMETHING)();
//  or
//   BOOL CALLBACK Something();
//
// Would be changed to...
//   LATELOAD_FUNC_0(0,BOOL,CALLBACK,Something)
//
// If "Something" could not be loaded, and it was called - it would return 0
//
#define LATELOAD_FUNC_0(ErrorResult,ReturnType,CallingConv,FuncName) \
protected: \
    typedef ReturnType(CallingConv * TYPE_##FuncName)(); \
    TYPE_##FuncName m_pf##FuncName; \
    ImportedProcState m_ips##FuncName;\
public: \
    BOOL Is_##FuncName() \
    { \
        if(ipsUnknown == m_ips##FuncName) \
            m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, \
                                                          m_ips##FuncName); \
        return(ipsAvailable == m_ips##FuncName); \
    } \
    ReturnType FuncName() \
    {\
        if( !Is_##FuncName() ) \
            return ErrorResult; \
        return m_pf##FuncName(); \
    }

Optionally, there are also LATELOAD_FUNC_n_VOID macros for void functions.

In Use

From the demo:

//---------------------
// In LateLoadDemo.h
//---------------------
#include "LateLoad.h"

LATELOAD_BEGIN_CLASS(CMsImg32Wrapper,msimg32,FALSE,FALSE)
    LATELOAD_FUNC_6(FALSE,BOOL,STDAPICALLTYPE,GradientFill,HDC,
                    PTRIVERTEX,ULONG,PVOID,ULONG,ULONG)
    LATELOAD_FUNC_0(FALSE,BOOL,STDAPICALLTYPE,BadFunctionName)
LATELOAD_END_CLASS()

class CLateLoadDemoDlg : public CDialog
{
    ....
    CMsImg32Wrapper m_msimg32;
    ....
}

//---------------------
// In LateLoadDemo.cpp
//---------------------
// Attempt to call a non-existant function in MSIMG32.DLL
if( m_msimg32.BadFunctionName() )
{
    // This will never return true because the default return value in the 
    // LATELOAD_FUNC_ for this non-existant funct if FALSE
    // m_msimg32.Is_BadFunctionName() would have returned false
}

void CLateLoadDemoDlg::OnPaint() 
{
    ...
    CPaintDC dc(this);

    BOOL bPaintedGradient = FALSE;

    TRIVERTEX      vert[2];
    GRADIENT_RECT  gRect;
    vert[0].x      = m_rcGradient.left;
    vert[0].y      = m_rcGradient.top;
    vert[0].Red    = MAKEWORD(0,GetRValue(m_clrStart));
    vert[0].Green  = MAKEWORD(0,GetGValue(m_clrStart));
    vert[0].Blue   = MAKEWORD(0,GetBValue(m_clrStart));
    vert[0].Alpha  = 0x0000;

    vert[1].x      = m_rcGradient.right;
    vert[1].y      = m_rcGradient.bottom; 
    vert[1].Red    = MAKEWORD(0,GetRValue(m_clrEnd));
    vert[1].Green  = MAKEWORD(0,GetGValue(m_clrEnd));
    vert[1].Blue   = MAKEWORD(0,GetBValue(m_clrEnd));
    vert[1].Alpha  = 0x0000;

    gRect.UpperLeft  = 0;
    gRect.LowerRight = 1;
#if 0
    //Using this will require explicit linking to msimg32.lib
    bPaintedGradient = ::GradientFill(dc.GetSafeHdc(),vert,2,&gRect,1,
        m_bHorizontal?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
#else
    bPaintedGradient = m_msimg32.GradientFill(dc.GetSafeHdc(),vert,2,
        &gRect,1,m_bHorizontal?GRADIENT_FILL_RECT_H:GRADIENT_FILL_RECT_V);
#endif
    ...
}

Points of Interest

Did you know that this is my first CodeProject code submission? Be kind.

History

2004.Mar.01 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Location: Orange County, California, USA
Latitude: 33.672166°
Longitude: -117.865662°
Occupation: Programmer/Toy Maker
Hobby: Evil Lawn Dart Master
Nickname: Lumberjack
Disclamer: my views are my own, and not that of my employeer
http://1001010.com

Comments and Discussions

 
Praisevery handy Pin
Member 831887726-Jul-16 21:07
Member 831887726-Jul-16 21:07 
GeneralMy vote of 5 Pin
magicpapacy30-Mar-12 22:46
magicpapacy30-Mar-12 22:46 
GeneralMacro for nine parameters Pin
ChristophG1-Feb-07 6:17
ChristophG1-Feb-07 6:17 
GeneralRe: Macro for nine parameters Pin
barton_c6-Dec-08 12:59
barton_c6-Dec-08 12:59 
GeneralUse Unicode Character Set Bug Pin
VincentKao17-Jan-07 23:30
VincentKao17-Jan-07 23:30 
GeneralRe: Use Unicode Character Set Bug Pin
Jason De Arte21-Jan-07 10:25
Jason De Arte21-Jan-07 10:25 
QuestionWhat about static member functions? Pin
Martin Herbort7-Jan-07 22:53
Martin Herbort7-Jan-07 22:53 
QuestionContructor initialization with ZeroMemory()? Pin
c2j224-Aug-05 23:36
c2j224-Aug-05 23:36 
AnswerRe: Constructor initialization with ZeroMemory()? Pin
Jason De Arte25-Aug-05 19:38
Jason De Arte25-Aug-05 19:38 
AnswerRe: Contructor initialization with ZeroMemory()? Pin
Daniel B.21-Feb-12 5:50
Daniel B.21-Feb-12 5:50 
Generaldecorated names Pin
hsd999-Mar-04 2:56
hsd999-Mar-04 2:56 
GeneralThanks for this class... Pin
Hans Dietrich3-Mar-04 7:02
mentorHans Dietrich3-Mar-04 7:02 
GeneralCongratulations! Pin
Hans Dietrich2-Mar-04 3:01
mentorHans Dietrich2-Mar-04 3:01 
GeneralPlease fix scrolling Pin
Sam Levy2-Mar-04 2:28
Sam Levy2-Mar-04 2:28 
GeneralRe: Please fix scrolling Pin
Jason De Arte2-Mar-04 12:25
Jason De Arte2-Mar-04 12:25 
QuestionWhy not use delay loading Pragma? Pin
ETA1-Mar-04 20:59
ETA1-Mar-04 20:59 
AnswerRe: Why not use delay loading Pragma? Pin
Jason De Arte2-Mar-04 12:16
Jason De Arte2-Mar-04 12:16 
Simple, I don't like Delayload with it's notification hooks, failure hooks & exception throwing. Wink | ;)
I prefer a simpler model that can (hopefully) be ported to other compilers with great ease.
Don't get be wrong, Delay load is great - but it isn't always appropriate for my needs.
GeneralExcellent Article Pin
ajhuddy1-Mar-04 17:55
ajhuddy1-Mar-04 17:55 
GeneralMacros not very powerful Pin
RetarT1-Mar-04 14:38
RetarT1-Mar-04 14:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.