© Roger Villela 2020
R. VillelaPro .NET 5 Custom Librarieshttps://doi.org/10.1007/978-1-4842-6391-4_1

1. .NET Platform

Roger Villela1  
(1)
Sao Paulo, São Paulo, Brazil
 

This chapter provides an overview of .NET 5 (previously .NET Core) and describes the fundamental architectural and the engineering features that you should expect in any implementation of .NET 5 (regardless of hardware, operating system, or execution system).

Acronyms

The following acronyms are introduced in this chapter:
  • Base Class Library (BCL)

  • Common Intermediate Language (CIL)

  • Common Language Infrastructure (CLI)

  • Common Language Runtime (CLR)

  • Common Type System (CTS)

  • Framework Class Library (FCL) (Although not specific to the .NET Framework implementation, the term is used for the full range of .NET types available in an official distribution of .NET.)

  • Intermediate Language (IL)

  • Microsoft Intermediate Language (MSIL)

  • Virtual Execution System (VES)

  • Windows Presentation Foundation (WPF) (a.k.a. execution engine)

ECMA-335 and .NET

ECMA-335

The ECMA-335 standard specification defines the Common Language Infrastructure (CLI) , which includes a set of conceptual definitions and rules to be followed and engineering mechanisms to be implemented, independent of the target operating system and hardware platforms. The CLI ensures that applications, components, and libraries can be written in multiple high-level languages and can be executed in different target system environments without needing to be rewritten.

We can access the ECMA-335 specification at www.ecma-international.org/publications/standards/Ecma-335.htm. Figure 1-1 shows an excerpt. The download link is www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf, and the XML specification download link is www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.zip.
../images/499927_1_En_1_Chapter/499927_1_En_1_Fig1_HTML.jpg
Figure 1-1

Excerpt of web page with information about the ECMA-335 standard specification

More objectively, the CLI is an open specification that describes executable code and an execution environment that enables multiple high-level languages to be used on different architectural platforms without being rewritten.

This execution environment must follow the architectural infrastructure described by the following:
  • Base Class Library (BCL): Foundational library defined by and part of the CLI standard specification. It is implemented by .NET Framework, .NET Core, .NET 5, and .NET 6 (early stages, available on Github.com), and is the main reason for the existence of the .NET standard.

  • Common Language Specification (CLS): Rules (restrictions and models) required for language interoperability. The detailed information on the CLS group is a subset of what is in the CTS, but the content is primarily for language designers and class library designers (frameworks). So, learning about CTS will offer a great base of knowledge for you and your team for when we start working with the rules in the CLS.

  • Common Type System (CTS): The CTS is a set of data types and operations that are shared by all languages that support the CTS, and learning about the CTS will offer a great base of knowledge to you and your team when we start working with the rules in the CLS.

  • Metadata: The metadata describes the program structure, enabling languages and tools to work together. Detailed understanding of the metadata group is not a requisite for a component developer or application developer. Instead, detailed information about such is primarily for tool builders and compiler writers.

  • Virtual Execution Engine (VES): How code is executed (and how types are instantiated), interacts, and dies. More abstractly, it is also known as an execution engine or execution environment. This execution system is responsible for loading, instantiating, executing, and ensuring the cohesiveness of the interactions between the instances. In brief, it offers entire lifecycle support for the instance of the types. The execution engine understands concepts, architecture, and implementation details of two fundamental areas of the platform: the CTS and the VES.

  • Semantics:
    • Capability to recognize contextuality (semantics), meaning mechanisms to constantly observe your own environment and ways to guarantee advanced security rules, data integrity (acting based on more flexible or disciplined rules), dynamic extensibility and expandability. In addition, we have the capability to interact with highly specialized environments (advanced data management systems, for example), development software environment systems (for instance, Microsoft Visual Studio), different target operating systems and hardware platforms (for example, the Microsoft Windows operating system implementations and UNIX-based operating system implementations, including Linux distributions, Apple MacOS, Apple iOS, Google Android, FreeBSD, IBM AIX, Red Hat Linux, Intel x86/Intel x64, ARM 32-bit, ARM 64-bit, IoT high-specialized environment for embedded systems, web development, desktop development, mobile development, game development, artificial intelligence development, machine-learning development, quantum computing environments, supercomputing highly specialized environments, scientific highly specialized research and development environments, research and development for enterprise and government at any level of complexity [local to global], and many more).

    • Capable of hosting, and be hosted by, other environments (such as Microsoft SQL Server advanced data management system, Microsoft Visual Studio 2017, Microsoft Visual Studio 2019, and the Microsoft Azure set of advanced cloud products and services).

The CLI standard specification also includes an intermediate assembly language , and it is the Common Intermediate Language (CIL). Here is a necessary distinction:
  • Intermediate Language: An IL is an abstract language used by a compiler as a step between program code and assembly code.

  • CIL: The CIL is a formal instruction set to the CIL described in the CLI standard specification.

  • Microsoft Intermediate Language (MSIL) : MSIL is Microsoft’s implementation of the formal instruction set based on the ECMA-335 CIL described in the CLI standard specification.

When writing code using a programming language that adheres to the CLI standard specification, the result of the compiled code is a sequence of instructions of the CIL instruction set, as examples show in Listing 1-1 and Listing 1-2.

Open the sample solution RVJ.Core.sln at <install_dir_on_your_local_computer>SourcesAPIsDotNET5.0ProCustomLibsCh01RVJ.Core.

In the first sample project (Buffers_Console_Client), we have in the Program.cs C# file a .NET custom data type named Program derived from System.Object, the .NET root data type for every kind of .NET concrete or abstract class or struct data type, directly or indirectly, as shown in Figure 1-2, Figure 1-3, and Figure 1-4, respectively.
../images/499927_1_En_1_Chapter/499927_1_En_1_Fig2_HTML.jpg
Figure 1-2

Every .NET data type inherits, directly or indirectly, from the System.Object root data type. In .NET, we have a reference type and a value type

../images/499927_1_En_1_Chapter/499927_1_En_1_Fig3_HTML.jpg
Figure 1-3

Some .NET types (for example, System.Array) are abstract data types and are implemented partially by code generation of the compiler (for example, a C# compiler)

../images/499927_1_En_1_Chapter/499927_1_En_1_Fig4_HTML.jpg
Figure 1-4

System.ValueType is a reference type, and System.Int32 is a value type derived from System.ValueType, which inherits from the System.Object reference type

In the C# programming language, because C# treats System.Object as the base class, we do not need to use the System.Object root data type explicitly when we do not have another class as the base data type.

In fact, the execution environment of the CLR (the VES) assumes this; therefore, most programming languages do not require that System.Object be informed explicitly in this scenario. However, it is good programming practice to explicitly use the base data type in such cases. Otherwise, this can become error prone when using more than one programming language in a project, potentially resulting in erroneous perceptions about the inheritance model supported by the .NET execution environment and the transfer of the desired feature to the programming language and the adoption of different programming languages (all because of basic resources of syntax).
#region Namespaces
using System;
#endregion
namespace ConsoleClient {
    public static class Program : System.Object {
         public static void Main() {
            return;
        }
    };
};
Listing 1-1

Typical Source Code in the C# Programming Language for a Console Application with an Entry-Point Member Method Called Program.Main()

.class public abstract auto ansi sealed beforefieldinit ConsoleClient.Program
       extends [System.Runtime]System.Object {
.method public hidebysig static void  Main() cil managed {
  .entrypoint
  // Code size       1 (0x1)
  .maxstack  8
   ret
} // end of method Program::Main
} // end of class ConsoleClient.Program
Listing 1-2

Source Code in MSIL Generated in the Binary File, .EXE, or .DLL

These instructions are not for real hardware or processors. Instead, the CLI standard specification describes a virtual environment that includes some characteristics and functionalities of the elements available in a real computer.

.NET Platform

Microsoft .NET is the official commercial name for the group of technologies and tools designed and implemented based on what is in the ECMA-335 standard specification.

Common Language Runtime, as the name suggests, is an implementation based on the CLI standard specification, and an implementation of the CLR has a set of elements for a fundamental architectural model. Each element has a fundamental set of conceptual definitions and rules to be followed, and engineering mechanisms to be implemented, independently of the target operating system and hardware platforms.

When we are implementing a CLR environment and technologies of a .NET platform, we are creating software elements for a platform that is a nonspecific hardware-based computer (more specifically, a software-only computer, and more commonly known as a virtual computer ). This description includes when planning and implementing custom data types, custom components, custom controls, custom libraries, and specialized tools and frameworks.

For this text, we are using a .NET 5 implementation of the CLR for the sample projects and respective source code.

You can check for the most up-to-date versions of .NET 5 at the official Microsoft website:

https://dotnet.microsoft.com/download/dotnet/5.0.

You can also use GitHub to access the source code of the runtime, libraries, and frameworks made using the CLR components and technologies and BCL fundamental library, as we have with Windows Forms (Microsoft Windows only), Windows Presentation Foundation (Microsoft Windows only), ASP.NET Core (Windows, Apple MacOS, Linux), and the .NET SDK Installer:

Independently or together, these abstract aspects focus on management of data types. So, reasonably, that form of environment and its components is known as a managed environment .

As mentioned previously, in this book we use a .NET 5 implementation of the CLR for the sample projects and respective source code. So, whenever you see CLR mentioned, this means the .NET 5 running on Microsoft Windows 10 2004 and Microsoft Visual Studio 2019 16.7.3 or more recent (Community, Professional, Enterprise). The following programming languages are used in this book:
  • C#

  • MSIL

For example, when we are developing some application and choose the System.String reference type, we are using one of the fundamental types available through the BCL.

However, the string reference type exists only because the CTS has the string fundamental built-in type defined on it, which is one of the platform-specific fundamental built-in types upon which string operations are built. In fact, the string content (value of) in any instance is made up of a sequence of values of the CTS char platform fundamental built-in type, which is System.Char fundamental data type in the BCL. These platform fundamental built-in types, BCL fundamental types, and any other types derived or based on them follow the rules described by the unified type system.

In the CLI specification, this unified type system is the CTS, which describes rules about conceptual, structural, and behavioral elements that must be followed by the CLI itself and specialized tools (such as compilers and runtime environments).

You’ll learn more information about these types in Chapter 2 and in discussions about the CLR throughout this book. For now, though, Table 1-1 shows the types defined by the CTS and described by the metadata.
Table 1-1

Fundamental Types Defined Through CTS

BCL Types

CTS Types

C#

CIL/MSIL

System.Boolean

bool

System.Char

char

System.Object

object

System.String

string

System.Single

float32

System.Double

float64

System.SByte

int8

System.Int16

int16

System.Int32

int32

System.Int64

int64

System.IntPtr

native int

System.UIntPtr

native unsigned int

System.TypedReference

typedref

System.Byte

unsigned uint8

System.UInt16

unsigned uint16

System.UInt32

unsigned uint32

System.UInt64

unsigned uint64

Table 1-2

Fundamental Data Types

Numeric Data Type

Description

Byte unsigned integer

All bits used to represent the value.

Values range from 0 to 255.

(2^8-1)

Word unsigned integer

All bits used to represent the value.

Values range from 0 to 65,535.

(2^16-1)

Doubleword unsigned integer

All bits used to represent the value.

Values range from 0 to 4,294,967,295.

(2^32-1)

Quadword unsigned integer

All bits used to represent the value.

Values range from 0 to 18,446,744,073,709,551,615.

(2^64-1)

Byte signed integer

The first 7 bits (6…0) used to represent the value, the most significant bit (MSB) used as the signed bit.

When the MSB has value 0, the number is positive. When the MSB has value 1, the number is negative.

Values range from -128 to +127.

Word signed integer

The first 15 bits (14…0) used to represent the value, he MSB used as the signed bit .

When the MSB has value 0, the number is positive. When the MSB has value 1, the number is negative.

Values range from -32,768 to +32,767.

Doubleword signed integer

The first 31 bits (30…0) used to represent the value, the MSB used as the signed bit.

When the MSB has value 0, the number is positive. When the MSB has value 1, the number is negative.

Values range from -2^31 to +2^31-1.

Quadword signed integer

The first 63 bits (62…0) used to represent the value, the MSB used as the signed bit

When the MSB has value 0, the number is positive. When the MSB has value 1, the number is negative.

Values range from -2^63 to +2^63-1.

Table 1-3

CTS System.Object (Root Managed Object Type)

BCL Types

CTS Types

C++/CLI projection

C# programming language

CIL

System::Object^ (same root managed object type)

C# object is the keyword used for CTS/BCL System.Object (same root managed object type)

object (same root managed object type)

Table 1-4

Contextual Resources and Their Fundamental Purposes

Your .NET specialized applications

Applications, services, components, libraries, and frameworks.

.NET

Software development kit (SDK, a specialized tools for software development, analysis, deployment, and some types of management)

Specialized components, libraries, and frameworks

CLR

Implementation of a specialized managed environment based of CLI specification

Uses the resources of the underlying hardware and operating system platform (for example, Microsoft Windows operating system)

Adaptable and capable of using the specialized resources of the underlying hardware and operating system (for example, Microsoft Windows 10, Microsoft Windows Server 2016, Linux distributions, Apple iOS, and Apple MacOS.

Remember that this is not a one-to-one mapping between reserved words, data structures, specialized resources, or anything else in the programming languages. That is, what is formalized through the instructions in CIL, what is defined in the CLI specification, and what is implemented by the mechanisms on the platform is what prevails.

As a reminder, unmanaged code means executable and nonexecutable code that is not in CIL and is not under management and the rules of the CLR environment. Erroneously, the unmanaged code is often considered synonymous with native code; this is incorrect. For example, the CIL instruction set includes the following attributes:
  • cil is a code implementation attribute that specifies that the method declaration and implementation consist only of CIL code (that is, managed code).

  • native is a code implementation attribute that specifies that the method declaration and implementation consist only of native code (that is, native instructions of a specific hardware/processor platform). Currently, this functionality of the managed environment CLR implementation is used specifically as one of the base technologies of Platform Invoke (P/Invoke). P/Invoke is one of the mechanisms of the platform, and it is described in the CLI specification.

  • runtime is a code implementation attribute that specifies that the implementation of the method be provided automatically by the runtime.

Two more of these attributes are available and can be combined with them:
  • managed is a code implementation attribute that is used with methods for which implementation is written using only CIL code.

  • unmanaged is a code implementation attribute that is used to describes that the implementation is not external. Currently, this code implementation attribute is used by P/Invoke technology, but it is not restricted to just that use.

The following implementation attributes are properly categorized as code implementation attributes:
  • cil

  • native

  • runtime

  • managed

  • unmanaged

When unmanaged code needs to be used from the managed code, the unmanaged code implementation attribute must be applied on the method implementation. In the specific case of the P/Invoke mechanism, the use of the unmanaged code implementation attribute is required.

The pinvokeimpl method attribute is used to indicate that the runtime will switch from a managed state to an unmanaged state when executing the unmanaged code.

Listing 1-3 shows an example of a managed code implementation that uses an unmanaged code implementation of a well-known Windows application programming interface (API) HeapAlloc() function . The method has been applied the unmanaged and native code implementation attributes.

A switch from a managed state to an unmanaged state, and vice-versa, is performed automatically by the P/Invoke.
.method assembly static pinvokeimpl( lasterr stdcall)
        void* modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall)
        HeapAlloc(void* A_0,
                  uint32 modopt([mscorlib]System.Runtime.CompilerServices.IsLong) A_1,
                  uint32 modopt([mscorlib]System.Runtime.CompilerServices.IsLong) A_2) native unmanaged preservesig
{
  .custom instance void [mscorlib]System.Security.SuppressUnmanagedCodeSecurityAttribute::.ctor() = ( 01 00 00 00 )
  // Embedded native code
}
Listing 1-3

Excerpt in MSIL of Unmanaged Code (Using P/Invoke to Call the HeapAlloc() Function of Windows Memory Management, the Windows API)

At this point, we have the following sequence of elements: the CLI standard specification that is composed by and describes the CTS group, the metadata group, the CLS and VES group, and the CLI itself.

About the Common Type System

When working with a sequence of bits, it is necessary to define the organization of these bits to do something useful. So, the data signified by the bit pattern should identify the data type (or a contextualized type based on the data).

The data type must have a purpose and contextually well-defined characteristics. For example, with regard to structural terms, the data type must have the required number of bits as defined and the fundamental operations that the type supports.

A type’s conceptual, structural, and behavioral fundamental characteristics create a model as to what can be done and what cannot be done with any particular type: a type system model. Because the number of types is constantly increasing, a type system model is necessary to enforce rules to ensure that the environment works as designed and expected.

A type system model describes the necessary rules related to each type’s conceptual, structural, and behavioral characteristics.

Fundamental Types and Hardware Platform

For this discussion, we use Intel IA-32/x64 and Intel 64 fundamental built-in data types (or fundamental built-in types), and we use some defined assembly instructions (implemented and supported) that derive the hardware architecture and the contextual interpretation of the bits on the data type.

The fundamental built-in data types are those defined as integral elements of the platform (in this case, the Intel IA-32/x64 and Intel 64 processor hardware architecture). Therefore, these types are integral elements of the hardware architecture and are not defined by an external library or execution environment.

These are the fundamental types:
  • Byte (8 bits) (1 byte)

  • Word (16 bits) (2 bytes)

  • Doubleword (32-bits) (4 bytes)

  • Quadword (64 bits) (8 bytes)

  • Double quadword (128 bits) (16 bytes)

Although these fundamental built-in data types are supported by a common set of assembly instructions (such as MOV) that perform a common set of operations such move data from one place to another, some assembly instructions support additional interpretation of fundamental built-in data types.

The purpose of this additional interpretation is to allow numeric operations to be performed, and within this context these fundamental built-in data types are viewed and manipulated as numeric data types.

The Intel IA-32/x64 and Intel 64 processors recognize two integer types: signed and unsigned.

Assembly instructions such as ADD and SUB can perform operations on both signed integers and unsigned integers, but some assembly instructions can perform operations only with one type.

The Organization of Fundamental Data Types

Here are the bits as a single pattern, without additional rules or interpretation, except for the fundamental requirements of the hardware platform:
  • Byte (8 bits)
    • Bits 7…0

  • Word (16 bits)
    • Bits 15…0

    • Bits 15…8 (high byte)

    • Bits 7…0 (low byte)

  • Doubleword (32 bits)
    • Bits 31…0

    • Bits 31…16 (high word)

    • Bits 15…0 (low word)

  • Quadword (64 bits)
    • Bits 63…0

    • Bits 63…32 (high doubleword)

    • Bits 31…0 (low doubleword)

  • Double quadword (128 bits)
    • Bits 127…0

    • Bits 127…64 (high quadword)

    • Bits 63…0 (low quadword)

Table 1-2 describes the bits in more detail, including information about fundamental hardware requirements and integer types (signed and unsigned).

CTS for Fundamental Types

The CTS supports types that describe values and types that specify contracts (behaviors that the type supports), and the support for these types must be present in an implementation of a CLR. These two types are supported because one of the principles of the CTS is to support object-oriented programming (OOP), procedural, and functional programming languages.

A value is a bit pattern used to represent types such as numbers (for example, integer numbers and float-pointing numbers).

Listing 1-4 shows examples in C# for two variables for instances of the System.UInt32 BCL value type (and not a simple value).
const uint LimitOne = 72; // C# code.
const System.UInt32 LimitTwo = 144; // C# code.
Console.WriteLine( "{0}", LimitTwo.ToString() );
Listing 1-4

C# Examples Declaring Variables Using uint and System.UInt32, the Same Kind of Object (An Instance of the Value Type of System.UInt32 Data Type of BCL)

A value type is not an object type, but it is defined using a class definition (declaration and implementation).

Remember that this way of work is defined by CTS and supported by VES in the CLR. From the perspective of the type system and execution environment, it is necessary that an object be declared, defined, and implemented to work within the CLR.

Table 1-3 describes the fundamental built-in types defined by CTS. As the table shows, the root object type is accessible through the object keyword of the CIL. So that programming languages such as C#, C++/CLI projection, F#, VB.NET, and others can access this root object type of the platform, there is a library of fundamental types that is part of the CLI specification. This foundational library is the BCL.

This root object type is the System.Object reference type. When declaring a variable of the object type (CTS model definition) or System.Object (BCL) reference type using any high-level programming language such as C#, C++/CLI projection, F#, VB.NET, and so on, the compiler generates an intermediate code using the object keyword of the CIL. Table 1-4 summarizes and helps you understand and memorize this sequence in a straightforward way.

Virtual Execution System

The VES provides an environment for running managed code, security boundaries, and memory management.

Two fundamental built-in types (string and array) are used as a starting point in this discussion to explain various aspects of CTS and VES.

These platform built-in fundamental types are present in any kind of software, so they stand as orthogonal elements.

However, the .NET platform also has a special foundational library, also part of the CLI specification, that supplies specialized types necessary to design and implement any kind of software: the BCL.

As we explore the the organization of the BCL, we’ll use the System.Object, System.String, and System.Array reference types as starting points and deconstruct many aspects of their implementation. This discussion will then enable us to explore the interface types implemented by these types in various specialized frameworks (such as Windows Forms, Windows Presentation Foundation [WPF], Universal Windows Platform [UWP] applications, and ASP.NET).

The VES provides direct support for a set of platform-specific built-in fundamental types, defines a hypothetical machine with an associated machine model and state, and provides a set of control flow constructs and an exception-handling model.

To a considerable extent, the purpose of the VES is to provide the support required to execute the MSIL instruction set.

The VES is the system that implements and enforces the CTS model. For example, the VES is responsible for loading and running programs written to CLI.

The VES provides the services needed to execute managed code and data using the metadata to connect separately generated modules together at runtime. The VES is also known as the execution engine.

.NET Module

When we use C++ to write code, the result of the compiled and linked code is a binary file in a specific format. In this case, we are working with PE/COFF (Portable Executable / Common Object File Format), which is used by the Microsoft Windows operating system. When we use C# to write code, or when we use any other programming language or group of extensions that adhere to the CLI specification, the resulting binary file is in the same PE/COFF format. However, that resulting binary file has some data structures changed/included to support the requirements described by CLI specification and aspects of the Microsoft Windows operating system. This is called the CLI PE/COFF module.

Currently, on Microsoft Windows, the CLI PE/COFF module can have .EXE, .DLL, .netmodule, .WinMD, and .UWP extensions created and recognized by the operation system or development tools. In addition, it can have any other extension that can be registered and recognized by the operating system or specialized tools (for software development or not).

In fact, the use of an extension is not required, but it is a good practice and the accepted standard.

If we are using .NET 5 or .NET Core (not the old Windows-only .NET Framework) in a different operating system and on a different hardware platform, the extensions and file formats used are specific to such software and hardware environments. However, the fundamental structural resources defined in CLI as a starting point are the same.

One VES responsibility is to load the CLI PE/COFF modules. Doing so includes verifying some structural rules about the file format and guaranteeing that all information is as expected. The VES uses the metadata information in the CLI PE/COFF modules to verify that the structural aspects are recognized by the rules that it knows as valid, required, or optional. If the structural elements exist and are valid, the next step is to apply the rules based on the nature of the elements and the context of use.

For example, if the element is a managed type, the execution system needs to verify whether it is a value type or a reference type.

If the element is an assembly reference type, one responsibility of this type is to describe various characteristics of the managed module (structural and behavioral), such as the relationships it has with other managed modules and what managed types are in it (and in any other managed module).

.NET Assemblies

People often wonder what a .NET assembly is exactly. Put simply, and as defined and described by the CLI, an assembly is a logical unit for management and deployment of resources designed to work together. In an implementation of CLR, assemblies can be static or dynamic.

Static Assemblies

Static assemblies are those stored in a storage device, such as a typical hard disk. In Microsoft Windows, the file format of each module is the CLI PE/COFF. These assemblies have typical .NET 5 types and other specialized resources (audio/video files, localization support files, images, and custom files created specifically for the application), depending on the purpose of each application. .NET 5 and .NET Core include the following assemblies and modules, for example:
  • Assembly mscorlib
    • Module mscorlib.dll

    • Module System.Runtime.dll

    • Module netstandard.dll

  • Assembly System.Activities (part of Microsoft Windows Workflow Foundation)
    • Module System.Activities.dll

  • Assembly System.Diagnostics.Debug
    • Module System.Diagnostics.Debug.dll

    • Module System.dll

    • Module netstandard.dll

Dynamic Assemblies

Dynamic assemblies are created dynamically at runtime and are created via specialized API calls of .NET 5/Core. These dynamic assemblies are created and executed directly in memory. However, the dynamic assembly can be saved in a storage device, but only after being executed.

In a typical project, though, we have many files—binary files with executable code or binary files with other types of data (for example, images)—that are part of the software. Therefore, the description, verification, and reinforcement of the relations and dependencies among them are made in part by the metadata.

Metadata is partly responsible for making resources available to perform these tasks.

Working with Assemblies and Modules

For a static assembly or a dynamic assembly, the principles prevails, a way of keep the cohesiveness of the types and resources designed to work together. Deployment, Execution and Management. The information stored in the modules and created through assemblies is what helps the runtime environment understand and apply the rules to the relations among the elements.

Let’s use a typical static assembly.

There are four elements:
  • CIL that implements all the types and required logic to the module

  • Metadata

  • The resources (audio/video files, localization support files, images and custom files created specifically for the application)

  • The assembly manifest

From the perspective of the runtime environment and basic structural rules described in the CLI, of these four elements, only the assembly manifest is a required item. However, considering even the simplest application or component, if we do not have the other elements, the application or component does not have a practical use (except for learning about the assemblies and modules, which I consider a quite practical use).

Organization of Elements in a Module (Physical File)

We start with a basic example here and continue with more details in Chapter 2.

Follow these steps:
  1. 1.

    Using the code editor of your preference, create a simple file and save it with the name RVJ.ProDotNETCustomLibs.il in the directory of your choice that can be used to build source code.

     
  2. 2.

    Open (as administrator) one of the developer command prompts installed and configured by Microsoft Visual Studio 2019.

     
  3. 3.

    Copy the following sequence of MSIL code into the file RVJ.ProDotNETCustomLibs.il and save the file:

     
.assembly extern System.Runtime {
  .ver 5:0:0:0
}
.assembly RVJ.ProDotNETCustomLibs.Buffers {
.ver 1:0:0:0
}
  1. 4.

    In the developer command prompt, write the following command:

     
ilasm /DLL /Output=RVJ.ProDotNETCustomLibs.dll RVJ.ProDotNETCustomLibs.il

If the code compiles without error, the output will be a binary file with the name RVJ.ProDotNETCustomLibs.dll.

By following these steps, we have created a single-file static assembly, with only the assembly manifest.

Using the ILDASM Tool

With the code compiled and the binary generated, we now can use the Intermediate Language Disassembler (ILDASM) tool. (ISLASM , in contrast, stands for Intermediate Language Assembler.) On the same command prompt that we used to compile the code, write the following command:
ildasm RVJ.ProDotNETCustomLibs.dll
With the module RVJ.ProDotNETCustomLibs.dll loaded by the ILDasm.exe tool, we see the screen shown in Figure 1-5.
../images/499927_1_En_1_Chapter/499927_1_En_1_Fig5_HTML.jpg
Figure 1-5

ILDASM showing a single-file static assembly

Now double-click in the manifest. A new window will open with information about the assembly manifest, as shown in Figure 1-6.
../images/499927_1_En_1_Chapter/499927_1_En_1_Fig6_HTML.jpg
Figure 1-6

ILDASM showing the assembly manifest of a single-file static assembly

Implementing the entrypoint Method

We have created a single-file static assembly, with only the assembly manifest. If we want to create an .EXE, we need to change the source code. Using the same RVJ.ProDotNETCustomLibs.il, update the source code to include a managed method that is the entry point:
.assembly extern System.Runtime {
  .ver 5:0:0:0
}
.assembly RVJ.ProDotNETCustomLibs.Buffers {
.ver 1:0:0:0
}
.method static public void MyEntryPointMethod() cil managed {
.entrypoint
    ret
}

As you can see, the name of the .entrypoint method does not need to be main.

To build this code, use the following command:
ilasm /Output=RVJ.ProDotNETCustomLibs.exe RVJ.ProDotNETCustomLibs.il
After the code compiles without error and with the binary generated, we can use the ILDasm.exe tool to load the module RVJ.ProDotNETCustomLibs.exe, and then we have more than just the assembly manifest, as shown in Figure 1-7.
../images/499927_1_En_1_Chapter/499927_1_En_1_Fig7_HTML.jpg
Figure 1-7

ILDASM showing a single-file static assembly

As shown in Figure 1-8, we have created a single-file static assembly, with the assembly manifest and one method (in this case, the entry-point method). When RVJ.DotNETProCustomLibs.exe runs, it runs like any other .NET managed executable.
../images/499927_1_En_1_Chapter/499927_1_En_1_Fig8_HTML.jpg
Figure 1-8

ILDASM showing a single-file static assembly, with the assembly manifest and one managed method

Listing 1-5 shows an example of managed instructions from one of the sample projects that comes with the companion content of this book. The .module directive indicates the name of the binary module (in this case, RVJ.ProDotNETCustomLibs.exe). The .assembly directive describes which assemblies make this a logical unit of management and deployment of resources designed to work together. The .assembly RVJ.ProDotNETCustomLibs.Buffers (without the extern keyword) describes that this assembly is in the current module. The use of .assembly extern directive describes to the assembly the types that your .assembly or .module are referencing. For example, .assembly extern System.Runtime indicates that the assembly RVJ.ProDotNETCustomLibs.Buffers is using one or more types of the assembly mscorlib. The highlighted CIL instructions are the same that you can read in the RVJ.ProDotNETCustomLibs.dll or RVJ.ProDotNETCustomLibs.exe modules. Chapter 2 discusses these and other instructions in more detail (with even fuller detail following in subsequent chapters).
.assembly extern System.Runtime {
  .ver 5:0:0:0
}
.assembly RVJ.ProDotNETCustomLibs.Buffers
{
  .ver 1:0:0:0
}
.module RVJ.ProDotNETCustomLibs.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    // ILONLY
.method public static void  MyEntryPointMethod() cil managed {
  .entrypoint
  // Code size       1 (0x1)
  .maxstack  8
  IL_0000:  ret
} // end of method 'Global Functions'::MyEntryPointMethod
Listing 1-5

Fundamental Keywords Used by Static Assemblies or Dynamic Assemblies

As you can see, the VES handles a lot of work. Even still, though, there are more interesting functionalities within this mechanism.

Chapter 2 discusses these resources and goes into more detail about the CTS and VES. Specifically, you’ll read more about fundamental built-in types and about how the execution environment deals with these types and structural elements of the platform. Initially, we use code written directly in CIL to provide more information about the use of the types and so that you better understand how to work with modules and assemblies. We then use some code in C++ to highlight some internal aspects of the execution environment and some special types. From that point, we embark on our journey through foundational BCL using the MSIL and C# programming languages.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.202.54