1

I have this object what is just a wrapper around an external dll, in Windows. On creation of the object, the constructor queries the registry for the key, then queries this key's value for the folder with the dll, then loads the dll, gets the ProcAddress of this dll's initializator, and initialises it.

Then, I want to offer to other clients all the functionality of this dll, so my class-wrapper offers member functions what will check if we have GetProcAddress'it, if not then we do it, get the pointer to this function, and call it.

I don't want to do all the GetProcAddress'es at constructor time because there are hundreds of functions and is very likely that a given client will need only a small bunch of them, so GetProcAddress'ing everything is a waste.

But now, I need to define these functions, and it feels like a gigantic copy&paste thing. All the member functions are doing exactly the same, something like

ResultType Wrapper::pertainingMemberFunction(...)
{
    if (this->pointerToPertainingDLLFunction == nullptr) {
        this->pointerToPertainingDLLFunction =
            reinterpret_cast<pertainingType>(GetProcAddress(HMODULEDLL, "pertainingName"));
    }
    return this->pointerToPertainingDLLFunction (...);
}

And this really feels like a very sub-optimal solution. I'm feeling there must be some way to templatize all this code to be generated at compile time correctly. Or perhaps the initial approach was not the best to begin with, I'm not sure.

camino
  • 10,085
  • 20
  • 64
  • 115
Nelson Vides
  • 443
  • 4
  • 11
  • It's worth noting that the "waste" would only be one of time (the pointers' storage is there no matter what). On the other hand, you wouldn't need to check everytime you call a function. Are you sure loading them all at construction is problematic? – Quentin Mar 07 '18 at 09:50
  • You're right on the second question. I'm just a bit sceptic because for the most part this class will be created and used only two or three of the more than a hundred functions this dll imports, and then destructed. And in a slightly-time critical fashion. I'll have to do some profiling to see how much slower _ProcAddress_ing everything would make it. – Nelson Vides Mar 07 '18 at 22:41
  • If this is time-critical, why not go the other way and cache a single instance of the wrapper? I don't know what the cost of a handful of `GetProcAddress` is, but it might be something to be wary of. – Quentin Mar 08 '18 at 09:03

3 Answers3

2

It's simple enough to factor out the logic into a function template:

template <class Function, class... Args>
decltype(auto) getProcAddressAndCall(
    Function *&function,
    char const *name,
    Args &&... args
) {

    if(!function)
        function = reinterpret_cast<Function *>(GetProcAddress(HMODULEDLL, name));

    return function(std::forward<Args>(args)...);
}

... which you can then call with:

ResultType Wrapper::pertainingMemberFunction(...)
{
    return getProcAddressAndCall(pointerToPertainingDLLFunction, "pertainingName", ...);
}
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • This is exactly the C++ beauty I was imagining it existed somewhere and I couldn't do it myself. Indeed there are some stuff here I don't know, like decltype(auto) or std::forward, but that'll be very straightforward to check documentation. Even though the Visual Studio sugar is indeed elegant, I take this answer for the C++ gotcha. Thanks! – Nelson Vides Mar 07 '18 at 22:43
1

The other solutions all are reasonable approached, but pretty complex.

It turns out you can get a lot more help from Visual Studio. The reason is that your behavior closely aligns with a Visual Studio feature, delay-loaded DLL's. Visual Studio's implementation of delay-loaded DLL's works by generating GetProcAddress calls for all the functions of a DLL, which is exactly what you need.

Now it appears that your specific functionality is to read a registry key to find the specific DLL. As it happens, the delay-loading mechanism has a hook mechanism. If you define __pfnDliNotifyHook2, it gets called with notification dliNotePreLoadLibrary just before LoadLibrary would be called.

Your hook function can substitute its own LoadLibrary function instead, and load the DLL from the desired location. Just return the HMODULE and Visual Studio will use that for its generated GetProcAddress calls.

So, your resulting solution looks like this:

ResultType Wrapper::DllFunction(...)
{
    return DLLFunction (...);
}

Simple, isn't it? Visual Studio will notice that DllFunction comes from a delay-loaded DLL and insert the GetProcAddress call, using the HMODULE from your __pfnDliNotifyHook2.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

Assuming that function declarations are available, it is possible to get away with exposing class fields containing method pointers as a callable objects:

extern "C"
{

void pertainingName(void);

}

class Wrapper
{
    ::HMODULE const m_h_library{};

public:
    #define WRAPPED_METHOD(method_name)                     \
        decltype(&::method_name) const method_name          \
        {                                                   \
            reinterpret_cast<decltype(&::method_name)>      \
            (                                               \
                ::GetProcAddress(m_h_library, #method_name) \
            )                                               \
        }                                                   \

    WRAPPED_METHOD(pertainingName);

    explicit Wrapper(::LPCWSTR const psz_library_path):
        m_h_library{::LoadLibraryW(psz_library_path)}
    {}
};

int main()
{
    Wrapper wrap{L"foo.dll"};
    wrap.pertainingName(); // invoking imported function just as it was a member function
    return 0;
}

The more sophisticated approach may include use of dedicated callable template class instead of raw pointers.

user7860670
  • 35,849
  • 4
  • 58
  • 84