Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unmanaged C++ DLL's with same name coexisting in same process

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.

like image 944
Irv Avatar asked Jan 11 '13 15:01

Irv


2 Answers

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).

like image 68
ssube Avatar answered Sep 29 '22 00:09

ssube


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;
like image 25
Owen Wengerd Avatar answered Sep 29 '22 02:09

Owen Wengerd