A colleague of mine recently posed an interesting question: Why would you ever want to specifically target an assembly compilation to x86 or x64? Wouldn’t you always want to use the AnyCPU option?
What are the options?
Here’s a bit of background on what each option does:
- AnyCPU – The assembly is compiled in a CPU-independent manner. If you are compiling an EXE, the EXE will run as an x64 processes when loaded by an x64 version of the .Net Framework on an x64 operating system. Otherwise the EXE will run as an x86 process. When you’re compiling a DLL, the DLL will load as an x64 DLL when loaded from an x64 process. Otherwise, the DLL will load as an x86 DLL.
- x86 – The assembly will always load as an x86 assembly, regardless of the operating system or .Net Framework version. The assembly will not load from an x64 process.
- x64 – The assembly will always load as an x64 assembly, regardless of the operating system or .Net Framework version. The assembly will not load on an x86 operating system or from an x86 process.
Well, it looks like the AnyCPU option is definitely the way to go. You get the most flexibility with the least amount of headaches due to platform and process differences. In most cases (99 out of 100) that is true.
Subtleties of assembly loading
However, there is one subtlety to how assemblies load into processes. A process can only load assemblies that are of the same architecture at runtime as the process itself. An x64 process can only load x64 assemblies, and x86 processes can only load x86 assemblies. This restriction still applies to assemblies compiled with the AnyCPU option. Even though assemblies compiled with the AnyCPU option are processor architecture-independent at compile-time, the assemblies do get tied to a particular processor architecture when they are loaded for execution. If a process tries to load an assembly with an incompatible architecture, a BadImageFormatException will be thrown.
Why does that matter? Wouldn’t all of this be irrelevant if all assemblies were compiled with the AnyCPU option? Yes. Except, that not all code your assembly needs to load or that will be loading your assembly is processor architecture-independent. In particular, this is most apparent when you need to interoperate with un-managed code, such as COM.
So what? I only use managed code
Many .Net developers believe that all of .Net is a brand new, shiny framework that frees them from the headaches or working with legacy runtimes and unmanaged binaries. This is not true. In fact, a large portion of the .Net Framework simply wraps existing un-managed operating system components, such as the EnterpriseServices and DirectoryServices namespaces. Here’s a screenshot of a COM+ component EnterpriseServices uses to extend transaction support into the .Net Framework:
There are also many components and products that are based on un-managed code and only wraps this code for interoperability with .Net assemblies. Many Microsoft applications fall into this category as well, such as Microsoft Office, even if you are using the Office primary interop assemblies.
When do I need to specify x86 or x64 target for my assembly?
Given what we now know about how assemblies are loaded, we can now see that when we need to interoperate with un-managed code, even code that has .Net wrappers, we may need to target our assemblies, particularly when our process or assembly is likely to be loaded as a 64-bit (x64) architecture but the unmanaged code is of 32-bit (x86) architecture.
For example, say we need to automate a Microsoft Office application and our assembly is running on an x64 operating system. We need to have a 32-bit process, since Office is strictly a 32-bit application (that is until Office 2010 is released), but because our process is running on an x64 operating system, the process will be loaded in x64 mode. In this case, we want to specify that our assembly should always be targeted to x86 architecture, even when running on an x64 operating system.