The world is not all managed (yet!)
When we work in a managed environment (.NET) for so long, we sometimes forget there is still unmanaged code out there.
At my current contract, I've been using MATLAB for some heavy numerical processing. The creators of MATLAB, MathWorks, have a tool called the MATLAB Builder for .NET. This allows me to create a .NET wrapper around a MATLAB function that can then be used easily by any .NET code. It's been working flawlessly for the past few months.
Two weeks ago, we made the move to Visual Studio 2005 (.NET 2.0). The move was on the horizon a few months ago so I knew there were spots where I could utilize some of the new features in .NET 2.0 (generics, partial classes, etc...). It's been fun finally putting those pieces in place.
We're using the Team Edition for Software Developers. So, naturally, the other feature in VS2005 we're going to start using is automated test cases. At first, we'll create test cases for existing code as we modify it and hopefully, down the road, we'll be creating test cases either during development or before development (I won't get into the whole "test first" debate right now...).
So as I was developing test cases for some of my MATLAB integration code I ran into a very strange error. MATLAB exposes a type called "MWArray" which is the base type by which all MATLAB objects that interact with .NET code are based on (MWNumericArray, MWCellArray, MWCharArray, etc...). Whenever I tried to use an MWArray-based object in a unit test I got a TypeInitializationException. The same code worked fine in my application. So I dug out Reflector and checked out the static constructor for MWArray.
I was a pretty simple constructor -- less than 30 lines of decompiled C# code. But one thing did stand out. The MATLAB Builder for .NET provides a custom attribute called NativeGCAttribute. It's used to allow you to tweak the way MATLAB integrates with the CLR garbage collector. Part of the static constructor was to see if this NativeGCAttribute had been applied:
Assembly assembly1 = Assembly.GetEntryAssembly();
NativeGCAttribute attribute1 = (NativeGCAttribute) Attribute.GetCustomAttribute(assembly1, typeof(NativeGCAttribute));
Looking at the stack trace for my TypeInitializationException I noticed the inner exception was "ArgumentNullException: parameter 'element' can not be null". A quick look at the documentation for GetCustomAttribute shows that the first parameter is called 'element'. So now I need to see why Assembly.GetEntryAssembly would return null. According to the docs:
The GetEntryAssembly method can return a null reference (Nothing in Visual Basic) when a managed assembly has been loaded from an unmanaged application.
Ah ha! The light bulb went on. Obviously, the process that hosts my .NET unit tests is an unmanaged application and therefore, there is no entry assembly. I went to the MathWorks support website a dug around a little. This is a known bug that they have fixed and provided a new DLL that I was able to download. My unit tests compiled and ran fine!
So make sure when you're designing components you realize that it may be utilizied from an unmanaged application -- such as a VS2005 test harness or even something like COM interop.