使用带有回退功能的新 Windows 功能
我使用动态库和 GetProcAddress 的东西已经有一段时间了,但它总是显得乏味、充满敌意、丑陋的做事方式。
有谁知道一种干净的方法来导入新功能,同时保持与旧操作系统的兼容性。
假设我想使用一个 XML 库,它是 Vista 的一部分。我调用 LoadLibraryW,然后如果 HANDLE 不为空,我就可以使用这些函数。
但我真的不想去 #typedef (void*)(PFNFOOOBAR)(int, int, int)
和 PFNFOOOBAR foo = reinterpret_cast
,全部乘以 50,这样。
有没有一种非黑客的解决方案可以避免这种混乱?
我正在考虑在项目设置中添加coolxml.lib,然后在delayload dll列表中添加coolxml.dll,并且也许复制我将在所需文件中使用的几个函数签名。然后检查 LoadLibraryW 返回是否为非空,如果它非空,则像常规程序流程一样分支到 Vista 分支。
但我不确定 LoadLibrary 和延迟加载是否可以一起工作,以及某些分支预测在某些情况下是否不会弄乱事情。
另外,不确定这种方法是否有效,以及升级到下一个 SDK 后是否不会导致问题。
I've been using dynamic libraries and GetProcAddress stuff for quite some time, but it always seems tedious, intellisense hostile, and ugly way to do things.
Does anyone know a clean way to import new features while staying compatible with older OSes.
Say I want to use a XML library which is a part of Vista. I call LoadLibraryW and then I can use the functions if HANDLE is non-null.
But I really don't want to go the #typedef (void*)(PFNFOOOBAR)(int, int, int)
and PFNFOOOBAR foo = reinterpret_cast<PFNFOOOBAR>(GetProcAddress(GetModuleHandle(), "somecoolfunction"));
, all that times 50, way.
Is there a non-hackish solution with which I could avoid this mess?
I was thinking of adding coolxml.lib in project settings, then including coolxml.dll in delayload dll list, and, maybe, copying the few function signatures I will use in the needed file. Then checking the LoadLibraryW return with non null, and if it's non-null then branching to Vista branch like in a regular program flow.
But I'm not sure if LoadLibrary and delay-load can work together and if some branch prediction will not mess things up in some cases.
Also, not sure if this approach will work, and if it wont cause problems after upgrading to the next SDK.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
IMO、LoadLibrary 和 GetProcAddress 是最好的方法。
(创建一些包装对象来为您处理这个问题,这样您就不会用这种逻辑和丑陋污染您的主代码。)
DelayLoad 带来了安全问题(请参阅此 OldNewThing 帖子)(编辑:但如果您确保永远不会在旧版本上调用这些 API,则不会Windows 版本)。
DelayLoad 还很容易意外地依赖于并非在所有目标上都可用的 API。是的,您可以使用工具来检查在运行时调用了哪些 API,但最好在编译时处理这些事情,IMO,并且这些工具只能检查您在其下运行时实际执行的代码。
此外,避免使用不同的 Windows 头版本编译代码的某些部分,除非您非常小心地隔离代码和传入/传出代码的对象。
这并不是绝对错误 - 对于插件 DLL 之类的事情来说这是完全正常的,其中两个完全不同的团队可能在两个模块上工作,而不知道彼此的目标 SDK 版本 - 但如果您不小心,所以一般情况下最好避免。
如果混合标头版本,可能会出现非常奇怪的错误。例如,我们有一个静态对象,其中包含一个在 Vista 中更改大小的操作系统结构。我们的大部分项目都是针对 XP 编译的,但我们添加了一个新的 .cpp 文件,其名称恰好以 A 开头,并且设置为使用 Vista 标头。然后,该新文件(任意)成为触发要分配的静态对象的文件,使用 Vista 结构大小,但该对象的实际代码是使用 XP 结构构建的。构造函数认为对象的成员与分配对象的代码位于不同的位置。结果发生了奇怪的事情!
一旦我们查明真相,我们就完全禁止这种做法;我们项目中的所有内容都使用 XP 标头,如果我们需要新标头中的任何内容,我们会手动将其复制出来,并根据需要重命名结构。
编写所有 typedef 和 GetProcAddress 内容,并从标头中复制结构和定义是非常乏味的(这似乎是错误的,但它们是二进制接口,因此不会更改)(不要忘记检查 #pragma也打包东西:(),但在我看来,如果你想要最好的编译时问题通知,这是最好的方法,
我确信其他人会不同意!
PS:我在某个地方做了一个小模板。让 GetProcAddress 的东西稍微不那么乏味...尝试找到它;如果我这样做,就会更新它。找到它,但实际上并没有那么有用。事实上,我的代码都没有使用过。它。 :)IMO, LoadLibrary and GetProcAddress are the best way to do it.
(Make some wrapper objects which take care of that for you, so you don't pollute your main code with that logic and ugliness.)
DelayLoad brings with it security problems (see this OldNewThing post) (edit: though not if you ensure you never call those APIs on older versions of windows).
DelayLoad also makes it too easy to accidentally depend on an API which won't be available on all targets. Yes, you can use tools to check which APIs you call at runtime but it's better to deal with these things at compile time, IMO, and those tools can only check the code you actually exercise when running under them.
Also, avoid compiling some parts of your code with different Windows header versions, unless you are very careful to segregate code and the objects that are passed to/from it.
It's not absolutely wrong -- and it's completely normal with things like plug-in DLLs where two entirely different teams probably worked on the two modules without knowing what SDK version each other targeted -- but it can lead to difficult problems if you aren't careful, so it's best avoided in general.
If you mix header versions you can get very strange errors. For example, we had a static object which contained an OS structure which changed size in Vista. Most of our project was compiled for XP, but we added a new .cpp file whose name happened to start with A and which was set to use the Vista headers. That new file then (arbitrarily) became the one which triggered the static object to be allocated, using the Vista structure sizes, but the actual code for that object was build using the XP structures. The constructor thought the object's members were in different places to the code which allocated the object. Strange things resulted!
Once we got to the bottom of that we banned the practise entirely; everything in our project uses the XP headers and if we need anything from the newer headers we manually copy it out, renaming the structures if needed.
It is very tedious to write all the typedef and GetProcAddress stuff, and to copy structures and defines out of headers (which seems wrong, but they're a binary interface so not going to change) (don't forget to check for #pragma pack stuff, too :(), but IMO that is the best way if you want the best compile-time notification of issues.
I'm sure others will disagree!
PS: Somewhere I've got a little template I made to make the GetProcAddress stuff slightly less tedious... Trying to find it; will update this when/if I do.Found it, but it wasn't actually that useful. In fact, none of my code even used it. :)是的,使用延迟加载。这就把丑陋的事情留给了编译器。当然,您仍然必须确保您没有在 XP 上调用 Vista 函数。
Yes, use delay loading. That leaves the ugliness to the compiler. Of course you'll still have to ensure that you're not calling a Vista function on XP.
延迟加载是避免直接使用
LoadLibrary()
和GetProcAddress()
的最佳方法。关于提到的安全问题,您唯一可以做的就是使用 延迟load hooks 以确保(并可选择强制)在 dliNotePreLoadLibrary 通知期间使用正确的系统路径(而不是相对于您的应用文件夹)加载所需的 DLL。当所需的 API 函数不可用时,使用回调还允许您在dliFailLoadLib
/dliFailGetProc
通知中替换您自己的后备实现。这样,其余代码就不必担心平台差异(或很少)。Delay loading is the best way to avoid using
LoadLibrary()
andGetProcAddress()
directly. Regarding the security issues mentioned, about the only thing you can do about that is use the delay load hooks to make sure (and optionally force) the desired DLL is being loaded during thedliNotePreLoadLibrary
notification using the correct system path, and not relative to your app folder. Using the callbacks will also allow you to substitute your own fallback implementations in thedliFailLoadLib
/dliFailGetProc
notifications when the desired API function(s) are not available. That way, the rest of your code does not have to worry about platform differences (or very little).