Click here to Skip to main content
Click here to Skip to main content

LateLoad DLL Wrapper

, 29 Feb 2004
Rate this:
Please Sign up or sign in to vote.
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 Wink | ;-)

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. Smile | :)

Unfortunately, it didn't come with full source Frown | :( 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 Wink | ;-)
//
// 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)

About the Author

Jason De Arte
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

 
GeneralMy vote of 5 Pinmembermagicpapacy30-Mar-12 22:46 
GeneralMacro for nine parameters PinmemberChristophG1-Feb-07 6:17 
GeneralRe: Macro for nine parameters Pinmemberbarton_c6-Dec-08 12:59 
GeneralUse Unicode Character Set Bug PinmemberVincentKao17-Jan-07 23:30 
GeneralRe: Use Unicode Character Set Bug PinmemberJason De Arte21-Jan-07 10:25 
In the immortal words of Homer J. Simpson...
 
D'oH!
 
Yup, _T() is your friend in unicode environments. Nice find.
 
[ Jason De Arte | Toy Maker | 1001010.com ]

QuestionWhat about static member functions? PinmemberMartin Herbort7-Jan-07 22:53 
QuestionContructor initialization with ZeroMemory()? PinsussChristian Kaiser24-Aug-05 23:36 
AnswerRe: Constructor initialization with ZeroMemory()? PinmemberJason De Arte25-Aug-05 19:38 
AnswerRe: Contructor initialization with ZeroMemory()? PinmemberDaniel B.21-Feb-12 5:50 
Generaldecorated names Pinmemberhsd999-Mar-04 2:56 
GeneralThanks for this class... PinmemberHans Dietrich3-Mar-04 7:02 
GeneralCongratulations! PinmemberHans Dietrich2-Mar-04 3:01 
GeneralPlease fix scrolling PinmemberSam Levy2-Mar-04 2:28 
GeneralRe: Please fix scrolling PinmemberJason De Arte2-Mar-04 12:25 
QuestionWhy not use delay loading Pragma? PinmemberETA1-Mar-04 20:59 
AnswerRe: Why not use delay loading Pragma? PinmemberJason De Arte2-Mar-04 12:16 
GeneralExcellent Article Pinmemberajh1-Mar-04 17:55 
GeneralMacros not very powerful PinmemberRetarT1-Mar-04 14:38 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 1 Mar 2004
Article Copyright 2004 by Jason De Arte
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid