We have lot of products and there are some common DLLs across each product's application. Right now we copy each common DLL into each product's bin directory and treat them as private assembly. This unnecessarily increase the msi size of each product and when a problem occurs in a DLL we have to build each product's msi comprising the DLL and deploy it.
Is there anyway to instruct the product application to use a common private directory to be used for loading DLLs [ using manifest scheme.. ]?
[ Note: Adding the private directory to PATH env will not provide a solution as if there is a DLL with same name exist in SYSTEM directory, that would take the privilege over our private directory ]
-Kartlee
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.
If you're talking about .NET, you can:
- Load your DLL directly from a database by using
Assembly.Load(byte[])
- By using
Assembly.TypeResolve
event
- By using
TypeProvider
class
- By defining a probe directory in your config file
Like:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin"/>
</assemblyBinding>
</runtime>
</configuration>
I am following Chris's answer. Make sure the case is correct on the manifests and configs. Otherwise they will fail.
I was able to get the assembly to be loaded, but the DLL wouldn't be chosen. In my case a Windows DLL in system32 is being chosen instead of my own with the same name. In Dependency Walker, my DLL is loaded, but at runtime, with Process Explorer, Windows' copy is loaded. Any ideas?
I'm not sure if this is what you're looking for but where I work now we use a common UNC path for all of our DLL's.
We have something akin to ...
\\server01\productionLibrary for production read DLLs, each in it's own directory.
and
\\server01\developmentLibrary which mirrors the production library and this is what the developers use as they develop.
When we merge code after testing has been completed we deploy to the production library. All projects reference the production library when they are built into MSI files for deployment. Our automated system builds the projects into MSIs and it verifies that all DLLs are pointing to the production library, so there is no chance that it will use a development copy accidentally.
Hope this helps.
I'm not sure I understand the question correctly, but if you're in .Net, there's a Global Assembly Cache (GAC): http://en.wikipedia.org/wiki/Global_Assembly_Cache
This caches the assemblies and permits the reuse of them by applications (like with .net framework). You simply will need to register the assembly to GAC on the installation, for example provide a "your framework install" with all common assemblies, and this can be deployed only once.
This may not help you, but... you CAN load dlls from arbitrary directories AND still rely on normal dynamic linking to them as long as you can control when the dlls are loaded via dynamic linking and ensure that you've already loaded the dll explicitly using a full path before it's loaded dynamically.
This is only likely to be of help if you're writing a plugin system where your plugins are dynamically linked to dlls which you wish to keep in a non standard directory. If you then know all about the dlls they're linked to you can explicitly load these dlls directly using their full path before you load the dlls (plugins) that depend on them dynamically. Since the dll is already in memory when your plugin needs to locate it it will use the version in memory. You can the unload the explicit load that you did before loading the plugin and you're good to go.
Unfortunately this wont work if your main exe needs to load dlls from arbitrary places as you cant get in ahead of the normal dll load process.