

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...
#define LATELOAD_BEGIN_CLASS(ClassName,ModuleName,bLoadDllNow,bManaged) \
class ClassName : public CLateLoadBase \
{ \
public:\
ClassName()\
{\
\
\
\
ZeroMemory(static_cast<ClassName*>(this),sizeof(ClassName)); \
m_bManaged = bManaged; \
\
dll_LoadLibrary(#ModuleName,bLoadDllNow); \
}
#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:
- 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
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 ;-)
#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:
#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;
....
}
if( m_msimg32.BadFunctionName() )
{
}
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
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.