Friday, March 14, 2008

Microsoft has replaced DLL hell with .NET hell

I usually don't blog, but I though I should publish my experiences somewhere in hopes that they might help someone some day with .NET hell.

Earlier today, I released a rapid prototyping tool to the members of my team. It worked fine on my machine, but as soon as I released it I was getting bug reports back. For the purposes of this post we can refer to the tool as some_csharp.exe. When you executed some_csharp.exe you would get the following output:

C:\some_path>some_csharp.exe

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembl
y 'CSWrapper, Version=1.0.2938.18330, Culture=neutral, PublicKeyToken=null' or o
ne of its dependencies. This application has failed to start because the applica
tion configuration is incorrect. Reinstalling the application may fix this probl
em. (Exception from HRESULT: 0x800736B1)
File name: 'CSWrapper, Version=1.0.2938.18330, Culture=neutral, PublicKeyToken=n
ull' ---> System.Runtime.InteropServices.COMException (0x800736B1): This applica
tion has failed to start because the application configuration is incorrect. Rei
nstalling the application may fix this problem. (Exception from HRESULT: 0x80073
6B1)
at SOME.SOME.Main(String[] args)

So, obviously it looks like .NET is having trouble finding CSWrapper.dll, so the first thing you do is get a directory listing. For the record, CSWrapper.dll is written in C++/CLI.

C:\some_path>dir

[stuff omitted]

01/24/2008 11:30 AM 56,832 CSWrapper.dll
03/14/2008 03:35 PM 20,480 some_csharp.exe


C:\some_path>

That's funny, some_csharp.exe can't find CSWrapper.dll even though it is in the right place. So I had to take a second look at the error message. "Could not load file or assembly 'CSWrapper, Version=1.0.2938.18330, Culture=neutral, PublicKeyToken=null' or one of its dependencies."

Now, this is your first clue that .NET is crap. It has its fancy, managed, JIT compiled CLI and it can't even tell me which DLL it it missing? Even a simple C app could tell me which LoadLibrary() call failed. So now I am stuck trying to figure out on my own which library .NET couldn't find.

Now, you would think that it would be easy to figure out your program's requirements in Visual Studio 2005, right? Wrong! I mean, Visual Studio (and .NET) are supposed to make your life easier, right? Shouldn't it be easy to get a list of my DLL's requirements? Even ILDasm was no help. It gives you some information about the manifest, but it didn't give me the information I needed. Thankfully, the copy of CSWrapper.dll.intermediate.manifest in the bin/Debug directory of the CSWrapper project does tell you exactly what it is looking for. The pertinent part is as follows:

<assemblyIdentity type='win32' name='Microsoft.VC80.DebugCRT'
version='8.0.50727.762' processorArchitecture='x86'
publicKeyToken='1fc8b3b9a1e18e3b' />

So, I think I have the problem nailed down. My DLL needs version 8.0.50727.762 of the Microsoft CRT but the version that shipped with .NET 2.0 is only 8.0.50727.40. 8.0.50727.762 is the version that shipped with Visual Studio 2005 SP1. Thankfully, it is available as a redistributable package from Microsoft here. I am sure that this will fix the problem, but, no beans.

I finally figure out that the publicly distributed version linked to above does not contain Microsoft.VC80.DebugCRT. It contains Microsoft.VC80. That's right folks, you can't run the debug version of this DLL unless you have Visual Studio 2005 SP1 installed (which is why it worked on my system).

So, I take a version of the DLL built in release mode and now my program works on the system with version 8.0.50727.762 of the CRT. However, this time I distribute the release version of the DLL with its very own copy of the CRT (it can be found in C:\Program Files\Microsoft Visual Studio 8\VC\redist\x86\Microsoft.VC80.CRT). Just to be explicit, my install package now looks like this:

CSWrapper.dll
some_csharp.exe
Microsoft.VC80.CRT\Microsoft.VC80.CRT.manifest
Microsoft.VC80.CRT\msvcm80.dll
Microsoft.VC80.CRT\msvcp80.dll
Microsoft.VC80.CRT\msvcr80.dll

Now my users don't have to grab the 8.0.50727.762 version of the CRT because it is already installed. The down side of this is that if Microsoft ever releases a patch for the CRT, my DLL will still be using the version it shipped with.

I really like the C# language (even though I think Java is a better solution most of the time because it is actually cross-platform), but I am really starting to hate .NET.

Reference: Microsoft promises end to 'DLL hell'