7/27/2011

07-27-11 - CALL_IMPORT

Syntactically friendly way to call manual Windows imports :

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.

1 comment:

Anonymous said...

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:

extern __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.

old rants