I was surprised to find - and you might be surprised to hear - that Microsoft decided not to allow even pure .NET applications that were built on 32-bit machines with certain versions of Visual Studio to run on the 64-bit CLR.
“What are you talking about?” I hear you ask. Well, I mean that if you have a C# application that is pure, safe, 100% .NET intermediate language it may still only be allowed to run on the 32-bit CLR. Applications built by newer versions of Visual Studio .NET 2005 (code named Whidbey), however, will be allowed to run in the 64-bit CLR. Don’t worry, these same applications will work fine on 32-bit CLRs too.
“But”, I hear you insist, “.NET provides platform independence! It has no native machine code! It is pure IL that runs on the CLR. The IL has not changed in 64-bit – so why wouldn’t run on the 64-bit CLR?”
These are good questions. The answer to these questions is, unfortunately, “Just in case!” It is possible, for example, to create pure .NET code that makes use of in-process COM objects that are not pure .NET. In fact it is possible to try to load DLL’s that were not even compiled for the same platform that your .NET application is running on – if you do this your application will not find the DLL or, worse, it will die a horrible death. And the key is that it’s possible to do it without the CLR knowing that you are going to do it at application load time.
So, for example, you can create a C# application that load’s a 32-bit DLL and calls some of the functions in that DLL. If that C# application is loaded into the 64-bit CLR then any attempt to load the same 32-bit DLL will fail.
Clearly, if the OS knew FOR SURE at load time that you had no 32-bit dependencies then your application could be loaded quite safely into the 64-bit CLR. However, 1.0 and 1.1 assemblies no nothing of “bitness” – they were built by compilers that do not know anything about the 64-bit edition of Windows. So, although they can be marked as containing only IL, they may still have 32-bit dependencies in there somewhere. So, to prevent older .NET applications from breaking in unexpected ways on 64-bit Windows the CLR team decided to force them to run in the WOW on the 32-bit CLR.
Interestingly this restriction does not extend to DLL’s that are loaded by applications that manage to be executed by the 64-bit CLR. If I compile my application with Whidbey and target AnyCPU the application will load into the 64-bit CLR. If I then reference any older assemblies from my application they will be allowed to load. So be careful doing this, if the older referenced assemblies do anything that is x86 specific your application may crash.
So, let’s look at the application loading policy used by the new Windows 64-bit operating system and the .NET Framework. The loading policy is just starting to get complicated enough that a diagram would help a lot here…
The flow chart above goes like this…
- First, the file format is checked. If it is PE32+ then it is clearly a 64-bit application. PE32+ is the new 64-bit executable file format. If you target AnyCPU or x86 in Whidbey then the PE32 file format is used to ensure that it will run in 32-bit Windows.
- Next, if the file is not a .NET application then it is loaded into the WOW.
- If it is a .NET application but it is not marked as ILONLY it is loaded into the WOW.
- If it IS marked as ILONLY, but was compiled by an older compiler then, “just in case”, it is loaded into the WOW.
- Finally, if it is an ILONLY .NET assembly and was compiled by Whidbey then it checks to see if it was explicitly marked by Whidbey as requiring the 32-bit runtime. If so then it is loaded in the WOW.
- Otherwise it is loaded into the 64-bit runtime.
There is an interesting aside here. As I mentioned earlier, assemblies that were compiled by Whidbey and marked as AnyCPU use the PE32 format for executable files. Some skullduggery is required to allow this file to load into the 64-bit runtime which expects the PE32+ format. A process that has “hack” written all over it has the CLR modify the format of the file and then pass it back to the OS which eventually passes it pack to the CLR for execution.
So, the take away here is if you want your .NET applications to load in the 64-bit CLR on 64-bit machines and still work on 32-bit machines then compile them with Visual Studio 2005 and mark them as AnyCPU (which sets the ILONLY flag).