Introduction
I'm really bored of this sort of code:
typedef int (__stdcall *MSGBOXWAPI)(IN HWND hWnd, IN LPCWSTR lpText, IN LPCWSTR lpCaption,
IN UINT uType, IN WORD wLanguageId, IN DWORD dwMilliseconds);
HMODULE hM = LoadLibrary(L"USER32.DLL");
MSGBOXWAPI a = (MSGBOXWAPI)GetProcAddress(hM,"MessageBoxTimeoutW");
if (a)
a(hParent,L"Hello",L"there",MB_OK,0,1000);
Too many lines of code over and over again. Here is a simple trick, based on C++ templates, template metaprogramming and std::tuple
to have the call in one line:
int j = DLLCall<int>(L"USER32.DLL","MessageBoxTimeoutW",
std::make_tuple<>(nullptr,L"Do you like me?",L"Message",MB_YESNO | MB_ICONINFORMATION,0,3000));
Woah. You liked it, yes? Here's its implementation.
Code
#include <windows.h>
#include <tuple>
#include <functional>
namespace DLLCALL
{
using namespace std;
template<int ...> struct seq {};
template<int N,int ...S> struct gens : gens < N - 1,N - 1,S... > {};
template<int ...S> struct gens <0,S... >
{
typedef seq<S...> type;
};
template<typename R,int ...S,typename...Args>
R ActualCall(seq<S...>,std::tuple<Args...> tpl,std::function<R(Args...)> func)
{
return func(std::get<S>(tpl) ...);
}
#pragma warning(disable:4290)
template <typename R,typename...Args> R DLLCall(const wchar_t* module,
const char* procname,std::tuple<Args...> tpl) throw(HRESULT)
{
HMODULE hModule = LoadLibrary(module);
FARPROC fp = GetProcAddress(hModule,procname);
if (!fp)
throw E_POINTER;
typedef R(__stdcall *function_pointer)(Args...);
function_pointer P = (function_pointer)fp;
std::function<R(Args...)> f = P;
R return_value = ActualCall(typename gens<sizeof...(Args)>::type(),tpl,f);
FreeLibrary(hModule);
return return_value;
}
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
int j = DLLCALL::DLLCall<int>(L"USER32.DLL","MessageBoxTimeoutW",
std::make_tuple<>(nullptr,L"Do you like me?",L"Message",MB_YESNO | MB_ICONINFORMATION,0,3000));
if (j == 32000)
MessageBox(nullptr,L"Was timeout",L"Message",MB_OK);
return 0;
}
What is happening here? First, we use an integer pack so, later, the ActualCall
function can expand all the parameters we have passed to the std::function
we use to store the pointer. The actual arguments we want are passed insode a std::tuple
. I use std::make_tuple<>
to auto-deduct the types in this trivial example - you could specify the parameters if there is any ambiguity, for example std::tuple<HWND,LPCTSTR,LPCTSTR,UINT,WORD,UINT>
for MessageBoxTimeoutW
.
The function throws E_POINTER
in case that the function is not found in the module.
It only works with __stdcall
, but you may change this line to work for other calling conventions:
typedef R(__stdcall *function_pointer)(Args...);
Parameterized GetProcAddress()
template <typename R,typename ...Args> std::function<R(Args...)>
GetProcAddress(HMODULE hModule,const char* procname)
{
FARPROC fp = GetProcAddress(hModule,procname);
if (!fp)
return nullptr;
typedef R(__stdcall *function_pointer)(Args...);
function_pointer P = (function_pointer)fp;
std::function<R(Args...)> f = P;
return f;
}
This is simpler. You do not need a tuple, but a simple Args
... expansion here. Call it as follows:
auto j2 = DLLCALL::GetProcAddress<HWND,const char*,const char*,UINT>
(LoadLibrary("USER32.DLL","MessageBoxA");
if (j2)
j2(0,"hello","there",MB_OK);
auto xx = DLLCALL::GetProcAddress<UINT,HWND,const char*,const char*,UINT>
((HMODULE)LoadLibrary(L"USER32.DLL"),(const char*)"MessageBoxA")(0,"hello","there",MB_OK);
IDispatch::Invoke()
In the CComPtr
class, they use the C old-style ellipsis in CComPtr::InvokeN()
to pass the arguments to the call. Here is a C++ 11 implementation. Note that, since all the parameter types are VARIANT (CComVariant
), we do not need a std::tuple
, but a simpler std::initializer_list
:
HRESULT InvokeX(CComPtr<IDispatch>& di,LPCOLESTR lpszName,
std::initializer_list<CComVariant> li,VARIANT* pvarRet)
{
HRESULT hr;
DISPID dispid;
hr = di.GetIDOfName(lpszName,&dispid);
if (SUCCEEDED(hr))
{
DISPPARAMS dispparams = {0};
std::vector<CComVariant> vs;
for (auto a : li)
vs.insert(vs.begin(),a); dispparams.cArgs = li.size();
if (vs.size())
dispparams.rgvarg = &vs[0];
hr = di->Invoke(dispid,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,
&dispparams,pvarRet,NULL,NULL);
}
return hr;
}
Without the Tuple
As Stuard Dootson said, you don't even need std::tuple
. Hey, it gets simpler.
template <typename R, typename... Args>
R DLLCall(const wchar_t* module, const char* procname, Args... args) throw(HRESULT)
{
HMODULE hModule = LoadLibrary(module);
FARPROC fp = GetProcAddress(hModule, procname);
if (!fp)
throw E_POINTER;
typedef R(__stdcall * function_pointer)(Args...);
function_pointer P = (function_pointer)fp;
const auto return_value = (*P)(std::forward<Args>(args)...);
FreeLibrary(hModule);
return return_value;
}
As with my GetProcAddress()
implementation, this version does not require the template expansion that would occur when using std::tuple
.
Points of Interest
The base idea for this was found here.
History
- 20/02/2015 - Simplification, thanks to Stuard Dootson
- 07/02/2015 - Added the
IDispatch::InvokeX
implementation - 23/11/2014 - First release