Introduction
From the surface, this question may be as simple as clicking one button to select build for any CPU inside Visual Studio 2012 to 2015 project property=>Build tab. Actually, it is not that simple. After intensive research, I share my understanding here with you.
Besides theoretic results, I integrate all scattered code snippets from the Internet into an easy and useful tool. It can tell an assembly if it is managed, or unmanaged; built for 32-bit or 64-bit platform.
After we build software and are ready to deploy our software to customers machines, we have one question coming up: is customer machine 32-bit Windows, or 64-bit Windows? Should we build our application to target any CPU? I hope this post will help you on this question.
Alternatives can refer to posting [8]-[14].
Disclaimer: I do not write and own these pieces of logic. I just integrate these pieces of code into a useful tool for you to use. These code snippets come from reference [2]-[7]. Particularly, CorFlagsReader.cs is under Apache License.
Visual Studio 2015 Default Build Configuration
When a .NET application is built inside Visual Studio 2012 and up, the default setting is AnyCPU with an additional sub-type of AnyCPU, “Any CPU 32-bit preferred”.[20] It implies the following:
- If the process runs on a 32-bit Windows system, it runs as a 32-bit process. IL is compiled to x86 machine code.
- If the process runs on a 64-bit Windows system, it runs as a 32-bit process. IL is compiled to x86 machine code.
- If the process runs on an ARM Windows system, it runs as a 32-bit process. IL is compiled to ARM machine code.
Another subtle observation is in [21].
Case Study
If a managed application is built for 32-bit OS, then it will run on 32-bit OS. If this application is built for 64-bit OS, and it references 32-bit unmanaged assembly, then it will not work on 64-bit OS.
SQLite ADO.NET installation is a real example for it. If you want to build a C# application to target 64-bit Windows, you need to install SQLite ADO.NET 64-bit DLLs.
Please refer to [23] for more information.
Overview on .NET Application Target Platforms
As we build .NET application in Visual Studio, we need to select target to a specific architecture.
"If you have 100% type safe managed code, then you really can just copy it to the 64-bit platform and run it successfully under the 64-bit CLR."
From [18], we know the following:
Consider a .NET application that is 100% type safe code. In this scenario it is possible to take your .NET executable that you run on your 32-bit machine and move it to the 64-bit system and have it run successfully. Why does this work? Since the assembly is 100% type safe we know that there are no dependencies on native code or COM objects and that there is no 'unsafe' code which means that the application runs entirely under the control of the CLR. The CLR guarantees that while the binary code that is generated as the result of Just-in-time (JIT) compilation will be different between 32-bit and 64-bit, the code that executes will both be semantically the same.
Therefore, if our application is 100% type safe managed code, then we have the following scenarios:
If we select to target x86 architecture, our application will run on any Windows-based computer that supports the targeted .NET framework version. More specifically, it will always run in a 32-bit process, even on a 64-bit operating system.
If we select to target x64 architecture, our application will require a 64-bit process and will not run on 32-bit operating systems.
Both the x86 and x64 options can be useful when we are using third party DLLs, particularly those that use unmanaged code, which require either a 32-bit or a 64-bit process. For example, when a third-party DLL is compiled for x86, a 64-bit process cannot use it.
If we select to target "Any CPU", our application will execute on any supported operating system and the process will use the most appropriate architecture option. Specifically, on 32-bit operating systems, it will run as 32-bit. On 64-bit operating systems, it will execute in a 64-bit process. A possibility comes out that our application may need to behave differently according to its process type. We then need to detect the type of process at run time.
Reference [1] is good to read.
Potential Barriers
If our managed applications have the following circumstances, then they will keep the application targeting to x64 from working well:
- Invoking platform APIs via p/invoke
- Invoking COM objects
- Making use of a mechanism for sharing information
- Using serialization as a way unsafe code (Please note: This is minor in terms of usage.)
- Using marshaling as of persisting state
No matter what our application is doing, we need to know our application source code and its dependent assemblies, is it managed or unmanaged, 32-bit or 64-bit? We can have the following choices:
- Migrate the code with no changes
- Make changes to your code to handle 64-bit pointers correctly
- Work with other vendors, etc., to provide 64-bit versions of their products
- Make changes to your logic to handle marshaling and/or serialization
In my projects, I am only concerned about unsafe code. But from [18], we see that: using unsafe code in your managed application does not mean that migrating to the 64-bit platform will not be possible. Nor does it mean that there will be problems. What it does mean is that you must review all of the unsafe code your managed application has and determine if there will be any issues.
CorFlag Values and their Applications
To know which platform a .NET assembly is built for, we can use CorFlag
Values. Reference[19] is the specification on PE file format. Reference[3] is a useful code to get all CorFlag
values. The following two tables contain the rule to know all related information about the assembly. Most recent information is updated in [25] for .NET framework 4.5.
CorFlag Information
CLR Header | .NET framework it targets for |
PE | PE header type:
PE32 = 32-bit
PE32+ = 64 bit
|
CorFlags | various flags for endian type, ILONLY, etc. |
ILONLY | the file contains IL only (i.e., no unsafe code) |
32BIT | 1=x86 target
0=any CPU
|
Signed | 1=Assembly signed
0=Assembly not signed
|
Rules to Decide Platform
Any CPU | PE32 and 32BIT=0 |
x86 | PE32 and 32BIT=1 |
x64/Itanium(IA-64) | PE32+ and 32BIT=0 |
Refined Rules for .NET framework 4.5.1
CPU Architecture | PE | 32BITREQ | 32BITPREF |
x86(32-bit) | PE32 | 1 | 0 |
x64(64-bit) | PE32+ | 0 | 0 |
any CPU | PE32 | 0 | 0 |
any CPU 32-bit preferred | PE32 | 0 | 1 |
Code Highlights
Thanks to Kirill Osenkov in reference[25], we get this precious details for CorFlags
values:
[Flags]
public enum CorFlags
{
ILOnly = 0x00000001,
Requires32Bit = 0x00000002,
ILLibrary = 0x00000004,
StrongNameSigned = 0x00000008,
NativeEntryPoint = 0x00000010,
TrackDebugData = 0x00010000,
Prefers32Bit = 0x00020000,
}
Based on these details, I add the following properties to CorFlagsReader
class in CorFlagsReader.cs file:
public bool Is32BITREQ
{
get
{
return (corflags & CorFlags.Requires32Bit) == CorFlags.Requires32Bit;
}
}
public bool Is32BITPREF
{
get
{
return (corflags & CorFlags.Prefers32Bit) == CorFlags.Prefers32Bit;
}
}
public bool IsPE32
{
get
{
return (peFormat == PEFormat.PE32);
}
}
public bool IsPE32Plus
{
get
{
return (peFormat == PEFormat.PE32Plus);
}
}
In the case of managed assembly, I code rules as follows in the openAnAssemblyToolStripMenuItem_Click
event:
if(isManagedAssembly)
{
textBox1.Text = fileName +" is a managed assembly.\n";
CorFlagsReader corFlags= CorFlagsReader.ReadAssemblyMetadata(fileName);
if (corFlags.IsPE32 & corFlags.Is32BITREQ & (!corFlags.Is32BITPREF))
{
textBox1.Text += "Target CPU architecture: x86";
}
if (corFlags.IsPE32 & (!corFlags.Is32BITREQ) & (!corFlags.Is32BITPREF))
{
textBox1.Text += "Target CPU architecture: anyCPU";
}
if ((corFlags.IsPE32 & (!corFlags.Is32BITREQ) & corFlags.Is32BITPREF) ||
(corFlags.IsPE32 & corFlags.Is32BITREQ & corFlags.Is32BITPREF))
{
textBox1.Text += "Target CPU architecture: anyCPU 32-bit preferred";
}
if (corFlags.IsPE32Plus & (!corFlags.Is32BITREQ) & (!corFlags.Is32BITPREF))
{
textBox1.Text += "Target CPU archtecture:x64";
}
}
Assembly Checker
This assembly checker can be used to check any assembly type. It has an easy GUI:
Please load any DLL or EXE file into it and let me know if you have any questions.
Source code files are uploaded here. An executable is ready for you to use.
A .NET Assembly Contains Unmanaged Code?
A good discussion in [26] is worthy reading. corflags
tool from Microsoft can tell if it is purely IL or contains mixed unmanaged code, but PEVerify tool can tell if it is 100% type safe.
Feedback
If you have good ideas and thoughts, please give your precious feedback. I hope this tool is useful and saves you more time.
References
- Back to Basics: 32-bit and 64-bit confusion around x86 and x64 and the .NET Framework and CLR
- How to determine whether a DLL is a managed assembly or native (prevent loading a native dll)?
- CorFlagsReader.cs content
- Check if unmanaged DLL is 32-bit or 64-bit?
- How to find if a native DLL file is compiled as x64 or x86?
- How to determine if a .NET assembly was built for x86 or x64?
- How do I determine if a .NET application is 32 or 64 bit?
- How do I tell if my application is running as a 32-bit or 64-bit application?
- 3 Ways to Learn Whether a Windows Program is 64-bit or 32-bit
- How to check if a binary is 32 or 64 bit on Windows?
- Quick way to tell if an installed application is 64-bit or 32-bit
- How to tell if a .exe file is a 32-bit or 64-bit application using dumpbin
- Migrating 32-bit Managed Code to 64-bit
- Determining Which Version of the .NET Framework Is Installed
- How can I tell if my .NET application is running in 64bit on the 64bit server?
- Checking if the Current Process is 64-bit
- x64 Development with .NET
- Migrating 32-bit Managed Code to 64-bit
- Microsoft PE and COFF Specification
- What AnyCPU Really Means As Of .NET 4.5 and Visual Studio 11
- Why does 'Any CPU (prefer 32-bit)' allow me to allocate more memory than x86 under .NET 4.5?
- 32bitness and 64bitness and migrating DasBlog on IIS7 and ASP.NET under Vista64
- SQLite configuration for C# application targeting any CPU - Part 1
- Moving from 32-bit to 64-bit application development on .NET Framework
- How to interpret the CorFlags flags?
- How do I find out if a .NET assembly contains unmanaged code?
History
- 07/05/16: Initialized this article