1

我有这个对象,它只是 Windows 中外部 dll 的包装器。在创建对象时,构造函数会在注册表中查询键,然后使用 dll 查询该键的值以查找文件夹,然后加载 dll,获取此 dll 的初始化程序的 ProcAddress 并对其进行初始化。

然后,我想向其他客户提供这个 dll 的所有功能,所以我的类包装器提供了成员函数来检查我们是否有GetProcAddress '它,如果没有,那么我们就这样做,获取指向这个函数的指针,然后调用它。

我不想在构造函数时执行所有 GetProcAddress,因为有数百个函数,并且很可能给定的客户端只需要一小部分,所以 GetProcAddress 一切都是浪费。

但是现在,我需要定义这些函数,感觉就像一个巨大的复制粘贴。所有成员函数都在做完全相同的事情,比如

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

这真的感觉像是一个非常次优的解决方案。我觉得必须有某种方法来模板化所有这些代码,以便在编译时正确生成。或者也许最初的方法不是最好的开始,我不确定。

4

3 回答 3

2

将逻辑分解到函数模板中很简单:

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)...);
}

...然后您可以调用:

ResultType Wrapper::pertainingMemberFunction(...)
{
    return getProcAddressAndCall(pointerToPertainingDLLFunction, "pertainingName", ...);
}
于 2018-03-07T10:07:50.197 回答
1

其他解决方案都是合理的,但相当复杂。

事实证明,您可以从 Visual Studio 获得更多帮助。原因是您的行为与 Visual Studio 功能、延迟加载的 DLL 密切相关。Visual Studio 对延迟加载 DLL 的实现通过为 DLL 的GetProcAddress所有函数生成调用来工作,这正是您所需要的。

现在看来,您的特定功能是读取注册表项以查找特定的 DLL。碰巧的是,延迟加载机制有一个钩子机制。如果你定义,它会在被调用之前被__pfnDliNotifyHook2通知调用。dliNotePreLoadLibraryLoadLibrary

您的钩子函数可以替代它自己的LoadLibrary函数,并从所需位置加载 DLL。只需返回HMODULE,Visual Studio 就会将其用于生成的GetProcAddress调用。

因此,您得到的解决方案如下所示:

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

很简单,不是吗?Visual Studio 将注意到DllFunction来自延迟加载的 DLL 并GetProcAddress使用HMODULE来自您的__pfnDliNotifyHook2.

于 2018-03-07T10:42:40.077 回答
0

假设函数声明可用,则可以避免将包含方法指针的类字段公开为可调用对象:

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;
}

更复杂的方法可能包括使用专用的可调用模板类而不是原始指针。

于 2018-03-07T10:13:36.890 回答