Taking what I said previously into account, a much safer variant could look like this:
Code:
template < typename RetType, typename... ArgTypes >
RetType DynCall(const char* dll_name, const char* api_name, ArgTypes...args) {
typedef RetType(__stdcall* F)(ArgTypes...);
HMODULE module = LoadLibrary(dll_name);
assert(module != INVALID_MODULE_HANDLE && "Failed to load library");
F fptr = (F)GetProcAddress(module, api_name);
assert(fptr && "Unable to resolve API");
return fptr(args...);
}
Usage would look like this:
Code:
int main() {
std::cout << "GetTickCount1: "
<< DynCal< DWORD >("kernel32.dll", "GetTickCount") << "\n";
DynCall< VOID, DWORD >("kernel32.dll", "Sleep", 1000);
std::cout << "GetTickCount2: "
<< DynCall< DWORD >("kernel32.dll", "GetTickCount") << "\n"
}
produces this output:
Code:
GetTickCount1: 16462234
GetTickCount2: 16463250
Press any key to continue . . .
The first template argument specifies the return type of the API, after that you pass the argument types. Note that the function assumes stdcall!
The advantage here is that the compiler does all the type checking for you, so this cannot fail unless you mess up the function prototype itself - but that problem cannot be fixed anyhow unless we're using a correct import library which would defeat the whole purpose of this exercise.
Things to improve:
- Throw an exception if loading library or resolving the function fails. Alternatively, wrap the result into std:ptional so callers are aware they need to check for error.
- Add string type as template param so code is agnostic of whether compiled with unicode or MBCS.
- Invoke FreeLibrary after API was invoked; possibly add an overload that takes an already loaded library handle without freeing it (i.e. do not transfer ownership).
Edit: the template argument do not display correctly due to forum system :/ I fixed it by adding a blank to the template arguments, now it displays correctly.