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...
#define LATELOAD_BEGIN_CLASS(ClassName,ModuleName,bLoadDllNow,bManaged) \
class ClassName : public CLateLoadBase \
m_bManaged = bManaged; \
#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
#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:
class CLateLoadDemoDlg : public CDialog
if( m_msimg32.BadFunctionName() )
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;
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.