7

Using Visual Studio c++ V10, I am trying to figure out how to build a DLL and resolve a DLL naming conflict. Here are the details.

Company S ships a product called M.EXE. Assume that M.EXE is installed in \S\BIN\M.EXE. Company S statically links to a DLL called U.DLL, which is installed in \S\BIN\U.DLL. U.DLL contains open source code, and is built with Visual C++ compiler options /Zc:wchar_t-, which doesn't recognize wchar as a native type.

Company C ships a DLL called O.DLL, and publishes the API's for this DLL, and ships an import library for O.DLL. Assume that O.DLL is installed in \C\BIN\O.DLL. O.DLL statically links to a DLL called U.DLL, which is installed in \C\BIN\U.DLL. U.DLL is built on the same open source code, but is built with Visual C++ compiler options /Zc:wchar_t, which does recognize wchar_t as a native type.

Ideally Company C and Company S would agree to build U.DLLusing the same Visual C++ options, but that is not possible.

M.EXE from Company S is extensible, in that I can build my own DLL in unmanaged C++, call it NODE.DLL that M.EXE will invoke if I set everything up correctly. I would like to build NODE.DLL so that it statically links to O.DLL from Company C. But the problem is that once M.EXE is running, it has loaded the U.DLL library from \S\BIN, and the symbols from \S\BIN\U.DLL are slightly different than the ones in \C\BIN\U.DLL, because of how U.DLL was built by each company. So when M.EXE attempts to load NODE.DLL, it fails, because when NODE.DLL loads O.DLL, which needs U.DLL, the symbols needed from \C\BIN\U.DLL are not there, because Windows sees U.DLL as already being loaded.

A diagram of the situation is as follows:

M.EXE static link to -> \S\BIN\U.DLL
M.EXE dynamic link to -> NODE.DLL
NODE.DLL static link to  O.DLL
O.DLL static link to \C\BIN\U.DLL

Effectively, I need both \S\BIN\U.DLL and \C\BIN\U.DLL to co-exist in the same process space, and have M.EXE use its version of U.DLL and O.DLL use its version of U.DLL.

Note that I do not have the option to rebuild M.EXE or O.DLL to change how they each load U.DLL. They come from third parties, so the static linking can't be changed. I also don't have the option of using LoadLibrary on O.DLL, because it is a C++ library, provided with an import library.

I believe that manifests can be used so that when I build NODE.DLL that is statically linked to O.DLL, I set things up in the manifest of NODE.DLL so that O.DLL loads its own copy of U.DLL that is installed in \C\BIN\U.DLL. I just cannot figure out how to do this. Ideally, I'd like to not modify the manifest of O.DLL, but if that is the only solution, I'll live with that.

MSalters
  • 173,980
  • 10
  • 155
  • 350
Irv
  • 540
  • 4
  • 13

4 Answers4

4

You can have multiple DLLs with the same filename in the same process by loading one or more of them with absolute paths. This does require the DLL to be dynamically loaded, but behavior is otherwise identical.

Instead of linking during the build process, you need to std::string moduleName = appPath + "\s\bin\u.dll"; LoadModule(moduleName.c_str()). Because this is unambiguous as to which DLL needs loaded, it allows you to load multiple ones with the "same" name.

Once you have the module loaded, you can assign each of the necessary functions to function pointers, then either wrap those or use the legal but little-used syntax of calling functions pointers as normal functions (funcPtr(params)).

If you are on a more recent version of Windows, you may be able to use DLL manifests to strengthen the versioning/naming around the module and cause the EXE to load a different DLL than it typically would. I'm not familiar with how exactly this would be done, although it is documented on MSDN (and probably here as well).

ssube
  • 47,010
  • 7
  • 103
  • 140
  • This is not an option, because I don't have the ability to rebuild M.EXE or O.DLL . They come from different third parties. – Irv Jan 11 '13 at 16:13
  • If one of those modules is using a partial or absolute path or one is using dynamic loading, then you might be able to get by. Otherwise, manifests will be your only chance. – ssube Jan 11 '13 at 17:21
  • I believe that manifests are the only choice. I'd like to know what to put in the manifest to make things work. Both M.EXE and O.DLL are built using import libraries that then load U.DLL using the normal DLL loading rules. Each company puts their own version of U.DLL in their own BIN directory. When M.EXE is run, it picks up its copy of U.DLL. When you write a standard EXE that uses O.DLL, it picks up its copy of U.DLL. – Irv Jan 11 '13 at 18:18
1

You can resolve the source DLL programmatically at runtiome by using the /delayload linker option (Linker/Input/Delay Loaded DLLs in VS project properties) along with a custom hook. In one of your source files, you'll need to define and register a delayload hook function. In the hook function's dliNotePreLoadLibrary notification handler, simply call LoadLibrary with an explicit path to the desired DLL, then pass the DLL's HMODULE back to the delayload code. The delayload code will resolve the imported functions to the DLL that you give it regardless whether another DLL of the same name is already loaded into the process.

Off the top of my head, your hook will look something like this (where MyCustomLoadLibrary needs to be replaced with code that calls LoadLibrary with the complete path to the desired DLL in the conflicting case, or just the unqualified filename otherwise):

#include <delayimp.h>
FARPROC WINAPI MyDliNotifyHook( unsigned dliNotify, PDelayLoadInfo pdli )
{
  if( dliNotify == dliNotePreLoadLibrary )
    return (FARPROC)MyCustomLoadLibrary( pdli->szDll );
  return NULL;
}
extern "C" PfnDliHook __pfnDliNotifyHook2 = MyDliNotifyHook;
Owen Wengerd
  • 1,628
  • 1
  • 11
  • 11
  • This only works if NODE.DLL is the one linking to the conflicting DLL. After re-reading your question, I see that you say it is one of the dependent DLLs that links to the conflicting DLL. In that case, you'll have to resolve the import address table of the dependent DLL programmatically. That's not difficult to do, but it's too complex to describe the steps here. – Owen Wengerd Jan 14 '13 at 05:29
  • 'resolve the import address table of the dependent DLL programmatically': can you give a pointer, wehre I can read more? – harper Jan 14 '13 at 06:19
  • 1
    There are two cases: the case where the loader can successfully resolve all imports (albeit to the wrong DLL); and the case where the loader cannot resolve the imports. If your case is the former, I can provide some pointers. If your case is the latter, you have to essentially reproduce the Windows loader code (resolving imports and applying fixups, then calling the entry point function). There is precious little documentation and I don't know of any public sources of information. It is not for the faint of heart, but it is possible: I have done it successfully. – Owen Wengerd Jan 16 '13 at 02:17
  • "If your case is the former, I can provide some pointers". Yes, please Sir. – harper Jan 16 '13 at 06:14
  • harper, I just realized that you are not the OP. Since this is off-topic for the OP, please start your own question. – Owen Wengerd Jan 16 '13 at 16:55
  • @Owen-Wengerd - my case is the latter. The loader cannot resolve all of the imports. Some are the same, and some are different. Ideally, because both M.EXE and O.DLL are built by different third parties and I want to get support from each of the third parties, I really want M.EXE to use the implementation of U.DLL that it supplies, and have O.DLL to use the implementation of U.DLL that it supplies. As I said in the original post, I think it can be done with manifests. I just can't figure manifests out. – Irv Jan 16 '13 at 17:16
  • @Irv I have very little experience using manifests because my work usually requires support for older versions of Windows. It could work, and in theory should work, but I'm afraid I can't offer any concrete help with manifests. – Owen Wengerd Jan 16 '13 at 20:10
0

Try using LoadLibrary and GetProcAddress. This will require restructuring your code to use function pointers everywhere though. See:

MSDN web page for LoadLibrary

Joel Jones
  • 49
  • 3
  • This is not an option, because I don't have the ability to rebuild M.EXE or O.DLL . They come from different third parties. – Irv Jan 11 '13 at 16:13
0

You're lucky that U.DLL is Open Source. You will need to build a version that supports both the /Zc:wchar_t- and /Zc:wchar_t functions. The first option merely defines wchar_t- as unsigned short. You'll get a ton of linker warnings for duplicate symbols, for each function that doesn't have a wchar_t argument, but otherwise you just end up with a fatter DLL.

Mind you, if there are any global or static variables using wchar_t, then you'll have two copies of those as well. But you'd have the same effect if you shoehorned two copies of U.DLL in one process.

MSalters
  • 173,980
  • 10
  • 155
  • 350