The Microsoft designed a new executable file format for .NET programs. A PE format executable program up to then had to be built for a particular processor type. This has advantages and major disadvantages. The advantage is that is ready to run straight away on the chosen processor for which it can be heavily optimized. The disadvantages include the fact that it can not possibly take advantage of new processors and most developers will produce an executable targeted at the lowest common denominator processor type - probably an Intel 80486 so that the program will run on the largest range of possible PCs. This has held back development of processors, there is little point in augmenting the instruction set of the processor if applications do not use them. Also, archaic instructions have to be kept in order to support these 'old' applications.
The only real answer to this issue is to separate the program from the processor at an intermediary level. A program is no longer produced for a specific processor instruction machine code. Instead it is generates an 'abstract' form that any current or future processor type can understand. Using this approach Windows and Windows applications no longer need to be wedded to Intel processors, if the whole of Windows is converted to .NET then it could run on 'anything'.
This is not a new idea, from the 1960s onward there was a battle between 'interpreters' and 'compilers'. An interpreter took a source program step by step and as it read it executed the native machine code in turn. A compiler on the other-hand generates a specific instruction set for one processor type of the whole program in one go. An interpreter is by its nature a lot slower than running compiled code. The concept that solves the efficiency problem is the ability to compile a program in two stages. The first stage is done by the software publishers, it is compiled into an intermediary language. The second stage is done by Windows when the .NET program is installed or first run, the intermediary language is compiled into the native program form.
Another issue facing Windows in the short term is the move to 64bit architecture from 32bit. Just like the move from 16bit to 32bits the aim is for a gradual transition. The 64bit processors will run 'all' 32 bit applications quite happily but in time all software should move to 64bit. Once again .NET can help with this as it can produce 32 or 64bit versions of the same intermediary code as it is effectively just another processor type.
The downside? Well until PCs have installed support for running .NET programs they can not run .NET applications. The download size and time taken to install the .NET infrastructure is not trivial and this will put off many 'small' software developers from using it. Also .NET programs are larger as they need to contain all the information needed to be compiled later on. With increased broadband and with the increased uptake of Windows 7,8 and 10 with .NET support pre-installed this will become less of an issue.
.NET and PE
Rather surprisingly .NET still uses the PE file format for executable files. Because they do not contain native executable code the PE file is just used to describe the program. It has a single import dependency, to the DLL mscoree.dll to the function _CorExeMain. It also contains a single Version resource and a new section type - a CLR header. The CLR header defines the usage that the .NET program makes of the Common Language Runtime (CLR). CLR is another innovation that should reduce the effects of DLL hell. A program declares the library functions it needs in full detail - the type and number of all parameters enabling it to link to the appropriate function with less scope for error. As its name suggests it is a library of all sorts of useful functions for programs to use. Rather than being just a function name all the names are built into a namespace hierarchy limiting the scope for confusion.
Viewing .NET Programs with InspectExe
InspectExe lets you display the main CLR header for a .NET program :