You don't specify if your environment is .NET or straight Win32.
I am assuming its Win32 because if its .NET the technologies to do this are all much closer to hand in terms of things like the Global Assembly Cache.
In terms of Win32 it is possible to load Dlls from a shared location in one of two ways:
Use LoadLibrary with explicit full paths. This means you cannot use static linking - all dll functions used in all products will have to be accessed via GetProcAddress. You cannot import c++ classes from dll's loaded via LoadLibrary - they must be statically linked to work so this approach may or may not be viable. Its not terribly hard to write shim header files that masquerade as the dll's interface and do a just in time dll load and GetProcAddress as needed for each call.
The other option is to turn the dll's into what are called "side by side assemblies" and install them into the WinSxS store. Don't be scared by the big name. "side by side assembly" means "A Dll file plus manifest file with version information".
Each of the various applications would then put 'strong name' - which includes version information - into its application manifest for each dll it uses, and the Win32 Dll loader will use this to pick the correct instance of the common dll from the WinSxS store. The basic process is described in the MSDN article Guidelines for Creating Side-by-side Assemblies
On Windows versions 6.1 and up (Windows Server 2008 and the ironically named Windows 7) application configuration files DO NOW support the probing element in Application Configuration Files
This means you should be able to provide a path (relative to your application) to a folder containing containing dll assemblies you want to load.
Ok, Ive done some testing on Windows 7, and this works:
Assuming you have an application app1.exe installed in \Program Files\App1, that depends on some common dll "thedll.dll"
In the application folder (\Program Files\App1) create a file App1.exe.config and give it the following contents :-
<configuration>
<windows>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="..\AcmeCommon"/>
</assemblyBinding>
</windows>
</configuration>
Now, create a folder called \Program Files\AcmeCommon, and in it a folder acme.thedll, and copy thedll.dll into \Program Files\AcmeCommon\acme.thedll
Also create a file in AcmeCommon\acme.thedll called acme.thedll.manifest - this will be the assembly manifest describing the assembly called 'acme.thedll'
The contents of acme.thedll.manifest will be:-
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="acme.thedll" version="1.2.3.4" processorArchitecture="x86" type="win32"/>
<file name="thedll.dll"/>
</assembly>
Now we have the common dll, in a common location, as a native sxs assembly. We have the app, with a config file that will, on Windows 7 and 2008 server (and up) tell it to search for assemblies in the common location. But the app is still trying to link to the dll as a dll, rather than via an assembly.
To get the app to load the assembly, we need to add a manifest file to the application. If you are using visual studio, your applications are probably already configured to create and embed manifests via the linker and manifest tool project settings. In which case the easiest way to tell the app about the assembly is to rebuild it after adding the following code to at least one header or c/cpp file in the project :-
#pragma comment(linker,"/manifestdependency:\"type='win32' name='acme.thedll' version='1.2.3.4' processorArchitecture='x86' language='*'\"")
If you are using an older build environment where the manifests are hand made you would need to merge the following xml with app1.exe.manifest in the App1 folder:
<dependency>
<dependentassembly>
<assemblyidentity type="win32" name="acme.thedll" version="1.2.3.4" processorArchitecture="x86" language="*"/>
</dependentassembly>
</dependency>
This should close the circle: When the app loads the win32 loader will load the application manifest (app1.exe.manifest or embedded as a RT_MANIFEST resource) and learn about the "acme.thedll" assembly. It will also load the application config file (app1.exe.config) and learn about the private path to search for assemblies. And it will then load and add "acme.thedll.manifest" to the apps "activation context". Then, when the loader tries to load "thedll.dll" it will search the activation context db, find that its in the acme.thedll assembly, and load it from the assemblies location.