1
votes

I'm trying to give my function a return type of its template parameter(to improve type checking), which is a function reference. Here is what I have attempted so far:

#include <Windows.h>

template <typename func>
decltype(auto) proxyFunction(LPCSTR dllPath, LPCSTR functionName){
    auto funcType = decltype(func);

    funcType funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    if(funcPtr)
        std::cout << "Proxy success" << std::endl;
    else
        std::cout << "Proxy fail" << std::endl;
    
    return funcPtr;
}

BOOL GetFileVersionInfoProxy(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData){
    auto getFileVersion = proxyFunction<GetFileVersionInfoProxy>("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA");
    getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);
}

However, I am getting the following compile-time error with regards to proxyFunction:

no instance of function template matches the argument list argument types are: (const char [32], const char [20])

I am not quite sure what to make of this error since it is rather vague. So I was wondering if anyone can explain what's the issue in my snippet?

P.S. I am using MS VS 2019 with C++ 17 standard, in case it is helpful.

2
typename func and decltype(func) don't work well togheter. What should be func? A type or a value?max66
@max66 func is expected to be a type of a function, as shown in the GetFileVersionInfoProxy.arslancharyev31
But GetFileVersionInfoProxy itself is not a type, but a function. The template parameter should probably be auto func instead.HolyBlackCat
So, instead of funcType, you should use directly func: func funcPtr = (func) GetProcAddress(LoadLibraryA(dllPath), functionName);max66
@arslancharyev31: Unrelated to everything, consider making auto getFileVersion a static local variable, so that GetProcAddress is only called once, rather than every timeMooing Duck

2 Answers

7
votes

Your func template parameter is already the type you want to return, so just use it as-is, there is no need to use decltype(auto). And your use of auto funcType = decltype(func); is just plain wrong.

Try this instead:

template <typename funcType>
funcType proxyFunction(LPCSTR dllPath, LPCSTR functionName)
{
    funcType funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    if (funcPtr)
        std::cout << "Proxy success" << std::endl;
    else
        std::cout << "Proxy fail" << std::endl;
    
    return funcPtr;
}

BOOL GetFileVersionInfoProxy(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    using GetFileVersionInfoA_FuncType = BOOL (WINAPI *)(LPCSTR, DWORD, DWORD, LPVOID);

    auto getFileVersion = proxyFunction<GetFileVersionInfoA_FuncType>("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA");
    if (getFileVersion)
        return getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);

    return FALSE;
}

Alternatively, you can omit passing in the template parameter if you let the compiler deduce it for you:

template <typename funcType>
bool proxyFunction(LPCSTR dllPath, LPCSTR functionName, funcType &funcPtr)
{
    funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    if (funcPtr)
        std::cout << "Proxy success" << std::endl;
    else
        std::cout << "Proxy fail" << std::endl;
    
    return (funcPtr != nullptr);
}

BOOL GetFileVersionInfoProxy(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    using GetFileVersionInfoA_FuncType = BOOL (WINAPI *)(LPCSTR, DWORD, DWORD, LPVOID);

    GetFileVersionInfoA_FuncType getFileVersion;
    if (proxyFunction("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA", getFileVersion))
        return getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);

    return FALSE;
}

UPDATE: based on @MooingDuck's comment, it looks like you are actually trying to pass your proxy function to the template and have it deduce the necessary parameters and return type for use with the DLL function. If so, then try something more like this instead:

template <typename RetType, typename... ArgTypes>
struct proxyTraits
{
    using funcType = RetType (WINAPI *)(ArgTypes...);
};

template <typename RetType, typename... ArgTypes>
auto proxyFunction(
    LPCSTR dllPath,
    LPCSTR functionName,
    RetType (*proxy)(ArgTypes...))
{
    using funcType = typename proxyTraits<RetType, ArgTypes...>::funcType;
    funcType funcPtr = (funcType) GetProcAddress(LoadLibraryA(dllPath), functionName);

    if (funcPtr)
        std::cout << "Proxy success" << std::endl;
    else
        std::cout << "Proxy fail" << std::endl;
    
    return funcPtr;
}

BOOL GetFileVersionInfoProxy(LPCSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    auto getFileVersion = proxyFunction("C:\\Windows\\System32\\Version.dll", "GetFileVersionInfoA", &GetFileVersionInfoProxy);
    if (getFileVersion)
        return getFileVersion(lptstrFilename, dwHandle, dwLen, lpData);

    return FALSE;
}

Live Demo

0
votes

So it turns out that std::function implements the kind of behavior I was looking for.

For example, if my dummy DLL function is defined as follows:

#include <Windows.h>

#define DllExport extern "C"  __declspec( dllexport )

DllExport int SQUARE(int number){
    return number * number;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved){
    return TRUE;
}

Then I would import it as follows:

#include <Windows.h>
#include <iostream>
#include <functional>

HMODULE dummyHandle = nullptr;

template<typename signature>
std::function<signature> proxyFunction(HMODULE hModule, LPCSTR functionName){
    auto funcPtr = GetProcAddress(hModule, functionName);
    std::cout << (funcPtr ? "Proxy success" : "Proxy fail") << std::endl;
    return std::function<signature>(reinterpret_cast<signature*>(funcPtr));
}

int SQUARE(int number){
    auto proxy = proxyFunction<decltype(SQUARE)>(dummyHandle, __FUNCTION__);
    return proxy ? proxy(number) : -1;
}

int main(){
    dummyHandle = LoadLibraryA("C:\\Absolute\\Path\\To\\Dummy.dll");
    if(dummyHandle)
        std::cout << "Square of 7 is " << SQUARE(7) << std::endl;
    else
        std::cout << "Failed to load dummy dll. Last Error: " << GetLastError() <<std::endl;

    system("pause");
}

I like this method since it enables basic static type checking without requiring definitions of auxiliary constructs. However, I still think that there is room for improvement. Every time we call proxyFunction we have to wrap the function into decltype(...). Does anyone have any idea how to hide that bit within the proxyFunction in a simple manner?