Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

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

[Edit]

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

  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.
like image 855
MichaC Avatar asked Nov 12 '08 23:11

MichaC


3 Answers

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.

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.

Here's on page that describes the IMPORTS directive:

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.

like image 155
Michael Burr Avatar answered Nov 13 '22 01:11

Michael Burr


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.

like image 36
Judge Maygarden Avatar answered Nov 13 '22 00:11

Judge Maygarden


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.

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:

  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)

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.

like image 1
MichaC Avatar answered Nov 13 '22 01:11

MichaC