【问题标题】:Using new Windows features with fallback使用带有后备功能的新 Windows 功能
【发布时间】:2010-12-10 09:14:17
【问题描述】:

我使用动态库和 GetProcAddress 的东西已经有一段时间了,但它总是显得乏味、智能敌对和丑陋的做事方式。

有没有人知道在保持与旧操作系统兼容的同时导入新功能的简洁方法。

假设我想使用一个 XML 库,它是 Vista 的一部分。我调用 LoadLibraryW,然后如果 HANDLE 不为空,我可以使用这些函数。

但我真的不想去#typedef (void*)(PFNFOOOBAR)(int, int, int)PFNFOOOBAR foo = reinterpret_cast<PFNFOOOBAR>(GetProcAddress(GetModuleHandle(), "somecoolfunction"));,一直都是 50 倍。

有没有一种非骇客的解决方案可以避免这种混乱?

我正在考虑在项目设置中添加coolxml.lib,然后将coolxml.dll 包括在延迟加载dll 列表中,并且可能复制我将在所需文件中使用的几个函数签名。然后检查 LoadLibraryW 返回的非空值,如果它是非空值,则像常规程序流一样分支到 Vista 分支。

但我不确定 LoadLibrary 和 delay-load 是否可以一起工作,以及某些分支预测是否在某些情况下不会搞砸。

另外,不确定这种方法是否可行,以及升级到下一个 SDK 后是否会出现问题。

【问题讨论】:

  • 这是您真正想要一个小脚本为您生成函数加载和标头代码的情况之一。

标签: c++ c windows visual-studio fallback


【解决方案1】:

IMO、LoadLibrary 和 GetProcAddress 是最好的方法。

(制作一些包装器对象来为您处理这些问题,这样您就不会因为这种逻辑和丑陋而污染您的主代码。)

  1. DelayLoad 带来了安全问题 (see this OldNewThing post)(编辑:但如果您确保从未在旧版本的 Windows 上调用这些 API,则不会)。

    DelayLoad 还很容易意外地依赖于并非在所有目标上都可用的 API。是的,您可以使用工具来检查您在运行时调用了哪些 API,但最好在编译时处理这些事情,IMO,这些工具只能检查您在它们下运行时实际执行的代码。

  2. 此外,请避免使用不同的 Windows 标头版本编译代码的某些部分,除非您非常小心地隔离代码和传递给/从它传递的对象。

    这并不是绝对错误的——这对于插件 DLL 之类的事情是完全正常的,其中两个完全不同的团队可能在不知道彼此针对的 SDK 版本的情况下处理这两个模块——但如果你使用它可能会导致棘手的问题不小心,所以一般情况下最好避免。

    如果您混合标头版本,您可能会遇到非常奇怪的错误。例如,我们有一个静态对象,其中包含一个 OS 结构,它在 Vista 中改变了大小。我们的大部分项目都是为 XP 编译的,但是我们添加了一个新的 .cpp 文件,该文件的名称恰好以 A 开头,并且设置为使用 Vista 头文件。然后(任意)该新文件成为触发静态对象分配的文件,使用 Vista 结构大小,但该对象的实际代码是使用 XP 结构构建的。构造函数认为对象的成员与分配对象的代码位于不同的位置。结果很奇怪!

    一旦我们查明真相,我们就完全禁止了这种做法;我们项目中的所有内容都使用 XP 头文件,如果我们需要新头文件中的任何内容,我们手动将其复制出来,并在需要时重命名结构。

编写所有 typedef 和 GetProcAddress 的东西,从头文件中复制结构和定义是非常乏味的(这似乎是错误的,但它们是二进制接口,所以不会改变)(不要忘记检查对于#pragma pack 的东西,也是:(),但如果你想要最好的编译时问题通知,IMO 是最好的方法。

我相信其他人会不同意!

PS:在某个地方,我做了一个小模板,让 GetProcAddress 的东西稍微不那么乏味......试图找到它;我会在/如果我这样做时更新它。找到它,但它实际上并没有那么有用。事实上,我的代码甚至都没有使用它。 :)

【讨论】:

    【解决方案2】:

    是的,使用延迟加载。这将丑陋留给编译器。当然,您仍然必须确保您没有在 XP 上调用 Vista 函数。

    【讨论】:

    • 好的,我应该如何处理函数签名?我应该将 _WIN32_WINNT 留在 0x0502 并复制粘贴我需要的签名,还是有更清洁/更安全/更好的方法?
    • 另外,用LoadLibrary验证dll加载成功了吗?
    • 我也对此感兴趣。请说明如何避免从 XP 调用 Vista 函数。检查 LoadLibrary 是不够的,因为 kernel32.dll 中也有新功能。
    • 另外我想知道如何提供功能签名。我可以想象一个包装器 cpp 文件,它在 0x0600 处定义 WIN32_WINNT 并在可用时调用函数。
    • 使用 DelayLoad 检测功能是一个安全漏洞:blogs.msdn.com/b/oldnewthing/archive/2010/11/11/10089223.aspx
    【解决方案3】:

    延迟加载是避免直接使用LoadLibrary()GetProcAddress() 的最佳方法。关于提到的安全问题,您唯一能做的就是使用delay load hooks 确保(并可选择强制)在dliNotePreLoadLibrary 通知期间使用正确的系统路径加载所需的DLL,而不是相对到您的应用程序文件夹。当所需的 API 函数不可用时,使用回调还允许您在 dliFailLoadLib/dliFailGetProc 通知中替换您自己的后备实现。这样,您的其余代码就不必担心平台差异(或很少)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-03
      • 2012-10-17
      • 1970-01-01
      • 2018-10-28
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      相关资源
      最近更新 更多