如前所述here,我正在尝试找到LNK2019问题的解决方法,该问题在构建使用C ++模板的静态库时出现,并将源与标头分开以使代码与其他模块保持私有项目。我相信我几乎得出了一个有效的结论(针对我的具体情况),但我不完全确定这是否是正确/最好的方式,并且想知道是否有人有任何建议,改进/意见添加?
目标是进行一些类型检查以查看模板的签名是否与目标原型函数的签名匹配,进行一些私有处理,并返回它是否成功。 请注意,我已从上述链接中的解决方案的先前版本中删除了SdkHookMgr.h
和SdkHookMgr.cpp
,并将所有内容合并回 SdkLib.h
和 SdkLib.cpp
进入一个静态类,以便清楚。
SdkLib.h:
#include <typeinfo>
#ifdef MY_EXPORTS
# define MYDECL __declspec(dllexport)
#else
# define MYDECL
#endif
// Prototypes
typedef HMODULE (WINAPI *HookLoadLibraryA)( LPCSTR lpFileName );
//...
class CHook;
class CHookManager;
MYDECL BOOL WINAPI ValidateHook( CHook *hook );
class CHook
{
public:
CHook() : m_type(NULL), m_target(NULL), m_result(FALSE) {};
CHook( const char *type, PVOID target ) : m_type(type), m_target(target) {
m_result = ValidateHook(this);
};
const char *m_type;
PVOID m_target;
BOOL m_result;
};
class CHookManager
{
public:
template <typename HookFunction> static BOOL Hook(HookFunction target)
{
const type_info& type = typeid(HookFunction);
CHook *hook = new CHook( type.name(), target );
return hook->m_result;
}
};
SdkLib.cpp:
#include <SdkLib.h>
IDXDECL BOOL WINAPI ValidateHook( CHook *hook )
{
// Do type checking, private processing, etc here...
return TRUE;
}
DemoDLL.cpp:
#include <SdkLib.h>
HMODULE WINAPI Hooked_LoadLibraryA( LPCSTR lpFileName )
{
DebugBreak();
}
// The function that starts the rollercoaster.
// - Syntax: Hook< prototype >( target )
if!(CHookManager::Hook<HookLoadLibraryA>(Hooked_LoadLibraryA))
cout << "Failed to create hook for LoadLibraryA!" << endl;
答案 0 :(得分:0)
您可能会发现typeid
的结果在DLL和主程序之间不一致。 (例如,参见typeid result across different dll's。)
由于您的可能挂钩列表有限,我觉得重载函数比模板更好。然后,您将没有DLL问题,并且将在编译时检查每个挂钩的有效性。这是我正在考虑的那种事情的一个例子;显然在实践中你将它分成单独的定义和声明,定义生活在DLL中,所以它们都被彻底分离出来。
class CHookManager {
public:
BOOL Hook(HookLoadLibraryA hook) {
assert(sizeof hook<=sizeof(uintptr_t));
return ValidateHook((uintptr_t)hook,"LoadLibraryA");
}
BOOL Hook(HookLoadLibraryW hook) {
assert(sizeof hook<=sizeof(uintptr_t));
return ValidateHook((uintptr_t)hook,"LoadLibraryW");
}
};
(注意,这显示了这种方法的一个缺点 - 每个函数签名只能有一个钩子。为了完整起见,我提到了这一点,但我认为这还没有证明是一个问题。)
(您可能希望用编译时断言替换断言,如果有的话。)
ValidateHook将使用strcmp
来确定挂钩的钩子。一旦弄清楚它是哪个钩子,它就会将uintptr_t
转换为适当的函数指针类型。它知道指针最初是该钩子的正确类型,因为你正在使用C ++重载机制来完成所有操作。 (或者你可以为所有钩子类型设置枚举,而不是传入一个字符串 - 这取决于你。关键部分是你可以完全控制传递的值,以便DLL和调用代码肯定使用匹配值。)
这段代码生成起来有点令人讨厌,但是如果你已经有了typedef名称列表,那么你可以在你选择的编辑器中使用正则表达式搜索和替换或键盘宏来创建相应的代码。或者你可以使用类似the so-called "X-Macro"的东西来自动生成整个事物。