LateLoad is a series of macros for managing
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 ;-)
I was getting sick & tired of constantly rewriting simple
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
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
The following sample will create the class
CMsImg32Wrapper from which we can access the
GradientFill() exported function from MSIMG32.DLL.
The generated class will be functionally equivalent to:
class CMsImg32Wrapper : public CLateLoadBase
BOOL STDAPICALLTYPE GradientFill(HDC a,PTRIVERTEX b,ULONG c,
PVOID d, ULONG e,ULONG f);
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.
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 \
/*Automagicaly blank out all the function pointers and */ \
/*ImportedProcState member vars that will be declared */ \
/*in following LATELOAD_FUNC_* declarations, very handy. */ \
m_bManaged = bManaged; \
/*and load the DLL*/ \
// 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.
LATELOAD_FUNC_n statements do the work of:
- specifying the return value if the function is not imported
typedef'ing the function prototype
- declaring the function pointer member var
- declaring an
ImportedProcState to keep track of the function pointer
- declaring the member function
BOOL Is_<FuncName>() to determine if the function pointer is loaded & valid
- declaring a member function with the same signature as the function loaded from the DLL
- offloading the important
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)();
// BOOL CALLBACK Something();
// Would be changed to...
// If "Something" could not be loaded, and it was called - it would return 0
#define LATELOAD_FUNC_0(ErrorResult,ReturnType,CallingConv,FuncName) \
typedef ReturnType(CallingConv * TYPE_##FuncName)(); \
TYPE_##FuncName m_pf##FuncName; \
BOOL Is_##FuncName() \
if(ipsUnknown == m_ips##FuncName) \
m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#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
From the demo:
// In LateLoadDemo.h
class CLateLoadDemoDlg : public CDialog
// 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
BOOL bPaintedGradient = FALSE;
vert.x = m_rcGradient.left;
vert.y = m_rcGradient.top;
vert.Red = MAKEWORD(0,GetRValue(m_clrStart));
vert.Green = MAKEWORD(0,GetGValue(m_clrStart));
vert.Blue = MAKEWORD(0,GetBValue(m_clrStart));
vert.Alpha = 0x0000;
vert.x = m_rcGradient.right;
vert.y = m_rcGradient.bottom;
vert.Red = MAKEWORD(0,GetRValue(m_clrEnd));
vert.Green = MAKEWORD(0,GetGValue(m_clrEnd));
vert.Blue = MAKEWORD(0,GetBValue(m_clrEnd));
vert.Alpha = 0x0000;
gRect.UpperLeft = 0;
gRect.LowerRight = 1;
//Using this will require explicit linking to msimg32.lib
bPaintedGradient = ::GradientFill(dc.GetSafeHdc(),vert,2,&gRect,1,
bPaintedGradient = m_msimg32.GradientFill(dc.GetSafeHdc(),vert,2,
Points of Interest
Did you know that this is my first CodeProject code submission? Be kind.
2004.Mar.01 - Initial release.