C++中如何动态定义成员函数
How to dynamically define member functions in C++
我有这个对象,它只是一个外部 dll 的包装器,在 Windows 中。在创建对象时,构造函数在注册表中查询密钥,然后在包含 dll 的文件夹中查询此密钥的值,然后加载 dll,获取此 dll 的初始化程序的 ProcAddress,并对其进行初始化。
然后,我想向其他客户提供此 dll 的所有功能,因此我的 class-wrapper 提供成员函数,用于检查我们是否有 GetProcAddress '它,如果不是那么我们就这样做,得到指向这个函数的指针,然后调用它。
我不想在构造函数时执行所有的 GetProcAddress'es,因为有数百个函数,并且给定的客户端很可能只需要其中的一小部分,所以 GetProcAddress'ing 一切都是浪费.
但是现在,我需要定义这些功能,感觉就像是一个巨大的复制粘贴。所有的成员函数都在做完全相同的事情,比如
ResultType Wrapper::pertainingMemberFunction(...)
{
if (this->pointerToPertainingDLLFunction == nullptr) {
this->pointerToPertainingDLLFunction =
reinterpret_cast<pertainingType>(GetProcAddress(HMODULEDLL, "pertainingName"));
}
return this->pointerToPertainingDLLFunction (...);
}
这真的感觉像是一个非常次优的解决方案。我觉得必须有某种方法可以将所有这些代码模板化,以便在编译时正确生成。或者也许最初的方法不是最好的开始,我不确定。
将逻辑提取到函数模板中非常简单:
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", ...);
}
假设函数声明可用,可以将包含方法指针的 class 字段公开为可调用对象:
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;
}
更复杂的方法可能包括使用专用的可调用模板 class 而不是原始指针。
其他解决方案都是合理的,但相当复杂。
事实证明,您可以从 Visual Studio 那里获得更多帮助。原因是您的行为与 Visual Studio 功能、delay-loaded DLL 的行为密切相关。 Visual Studio 对 delay-loaded DLL 的实现通过生成 GetProcAddress
对 DLL 的所有函数的调用来工作,这正是您所需要的。
现在看来,您的特定功能是读取注册表项以查找特定的 DLL。正好,delay-loading机制有一个钩子机制。如果您定义 __pfnDliNotifyHook2
,它会在调用 LoadLibrary
之前调用并通知 dliNotePreLoadLibrary
。
您的挂钩函数可以替代它自己的 LoadLibrary
函数,并从所需位置加载 DLL。只有 return HMODULE
和 Visual Studio 将使用它来生成 GetProcAddress
调用。
因此,您得到的解决方案如下所示:
ResultType Wrapper::DllFunction(...)
{
return DLLFunction (...);
}
很简单,不是吗? Visual Studio 会注意到 DllFunction
来自 delay-loaded DLL 并使用 __pfnDliNotifyHook2
.[=22 中的 HMODULE
插入 GetProcAddress
调用=]
我有这个对象,它只是一个外部 dll 的包装器,在 Windows 中。在创建对象时,构造函数在注册表中查询密钥,然后在包含 dll 的文件夹中查询此密钥的值,然后加载 dll,获取此 dll 的初始化程序的 ProcAddress,并对其进行初始化。
然后,我想向其他客户提供此 dll 的所有功能,因此我的 class-wrapper 提供成员函数,用于检查我们是否有 GetProcAddress '它,如果不是那么我们就这样做,得到指向这个函数的指针,然后调用它。
我不想在构造函数时执行所有的 GetProcAddress'es,因为有数百个函数,并且给定的客户端很可能只需要其中的一小部分,所以 GetProcAddress'ing 一切都是浪费.
但是现在,我需要定义这些功能,感觉就像是一个巨大的复制粘贴。所有的成员函数都在做完全相同的事情,比如
ResultType Wrapper::pertainingMemberFunction(...)
{
if (this->pointerToPertainingDLLFunction == nullptr) {
this->pointerToPertainingDLLFunction =
reinterpret_cast<pertainingType>(GetProcAddress(HMODULEDLL, "pertainingName"));
}
return this->pointerToPertainingDLLFunction (...);
}
这真的感觉像是一个非常次优的解决方案。我觉得必须有某种方法可以将所有这些代码模板化,以便在编译时正确生成。或者也许最初的方法不是最好的开始,我不确定。
将逻辑提取到函数模板中非常简单:
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", ...);
}
假设函数声明可用,可以将包含方法指针的 class 字段公开为可调用对象:
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;
}
更复杂的方法可能包括使用专用的可调用模板 class 而不是原始指针。
其他解决方案都是合理的,但相当复杂。
事实证明,您可以从 Visual Studio 那里获得更多帮助。原因是您的行为与 Visual Studio 功能、delay-loaded DLL 的行为密切相关。 Visual Studio 对 delay-loaded DLL 的实现通过生成 GetProcAddress
对 DLL 的所有函数的调用来工作,这正是您所需要的。
现在看来,您的特定功能是读取注册表项以查找特定的 DLL。正好,delay-loading机制有一个钩子机制。如果您定义 __pfnDliNotifyHook2
,它会在调用 LoadLibrary
之前调用并通知 dliNotePreLoadLibrary
。
您的挂钩函数可以替代它自己的 LoadLibrary
函数,并从所需位置加载 DLL。只有 return HMODULE
和 Visual Studio 将使用它来生成 GetProcAddress
调用。
因此,您得到的解决方案如下所示:
ResultType Wrapper::DllFunction(...)
{
return DLLFunction (...);
}
很简单,不是吗? Visual Studio 会注意到 DllFunction
来自 delay-loaded DLL 并使用 __pfnDliNotifyHook2
.[=22 中的 HMODULE
插入 GetProcAddress
调用=]