LateLoad DLL Wrapper






4.83/5 (22 votes)
Automate and manage your GetProcAddress code with these handy wrapper macros.
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...
// // 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:
- 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 ;-)
// // 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.