The .NET Framework is a computing platform that has been designed by Microsoft to simplify the development of modern applications, such as:
Applications that use sophisticated GUI front ends
Applications that use the Internet
Applications that are distributed over more than one computer
Applications that make sophisticated use of databases
There are two main components to the .NET Framework: the common language runtime (CLR) and the .NET Framework class library. We’ll examine both of these in this chapter.
You’ve already met the common language runtime, because this is the part of .NET that provides the “management” in the Managed Extensions for C++. The CLR is a run-time execution engine that is responsible for executing code within the .NET environment, providing services such as security, memory management, and remoting (communication between objects in different domains, processes, or computers). Code that is run by the CLR is known as managed code; code that executes outside the control of the CLR is unmanaged code. All Microsoft Visual Basic and C# code is managed, but it’s possible to write both managed and unmanaged code in Microsoft Visual C++ and to have both types of code working together in the same program.
All .NET languages compile down into an intermediate form called intermediate language, or IL. It’s sometimes also known as MSIL, for Microsoft intermediate language.
IL is similar to Java bytecode, in that it’s an intermediate form of code produced by the compiler that can’t be directly executed on a target system. IL code is also portable. In contrast to Java, however, IL code is always converted into native code before it’s executed, which is done by a Just-In-Time (JIT) compiler. This conversion might happen on demand, function-by-function as a program executes, or all at once when a program is installed.
One of the great innovations of IL is that it isn’t simply a low-level, machine- independent object code. In fact, support for object-oriented functionality— such as the ideas of classes, encapsulation and data hiding, polymorphism, and inheritance—is built into IL, so you can view it as a type of object-oriented assembler language. This functionality makes it far more powerful than Java bytecode, and it allows you to perform cross-language object-oriented programming, easily calling members in managed C++ classes from Visual Basic and vice-versa, and even inheriting from a managed C++ class in Visual Basic.
If you’re interested in seeing what IL looks like, you can use the IL Disassembler tool, ILDASM, to open a .NET executable and show you the code in IL. There’s an example of how to do so in the “Metadata” section later in the chapter.
The Common Type System (CTS) provides a specification for how types are defined, managed, and used, which is an important part of the .NET cross- language integration. The CTS provides a set of rules that languages must obey, which helps to ensure that types created in different languages can interoperate with one another.
The Common Language Specification (CLS) is a set of rules and constraints that compiler and library writers need to follow to be sure that the languages and code they produce will interoperate with other .NET languages. The CLS forms a subset of the CTS, and if a language or a library is CLS-compliant, it will completely interoperate with other CLS-compliant languages.
You’ll see in the online documentation that some .NET member functions are marked as not CLS-compliant, which means that they might not be accessible from some .NET languages. For example, functions that use unsigned integers are not CLS-compliant because unsigned integers aren’t supported by Visual Basic. As a result, unsigned integers are not included in the types specified by the CLS.
The .NET Framework class library is an object-oriented library of classes that provides all the tools you need to write a wide variety of programs.
Over the years since Windows was first released, programmers have written Windows applications using the Windows API (application programming interface). This API gives you a large number—several thousand—of C functions that you can call from your programs to interact with Windows. However, there are two main problems with the Windows API: first, it isn’t object-oriented, and second, it’s a C library, so it can’t easily be used from every language.
One of the benefits of object-oriented programming is the help that it gives in structuring and managing large-scale projects. The Windows API has grown to several thousand functions, and it gets harder and harder to manage such a large collection of unstructured routines. In addition to its other benefits, such as encapsulation and polymorphism, object-oriented programming lets you impose a structure onto code. So, for example, a Dialog class can contain all the functions relating to dialog boxes. This ability makes it much easier to use a library the size of the Windows API.
The second problem with the Windows API is that it’s basically written for C programmers, so it uses many features that are unique to C, such as pointers and null-terminated strings, which makes it hard—and sometimes impossible— to use some functionality from languages other than C or C++. You also tend to need a lot of ugly “plumbing” to interface between languages such as Visual Basic and the API.
The .NET Framework class library provides a set of classes that can be used from any .NET language because it works at the IL level. All .NET languages compile down to the same intermediate code, and because they all use references and agree on the basic set of value types, they can all use the classes defined in the class library. This is a huge step forward and provides language interoperability on a scale never seen before. In the past, compiler vendors who supported more than one language—such as TopSpeed or Salford—have added features to their compilers to make it simple to mix their languages in multilanguage applications, but no one has produced a language-independent programming framework on the scale of the .NET Framework before.
The first release of the .NET Framework is on the Windows platform, but it has been designed so that it can be ported to other platforms in the future.
Assemblies are the basic building blocks from which .NET applications are constructed, and they’re the fundamental unit of deployment and versioning. Assemblies contain IL code, metadata that describes the assembly and its contents, and any other files needed for run-time operation. An assembly is therefore much more self-contained than a standard Windows executable or COM object because there is no reliance on external sources of information such as the Windows Registry. Every .NET type is part of an assembly, and no .NET type can exist outside an assembly.
There are several ways in which assemblies are fundamental to the .NET world:
Versioning The assembly is the smallest unit to which versioning is applied, and the assembly manifest describes the assembly’s version together with the versions of any assemblies on which it depends. This information means that it’s possible to check that components with the wrong version information aren’t being used at run time.
Deployment Assemblies are loaded only as needed, which makes them highly suitable for distributed applications.
Type A type’s identity includes the assembly in which it lives. Two types with the same name living in two different assemblies are considered to be two completely different types.
Security The boundary between assemblies is where security
permissions are checked.
.NET classes are self-describing in that they carry descriptive information with them in the EXE or DLL. This information, called metadata, includes the following information:
The name, version, and culture-specific information (such as the language and calendar used) for the assembly
The types that are exported by the assembly
Other assemblies that this one depends on
Security permissions needed to run
Information for each type in the assembly: name, visibility, base class, interfaces implemented, and details of members
Additional attribute information
Most of the metadata is standard and is created by the compiler when it produces the IL code, but you can use attributes to add extra metadata information.
The following exercise shows you how to modify the standard metadata produced by the compiler.
Create a Visual C++ Console Application (.NET) project named Meta1. Open Solution Explorer and look at the Source Files folder, and you’ll see that the project contains three C++ source files. Meta1.cpp is the code for the application, AssemblyInfo.cpp contains definitions of the standard metadata items that you can modify, and StdAfx.cpp is there to include the StdAfx.h header file.
Open AssemblyInfo.cpp. You’ll see that the file contains a number of lines that look like the following:
[assembly:AssemblyTitleAttribute("")]; [assembly:AssemblyDescriptionAttribute("")]; [assembly:AssemblyConfigurationAttribute("")]; [assembly:AssemblyCompanyAttribute("")]; [assembly:AssemblyProductAttribute("")]; [assembly:AssemblyCopyrightAttribute("")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")];
Metadata is added to C++ code by enclosing declarations in square brackets (). The assembly: part at the start means that this is an attribute that applies to an assembly, as opposed to a type within an assembly. There’s a set of standard attributes that you can use to change the metadata compiled into an assembly, and most of them are listed in AssemblyInfo.cpp.
Edit the AssemblyCompanyAttribute line to contain some suitable name, like this:
[assembly:AssemblyCompanyAttribute("Acme Rocket Sled, Inc.")];
Now build the project, which automatically creates the assembly for you. How can you be sure that the metadata in the assembly reflects your change? One way to find out is to use ILDASM. ILDASM is part of the .NET SDK, and it’s located in the \Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin directory. You can either start it from there or open a Microsoft Visual Studio .NET Command Prompt using the Programs, Microsoft Visual Studio .NET 2003, Visual Studio .NET Tools, Visual Studio .NET 2003 Command Prompt entry on the Start menu. A console window will open that has the path set to include all the Visual Studio .NET and .NET SDK directories, so you can simply type ildasm to start the program.
When the ILDASM window opens, use the File menu to browse for the Meta1.exe executable and open it. You should see something like the following figure.
Double-click MANIFEST, which will open a separate window displaying the assembly metadata. Scroll down until you find the AssemblyCompanyAttribute line, which should read something like this:
.custom /*0C000005:0A000009*/ instance void [‘mscorlib’/* 23000001 */] ‘System.Reflection’.’AssemblyCompanyAttribute’ /* 0100000D */::.ctor(string) /* 0A000009 */ = ( 01 00 16 41 63 6D 65 20 52 6F 63 6B 65 74 20 53 // ...Acme Rocket S 6C 65 64 2C 20 49 6E 63 2E 00 00 ) // led, Inc...
Although the contents are given in hex, you can see that the metadata does reflect the change you made to the project.