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.