template <
typename t_func_type>
t_func_type GetWindowsImport( t_func_type * pFunc , const char * funcName, const char * libName )
{
if ( *pFunc == 0 )
{
HMODULE m = GetModuleHandle(libName);
if ( m == 0 ) m = LoadLibrary(libName); // adds extension for you
ASSERT_ALWAYS( m != 0 );
t_func_type f = (t_func_type) GetProcAddress( m, funcName );
// not optional : (* should really be a throw)
ASSERT_ALWAYS( f != 0 );
*pFunc = f;
}
return (*pFunc);
}
#define CALL_IMPORT(lib,name) (*GetWindowsImport(&RR_STRING_JOIN(fp_,name),RR_STRINGIZE(name),lib))
#define CALL_KERNEL32(name) CALL_IMPORT("kernel32",name)
so then instead of doing
InitializeConditionVariable(&cv);you do
// put this somewhere : VOID (WINAPI *fp_InitializeConditionVariable) (PCONDITION_VARIABLE ) = NULL; // call like this : CALL_KERNEL32(InitializeConditionVariable)(&cv);which is not too bad. (of course you can hide the difference completely by doing
#define InitializeConditionVariable CALL_KERNEL32(InitializeConditionVariable)so that the client code looks identical to if it was a real lib call, that way you can just have like an #if PLATFORMSDK < 7.1 somewhere that makes the imports for you, and the client code doesn't have to change at all when it goes from being a lib import to a GetProcAddress manual import.
Of course if you are using real C++ then when GetProcAddress fails to find the function it should throw.
Also : warning : if you use this on non-built-in-libs (eg. if you used it on something like "psapi" as opposed to "kernel32" or "user32" or whatever) then there is actually a race that could cause a crash. The problem is that GetModuleHandle doesn't inc the ref on the lib, so it could get unloaded while you are calling it. A more fully correct implementation would return a proxy object that holds a ref on the lib on the stack, that way the lib is kept ref'ed for the duration of the function call.
For a header-file library I'm wrting I need to do a bunch of windows functions, and I don't want to include windows.h and I don't want to rely on the functions being available, so my code looks like this:
ReplyDeleteextern __declspec(dllimport) int (__stdcall * __stdcall GetProcAddress (struct HINSTANCE__ *module, const char *procname))();
...
struct HINSTANCE__ *net = LoadLibraryA("NETAPI32.DLL");
#define STB__GETPROCA(dll, name, var, rtype, args) \
rtype (__stdcall *var) args = (rtype (__stdcall *) args) (dll ? GetProcAddress(dll, name) : 0)
STB__GETPROCA(net, "NetStatisticsGet" , netstatget , unsigned long, (stbc_u16 *, stbc_u16 *, stbc_u32, stbc_u32, stbc_u8 **));
STB__GETPROCA(net, "NetApiBufferFree" , netfree , unsigned long, (stbc_u8 *));
if (netstage && netfree) {
...
}
if (adv) FreeLibrary(adv);
which isn't too bad since I have like 4 DLLs and 10 functions -- in the end the boilerplate doesn't drown things out too much.