我一直在使用动态库和GetProcAddress这些东西已经有一段时间了,但它总是显得单调乏味,智能无意义和丑陋的做事方式。
有没有人知道在保持与旧操作系统兼容的同时导入新功能的简洁方法。
说我想使用属于Vista的XML库。我调用LoadLibraryW,然后如果HANDLE非空,我就可以使用这些函数。
但我真的不想去#typedef (void*)(PFNFOOOBAR)(int, int, int)
和PFNFOOOBAR foo = reinterpret_cast<PFNFOOOBAR>(GetProcAddress(GetModuleHandle(), "somecoolfunction"));
,所有那些时间都是50,方式。
是否有一种非黑客的解决方案可以避免这种混乱?
我正在考虑在项目设置中添加coolxml.lib,然后在delayload dll列表中包含coolxml.dll,并且可能会复制我将在所需文件中使用的一些函数签名。然后检查LoadLibraryW返回非null,如果它是非null,则分支到Vista分支,就像在常规程序流中一样。
但是我不确定LoadLibrary和延迟加载是否可以一起工作,如果某些分支预测在某些情况下不会搞砸了。
此外,不确定此方法是否有效,以及升级到下一个SDK后是否会导致问题。
答案 0 :(得分:4)
IMO,LoadLibrary和GetProcAddress是最好的方法。
(制作一些包装对象,为您处理这些问题,因此您不会因为逻辑和丑陋而污染主代码。)
DelayLoad带来了安全问题(see this OldNewThing post)(编辑:如果你确保你从未在旧版本的Windows上调用这些API,那就不行了。)
DelayLoad还使得很容易意外地依赖于所有目标都无法使用的API。是的,您可以使用工具来检查您在运行时调用哪些API,但最好在编译时处理这些事情,IMO,这些工具只能检查您在其下运行时实际运行的代码。
此外,避免使用不同的Windows标头版本编译代码的某些部分,除非您非常小心地将代码和传递给它的对象分开。
这并不是绝对错误 - 对于插件DLL这样的事情是完全正常的,其中两个完全不同的团队可能在两个模块上工作而不知道彼此针对的SDK版本 - 但是如果你可能会导致难题不小心,所以最好避免一般。
如果混合标题版本,则会出现非常奇怪的错误。例如,我们有一个静态对象,其中包含一个在Vista中改变大小的OS结构。我们的大多数项目是为XP编译的,但我们添加了一个新的.cpp文件,其名称恰好以A开头,并设置为使用Vista标头。然后(任意)使用Vista结构大小触发静态对象的那个新文件,但该对象的实际代码是使用XP结构构建的。构造函数认为对象的成员与分配对象的代码位于不同的位置。结果奇怪!
一旦我们达到底线,我们完全禁止了这种做法;我们项目中的所有内容都使用XP标头,如果我们需要更新标头中的任何内容,我们会手动将其复制出来,如果需要,可以重命名结构。
编写所有typedef和GetProcAddress的东西,并复制结构和定义标题(这似乎是错误的,但它们是二进制接口所以不会改变)是非常繁琐的(不要忘记检查对于#pragma pack stuff,也是:(),但如果你想要最好的编译时问题通知,IMO是最好的方法。
我相信别人会不同意!
PS:在某个地方,我有一个模板,我把GetProcAddress的东西稍微不那么乏味......试图找到它;如果我这样做,我会更新。找到它,但实际上并没有那么有用。事实上,我的代码都没有使用它。 :)
答案 1 :(得分:2)
是的,使用延迟加载。这给编译器带来了丑陋。当然,你仍然必须确保你没有在XP上调用Vista功能。
答案 2 :(得分:1)
延迟加载是避免直接使用LoadLibrary()
和GetProcAddress()
的最佳方法。关于提到的安全问题,您可以做的唯一事情是使用delay load hooks确保(并且可选地强制)在dliNotePreLoadLibrary
通知期间使用正确的系统路径加载所需的DLL ,而不是相对于您的应用程序文件夹。当所需的API函数不可用时,使用回调还允许您在dliFailLoadLib
/ dliFailGetProc
通知中替换您自己的回退实现。这样,代码的其余部分就不必担心平台差异(或者很少)。