Hi guys,
long time no post, however I decided it is time to start blogging again...
Right now I run into dll hell problems again :( You don't know DLL hell?
Lucky you ;)
Many years ago we were fighting for each megabyte - talking about HDD (which is really cheap now) and also memory (same applies). That is why Microsoft came with concept of shared libraries.
What it really means? Different applications were able to use shared libraries. If your applications requires msvcrt.dll, they could share one from System32 folder. BUT...
What if your applications requires different version than is currently deployed? Baang - you have problem. What if you have application from one stupid vendor, that overrides default library with old one? It is (or was) really common practice from developers - nothing is important, just to make my application work. I built my application on COM objects (ActiveX, objects that must be registered prior to be used), but I want to be able to run them from CD\USB without installation. Ok, I will just add function that will automatically register them when application is running.
Problem is that COM objects are accessed through registry - you request DLL without knowing exact location, location is retrieved from registry and that's it. Problem is that Last registration wins - if you register library from CD, that you can break other applications that are using same library (Request to registry -> Retrieved location (CD)).
But don't be too pessimistic - with Windows XP, new features arrived that can help us. SideBySide (having different versions of DLLs), .NET (GAC etc and strongly typed libraries) and local and manifest files.
Local and manifest files provides functionality I really love. They allows you to modify application behavior without having access to source codes (in 99,9% cases you don't have access to source code and developers usually don't understand administrator's problems).
If you want to sandbox libraries (normal ones, not COM), just create empty .local file. For example, if affected application is called AppX.exe, create file AppX.exe.local in same folder. This will instruct Windows to search for all libraries at current folder first, even if application is trying to load library using hardcoded path. Even better is when you use AppX.exe.local as folder, not file. In this case just copy all your libraries to this folder.
So consider scenario, where you have application AppX from VendorX. This application is using library LibX.dll, but it requires older version. You will run into problem, you trace problem you LibX.dll and contact vendor - most of the time answer will be "replace LibX.dll in System32 with this file and it will work", which is not something we want to hear. So we just create AppX.exe.local folder next to AppX.exe and copy old version of LibX.dll to this folder and problem is fixed :)
This solves problems with normal libraries, however what to do with COM objects (and they are usually problematic ones)? There is also one solution - create manifest file for your application.
When you query registry, bunch of information is retrieved. Probably most important is InProcServer32 that defines location of library. Using manifest file, you can skip\ignore registry query and provide directly information you want to use.
Following example was taken from great article Escape DLL hell.
Your application is called RegFreeComDemo1.exe and requires COM library VB6Hello.dll. Just create file RegFreeComDemo1.exe.manifest and paste following to this file:
<?xml version="1.0" encoding="utf-8"?>
<assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity name="RegFreeComDemo1.exe" version="1.0.0.0" type="win32" />
<file name="VB6Hello.dll" asmv2:size="20480">
<hash xmlns="urn:schemas-microsoft-com:asm.v2">
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<dsig:DigestValue>jdNWRZtiPPgwxdkjQt8zBQyVsAE=</dsig:DigestValue>
</hash>
<typelib tlbid="{ea995c49-e5d0-4f1f-8489-31239fc9d9d0}" version="2.0" helpdir="" resourceid="0" flags="HASDISKIMAGE" />
<comClass clsid="{97b5534f-3b96-40a4-88b8-19a3bf4eeb2e}" threadingModel="Apartment" tlbid="{ea995c49-e5d0-4f1f-8489-31239fc9d9d0}" progid="VB6Hello.Class1" />
</file>
</assembly>
Query to registry is automatically skipped and information from .manifest is used instead.
There is however one cave eat - you must know information prior to be able to generate such file. There are few ways how to retrieve them:
- Use OleView from Microsoft
- Import to registry and manually dump
- Create Visual Studio project with same name, import libraries and select them as Isolated
- MyProject\Refereces\Add\Browse - load library
- Select added item and open Properties
- Set Isolated to True
- Build solution - manifest file with all required entries is automatically generated
I started to work on utility that will be able to automatically generate manifest file for you, however I am afraid it is not as simple as it looks like :(
If there is some developer out there that is willing to help desperate administrators, please let me know. It would be really nice to have one-click solution to most DLL hell problems ;)
Martin