Moving ASP.NET web application from 32 bit to 64 bit
Before we jump in to the details, we need to know few things to understand some basics.
Managed module is a standard 32 bit Microsoft Windows Portable Executable (PE32) file or standard 64 bit Microsoft Windows Portable Executable file (PE32+) that requires CLR to execute.
Parts of managed module:
|PE32 or PE32+ header||
Contains information about
This includes information about
Usually this part contains 2 tables
|Code produced by compiler during compilation. At run time CLR compiles the IL in to native CPU instructions.|
Always keep in mind that all CLR complaint compilers produce IL code. At times this IL code is referred as managed code because it is managed by CLR.
|Assembly||Assembly is a logical group of one or mode modules|
|Manifest||Manifest is a set of metadata tables that have information about list of files in the assembly, the dependencies of the files in the assembly and resource or data files associated with the files|
Before we understand how CLR loads the managed code or assembly we need to study the difference between 32 bit and 64 bit versions of windows.
If your assembly files contain only type-safe managed code, you are writing code that should work on both 32-bit and 64-bit versions of Windows. No source code changes are required for your code to run on either version of Windows. In fact, the resulting EXE/DLL file produced by the compiler will run on 32-bit Windows as well as the x64 and IA64 versions of 64-bit Windows! In other words, the one file will run on any machine that has a version of the .NET Framework installed on it.
On extremely rare occasions, developers want to write code that works only on a specific version of Windows. Developers might do this when using unsafe code or when interoperating with unmanaged code that is targeted to a specific CPU architecture.
What is Unsafe Code?
By default Microsoft’s C# compiler produces safe code. Safe code is code that is verifiably safe. However it is possible to write unsafe code. Unsafe code is allowed to work directly with memory addresses and can manipulate bytes at these addresses. This is a powerful feature and is useful when interoperating with unmanaged code.
How to check if an assembly is type safe or not?
Microsoft supplies a utility called PEVerify.exe that analyses and reports the error if any unsafe code is used in the assembly.
To aid these developers,the C# compiler offers a /platform command-line switch. This switch allows you to specify whether the resulting assembly can run on x86 machines running 32-bit Windows versions only, x64 machines running 64-bit Windows only, or Intel Itanium machines running 64-bit Windows only. If you don’t specify a platform, the default is anycpu, which indicates that the resulting assembly can run on any version of Windows.
Depending on the platform switch, the C# compiler will emit an assembly that contains either a PE32 or PE32+ header, and the compiler will also emit the desired CPU architecture (or agnostic) into the header as well. Microsoft ships two command-line utilities, DumpBin.exe and CorFlags.exe, which you can use to examine the header information emitted in a managed module by the compiler.
How to find Platform dependency?
2.0 = .NET 1.0 or 1.1
2.5 = .NET 2.0
PE header type
PE32 = 32-bit
PE32+ = 64-bit
CorFlags Different flags ILONLY Since assembly also allowed to contain native code, to be “AnyCPU” the assembly shall contain only IL. 32BIT
1 = x86 target
0 = Any CPU target
Signed 1 = Assembly signed
0 = Assembly not signed
In our example we will have to look at three properties to find if the assembly is platform dependent or not.
ILONLY –> 1
Even the assembly has only ILCode still it can be platform dependent. PE and 32 Bit properties help to get more information.
PE & 32 Bit –> PE32 & 0
The combination of PE & 32 bit for different platforms are as follows:
PE 32Bit AnyCPU PE32 0 X86 PE32 1 x64 PE32(+) 0
So from the information displayed by CorFlags, our test assembly is truly “AnyCPU”
Also CorFlags can be used to forcefully change the PE headers. But personally I don’t like this because if it is compiled like that then it is done for a reason.
What happens during running of the executable?
1. Windows examines the EXE file’s header to determine whether the application requires a 32 bit or 64 bit address space
2. A file with PE32 header can run with a 32 bit or 64 bit address space
3. A file with PE32+ header requires a 64 bit address space
4. Windows also checks the CPU architecture information embedded inside the header to ensure that it matches the CPU type in computer.
64 bit versions of windows offer a technology that allows 32 bit windows applications to run. This technology is called WOW64 (for windows on Windows 64).It would be more appropriate if it was named as ‘Windows 32 on Windows 64”. To make it simple, WOW64 acts as a layer and let 32 bit process to run as if they are running in 32 bit system even though in real they are running in 64 bit OS. As you see there is an extra cost here. There is no free lunch.
The even allows 32 bit applications with x86 native code in them to run on an Itanium machine, because WOW technology can emulate X86 instruction set, but with a performance cost.
How do we know if the application is running under WOW64?
In the task manager if you see *32 near to your image name then your application is running under 32 bit emulation mode using WOW64. If you want to determine it from out of process use IsWow64Process.
Also you could use Module.GetPEKind to determine the platform targeted by the module.
|Resulting managed module||X86 Windows||X64 Windows||IA64 Windows|
|anycpu||PE32/platform agnostic||Runs as a 32 bit application||Runs as 64 bit application||Runs as a 64 bit applications|
|X86||PE32/X86||Runs as a 32 bit application||Runs as a WoW64 application||Runs as a WoW64 application|
|X64||PE32+/X64||Doesn’t run||Runs as a 64 bit application||Doesn’t run|
|Itanium||PE32+/Itanium||Doesn’t run||Doesn’t run||Runs as a 64 bit application|
Where to set the /platform switch in VS
Process of creating a Process
After Windows has examined the EXE file’s header to determine whether to create a 32-bit process, a 64-bit process, or a WoW64 process, Windows loads the x86, x64, or IA64 version of MSCorEE.dll into the process’s address space. On an x86 version of Windows, the x86 version of MSCorEE.dll can be found in the C:\Windows\System32 directory. On an x64 or IA64 version of Windows, the x86 version of MSCorEE.dll can be found in the C:\Windows\SysWow64 directory, whereas the 64-bit version (x64 or IA64) can be found in the C:\Windows\System32 directory (for backward compatibility reasons). Then, the process’ primary thread calls a method defined inside MSCorEE.dll. This method initializes the CLR, loads the EXE assembly, and then calls its entry point method (Main). At this point, the managed application is up and running.
1. If your application has only managed code then you don’t hesitate to use “Any CPU”. Best option if you are just using pure c#.
2. If your application (or any of the third party dlls) has direct native code or have an assembly that is targeted to x86 (32) then you cannot run your application in X64 environment because when the dependent assembly (targeted to x86) could not be loaded in x64 environment (will throw bad format exception). In this case, use CorFlags tool to change the target to x64, if you are so sure that can be done!. Otherwise there is a hack.
3. If any of your code targets specifically x86 platform then you must compile targeting the platform x86. This will run in x64 in emulated mode (WoW64) but bear in mind that you have a performance hit in WoW64.
4. If any of your code targets specifically x64 platform (this wont occur normally until you have specific pointer sizes) then you got no choice except to compile your application targeting X64.