I've always skirted around the issue of building 64-bit desktop apps as I somehow got it in my head that it wasn't a trivial thing to do, plus I guess I didn't really understand the solution "configuration manager" dialog, platforms and configurations, either.
Anyway, I've just tried converting an existing application by simply changing all of the solution projects' platforms to x64, and it worked. The only issue I had was with a C++ DLL referenced by one of the projects, which needed rebuilding as x64.
So my question (finally) is: why did I only have an issue with the C++ DLL, but the many .Net assembly DLLs referenced by the projects in my solution were fine?
And what actually determines whether my application is built as 64-bit? I changed all of the solution's projects to "x64", but would it still have worked if they were left as "Any CPU", and I only changed the WPF (startup) project to "x64"?
Edit
So in the end it seems that I was simply able to set all project platforms to "Any CPU" rather than "x64" (the TFS server wasn't able to run unit tests with the latter). Even the projects referencing the unmanaged 64-bit DLL seem happy with this (not sure why).
The trick was to uncheck the Prefer 32-bit
option in the WPF (startup) project's properties. The built application now runs as a 64-bit app, and TFS/unit tests run happily.
TL;DR: .Net assemblies are never assembled in to machine specific instructions, instead they are compiled into MSIL which runs under the .Net virtual machine, at runtime the VM uses the JITer to produce machine instructions which can be executed by the CPU.
The differences between 32bit
and 64bit
that "breaks" object file are either: Pointer size, Memory model and most importantly the instruction set architecture (ISA). Lets break down each point and see why .Net
assemblies are mostly unaffected.
In a 32bit
execution environment pointers are 32 bit long, and as you'd expect int 64 bit they are 64 bit long. This fact can break your assembly in one of two ways:
Since .Net languages are safe
- you don't get to play with pointers (generally, unless you are under an unsafe context). Not having the option to play with pointers solves the 2nd point. As for the first point - this is the reason you can get the size (using sizeof
) of a class no the size of a struct containing class references.
The old 32bit
processors used to have a feature called Memory segmentation
which allowed the OS
or a Supervisor program
to declare regions of memory that are accessed using special CPU registers
called Segment registers
. In 64bit
this feature is mostly disabled. So programs compile to work under memory segmentation may have problems working in a non segmented environment.
Generally we don't deal with memory at this low level, this is possible because of the .Net virtual machine
as explained in the next point.
32bit
and 64bit
(x86
and AMD64
) are similar but completely different ISAs
which means that code which was assembled to run under one will not run under the other. So unlike the other two points this one will break your assembly regardless of what you've written in it.
So how could .Net assemblies possibly be compiled as Any CPU
? The trick is that when you compile a .Net language you never assemble it. That means when you press compile
you only compile your code into an intermediate object (known as a .Net assembly) that is not assembled (ie. not ran through an assembler).
Usually when you compile code in C/++
you first run it through a compile the generates some assembly instructions
these instructions are then passed down to an assembler
which generates machine instructions
. The CPU is only capable of executing these machine instructions
.
.Net languages are different, as mentioned above, when you compile a .Net language you run it through a compiler and stop there, no assembler involved. The compile here, too, generates some assembler instructions
but unlike the instructions produced by a c/c++
compile these instructions are machine agnostic, they are written in language called MSIL - Microsoft intermediate language
. No CPU knowns how to execute these instructions, since just like ordinary assembler instructions they too must be assembled into machine instructions.
The trick is that this all happens at run time, when a user opens you program he launches an instance of the .Net runtime
in which your program runs, your program never runs directly against the native machine itself. When your program needs to call a method a special component in the .Net virtual machine called the JITer - just in time compiler
is tasked with assembling this method from MSIL to machine instructions specific for the machine the .Net framework was installed on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With