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解决方案不喜欢的内容。我不喜欢它有两个原因:
- 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.
- 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.
它使代码更复杂。调用函数的一行代码被三行代码替换,一行用于定义函数签名,一行用于调用GetProcAddress,另一行用于实际调用函数。
它更容易出现运行时错误。如果我拼错了函数名称或弄乱了签名,我在运行时才会看到错误。假设我决定集成新版本的dll并且其中一个方法名称已更改,它将编译得很好,并且在实际调用GetProcAddress之前不会出现问题,甚至可能在测试过程中错过。
3 个解决方案
#1
4
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
Other kludgy alternatives are:
其他kludgy替代品是:
-
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.为冲突的API创建包装函数。这些函数可以执行LoadLibrary()/ GetProcAddress()舞蹈。所有其他非冲突函数可以正常隐式链接。实际上,这个解决方案可能是这个答案中3中最少的一个。
-
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.
创建2个包装器DLL,使每个DLL仅链接到具有冲突名称的一个或另一个库。在包装器DLL中使用不同的名称,这些名称只是简单地转移到真正的库中。请注意,包装器库不需要包装所有API - 它们只需要包装冲突的API。
#2
1
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知识库文章,该文章描述了该过程:如何创建没有.OBJ或源的32位导入库。
#3
1
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的kludgy替代品#2(或者如果算上原始建议的话#3)。
This is my favorite of the three for my specific case because 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:
对于我的具体情况,这是我最喜欢的三个,因为它似乎需要最少量的工作,并且可能是最明显的解密新代码的人。我相信我可以按照这些步骤启动并运行:
- 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.
- link the wrapper dll to its corresponding base dll.
- 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)
使用一些正则表达式魔术和查找/替换来将头文件与我有的函数调用一起转换为包装器头文件和包装器实现文件。 (其中包装器dll中的每个方法都通过名称的一些常见新元素来区分,节省了我必须排除冲突的时间)因此,FunctionCall()在每个相应的包装器dll中变为WrapperOneFunctionCall()和WrapperTwoFunctionCall()。
将包装器dll链接到其对应的基本dll。
将我的可执行文件链接到包装器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.
感谢响应者的帮助。
#1
4
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
Other kludgy alternatives are:
其他kludgy替代品是:
-
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.为冲突的API创建包装函数。这些函数可以执行LoadLibrary()/ GetProcAddress()舞蹈。所有其他非冲突函数可以正常隐式链接。实际上,这个解决方案可能是这个答案中3中最少的一个。
-
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.
创建2个包装器DLL,使每个DLL仅链接到具有冲突名称的一个或另一个库。在包装器DLL中使用不同的名称,这些名称只是简单地转移到真正的库中。请注意,包装器库不需要包装所有API - 它们只需要包装冲突的API。
#2
1
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知识库文章,该文章描述了该过程:如何创建没有.OBJ或源的32位导入库。
#3
1
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的kludgy替代品#2(或者如果算上原始建议的话#3)。
This is my favorite of the three for my specific case because 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:
对于我的具体情况,这是我最喜欢的三个,因为它似乎需要最少量的工作,并且可能是最明显的解密新代码的人。我相信我可以按照这些步骤启动并运行:
- 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.
- link the wrapper dll to its corresponding base dll.
- 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)
使用一些正则表达式魔术和查找/替换来将头文件与我有的函数调用一起转换为包装器头文件和包装器实现文件。 (其中包装器dll中的每个方法都通过名称的一些常见新元素来区分,节省了我必须排除冲突的时间)因此,FunctionCall()在每个相应的包装器dll中变为WrapperOneFunctionCall()和WrapperTwoFunctionCall()。
将包装器dll链接到其对应的基本dll。
将我的可执行文件链接到包装器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.
感谢响应者的帮助。