windows 从两个不同的 C DLL 调用具有相同名称的两个函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/285731/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 11:35:50  来源:igfitidea点击:

Calling two functions with the same name from two different C DLLs

cwindowsvisual-studio-2008dll

提问by MichaC

I have two C DLLs that I need to access in the same executable. I have header files and .LIB files for both libraries. Unfortunately a subset of the functions that I need to access have the exact same names. The best solution I have been able to come up with so far is to use LoadLibrary to load one of the DLLs and explicitly call its methods using GetProcAddress. Is there a way for me to implicitly load both libraries and somehow give the compiler a hint that in one case I want to call OpenApi in DLL A and in the other case I want to call OpenApi in DLL B?

我需要在同一个可执行文件中访问两个 C DLL。我有两个库的头文件和 .LIB 文件。不幸的是,我需要访问的函数子集具有完全相同的名称。到目前为止,我能想出的最佳解决方案是使用 LoadLibrary 加载其中一个 DLL,并使用 GetProcAddress 显式调用其方法。有没有办法让我隐式加载这两个库,并以某种方式给编译器一个提示,在一种情况下我想在 DLL A 中调用 OpenApi,在另一种情况下我想在 DLL B 中调用 OpenApi?

I'm developing my executable in C++ using Visual Studio 2008 and the corresponding C runtime library (msvcr90.dll).

我正在使用 Visual Studio 2008 和相应的 C 运行时库 (msvcr90.dll) 在 C++ 中开发我的可执行文件。

[Edit]

[编辑]

Commenter Ilya asks below what I don't like about the GetProcAddress solution. I don't like it for two reasons:

评论者 Ilya 在下面询问我不喜欢 GetProcAddress 解决方案的哪些方面。我不喜欢它有两个原因:

  1. It makes the code more complex. One line of code to call a function is replaced with three lines of code, one to define the function signature, one to call GetProcAddress, and one to actually call the function.
  2. It's more prone to run-time errors. If I misspell the function name or mess up the signature I don't see the error until run-time. Say I decide to integrate a new version of the dll and one of the method names has changed, it will compile just fine and won't have a problem until the actual call to GetProcAddress, which could possibly even be missed in a test pass.
  1. 它使代码更加复杂。一行调用函数的代码替换为三行代码,一行定义函数签名,一行调用GetProcAddress,一行实际调用函数。
  2. 它更容易出现运行时错误。如果我拼错了函数名或弄乱了签名,我直到运行时才会看到错误。假设我决定集成一个新版本的 dll 并且其中一个方法名称已更改,它会编译得很好,并且在实际调用 GetProcAddress 之前不会出现问题,甚至可能会在测试通过时错过。

采纳答案by Michael Burr

It used to be you could 'rename' an imported symbol using a linker .def file. You probably still can, but it's been so long since .def files have been widely used, it's difficult to locate documentation.

过去,您可以使用链接器 .def 文件“重命名”导入的符号。您可能仍然可以,但是自从 .def 文件被广泛使用以来已经很长时间了,很难找到文档。

The current MSDN documentation lists the IMPORTS directive as a 'reserved keyword'. I'm not sure if this means they removed the functionality, or if they just don't want to support it anymore.

当前的 MSDN 文档将 IMPORTS 指令列为“保留关键字”。我不确定这是否意味着他们删除了该功能,或者他们只是不想再支持它。

Here's on page that describes the IMPORTS directive:

这是描述 IMPORTS 指令的页面:

http://www.digitalmars.com/ctg/ctgDefFiles.html#imports

http://www.digitalmars.com/ctg/ctgDefFiles.html#imports

Other kludgy alternatives are:

其他笨拙的选择是:

  1. create wrapper functions for the conflicting APIs. Those functions can do the LoadLibrary()/GetProcAddress()dance. All other non-conflicting functions can be implicitly linked as normal. Actually, this solution is probably the least kludgy of the 3 in this answer.

  2. create 2 wrapper DLLs such that each links only to one or the other library with the conflicting names. Use differing names in the wrapper DLLs that simply thunk over to the real librarys. Note the wrapper libraries do not need to wrap all the API's - they just need to wrap the conflicting ones.

  1. 为冲突的 API 创建包装函数。这些功能可以LoadLibrary()/GetProcAddress()跳舞。所有其他非冲突函数都可以正常隐式链接。实际上,此解决方案可能是此答案中 3 个解决方案中最不笨拙的。

  2. 创建 2 个包装 DLL,每个 DLL 只链接到一个或另一个名称冲突的库。在包装 DLL 中使用不同的名称,这些名称只是简单地转换为真实的库。请注意,包装库不需要包装所有 API - 它们只需要包装冲突的 API。

回答by Judge Maygarden

You may be able to build new import library files that rename the functions in question and then implicitly link with both DLL modules using these new import libraries. Here is a Microsoft Knowledge Base article that describes the process: How To Create 32-bit Import Libraries Without .OBJs or Source.

您可以构建新的导入库文件,重命名有问题的函数,然后使用这些新的导入库隐式链接两个 DLL 模块。这是描述该过程的 Microsoft 知识库文章:How To Create 32-bit Import Libraries without .OBJs or Source

回答by MichaC

I managed to arrive at Mike B's kludgy alternative #2 (or #3 if you count the original suggestion) after I put a little bit more thought into the matter.

在我对这个问题进行了更多思考之后,我设法得出了 Mike B 的笨拙的替代方案 #2(或者 #3,如果你算上最初的建议)。

This is my favorite of the three for my specific casebecause it seems to require the least amount of work and could probably be the the most obvious to decipher to someone new looking at the code. I believe I can just follow these steps and be up and running:

对于我的特定情况,这是我最喜欢的三个因为它似乎需要最少的工作,并且可能是最容易破解的。我相信我可以按照以下步骤启动并运行:

  1. use some regular expression magic and find/replace to turn the header files with function calls that I have into a wrapper header file and a wrapper implementation file. (where every method in the wrapper dll is differentiated by some common new element to the name, saving me time in having to sort out conflicts) So FunctionCall() becomes WrapperOneFunctionCall() and WrapperTwoFunctionCall() in each respective wrapper dll.
  2. link the wrapper dll to its corresponding base dll.
  3. link my executable to the wrapper dlls, remove links to the base dlls, but still include the constant, enum and struct definitions that don't need to be resolved from the base dll (because my wrapper functions still take the same parameter types)
  1. 使用一些正则表达式魔术并查找/替换将带有函数调用的头文件转换为包装器头文件和包装器实现文件。(其中包装 dll 中的每个方法都通过名称的一些常见新元素进行区分,从而节省了我必须解决冲突的时间)因此 FunctionCall() 在每个相应的包装 dll 中变为 WrapperOneFunctionCall() 和 WrapperTwoFunctionCall()。
  2. 将包装器 dll 链接到其相应的基础 dll。
  3. 将我的可执行文件链接到包装器 dll,删除到基础 dll 的链接,但仍然包括不需要从基础 dll 解析的常量、枚举和结构定义(因为我的包装器函数仍然采用相同的参数类型)

If anyone's still reading this thread my followup question would be: "Is there anything wrong with this solution?"

如果有人仍在阅读此线程,我的后续问题将是:“此解决方案有什么问题吗?”

Thank you to the responders for their help.

感谢响应者的帮助。