2
votes

I'm importing circa 1500 functions from a vendor provided dll (all __stdcall). For tedious reasons the dll exists in a number of versions containing various subsets of the full function list (although all functions that do exist have common interface). All of the functions return a UINT indicating an error code, but take various numbers of arguments of diverse types. Currently if GetProcAddress fails (because the function in question doesn't exist in the particular dll version), the function pointer is left = nullptr, which needs to be checked for each time the client calls a function from the dll. I would like instead to assign a pointer to a function which returns a UINT of an appropriate error code. The proposed solution was along these lines:

UINT missing_func() {
   return ERR_MISSING_FUNC;
}
.....
typedef UINT(*LibFuncTy)(int a, const char *b, double c);
.....
//GetProcAddress returned nullptr... set generic function
LibFuncTy LibFunc = reinterpret_cast<LibFunctTy>(missing_func);
.....
//programme tries to call library function
errorval = LibFunc(arg1, arg2, arg3);

This approach works as written (despite being explicitly undefined by the standard) but fails as soon as the calls are __stdcall (presumably because we've messed up the stack). I had a look at using (/abusing) bind, but doesn't appear to help with what I'm trying to do (unless I'm missing something). Many of the library function calls end up in a deeply nested loop, so I don't want to incur any (non trivial) overhead in cases where GetProcAddress was able to find the function.

Is there a "correct", standard compliant, way of achieving this behaviour which will work with the __stdcall calling convention, or am I going to have to generate 1500 different versions of missing_func() with varying argument lists, all returning the same value?

2
Is it failing during compilation or execution?Trenin
Right, you messed up the stack. Throw an exception instead.Hans Passant
It fails because stdcall relies on the callee to pop arguments off the stack, and missing_func() will pop nothing. Hopefully, the number of functions you're importing that have distinct signatures is much smaller than 1500. The easiest, if tedious, solution would be to create missing_func() overloads for each of these, and assign them appropriately.Praetorian
There are indeed less than 1500 signatures, but more than 100 which would still be tedious to generate and assign manually. Have currently just processed the function list automatically to create all the "missing_func" clones, at expense of some redundancy...Xphraz

2 Answers

2
votes

You need one dummy function for each stack frame size, since the ret instruction for this convention adjusts the stack pointer correspondingly.

Note that there is a standard error code for "not implemented", I think symbolic name like E_NOTIMPL or such. Check it out.

1
votes

Creating a wrapper that checks for nullptr before dispatching the call to the imported function seems to work.

template<typename... Args>
struct get_proc_address_wrapper;

template<typename... Args>
struct get_proc_address_wrapper<unsigned(Args...)>
{
    typedef unsigned(__stdcall *func_type)(Args...);
    get_proc_address_wrapper(func_type pf)
        : pf(pf)
    {}

    unsigned operator()(Args... args)
    {
        if (not pf) return static_cast<unsigned>(-1);
        else return (*pf)(args...);
    }

    func_type pf;
};

Here's a demo of how you'd use it. As you can see, it gracefully returns an error code if the wrapper was initialized with nullptr. I tested the same code on VS2013 and it returned the same result.