Licence Metinet - IUT LYON 1
Concepteur et Gestionnaire de Sites Internet

 

Auteur: Christian ALLEMAND

Contenu

Cours. 

3.1 - Définition d’un assembly .NET. 

3.2 - Private assembly ou assembly privé.

3.3 - Shared assembly ou assembly avec nom fort.

3.4 – Le Global Assembly Cache : GAC. 

3.5 - Structure d’une assembly .NET.

3.6 – Les Espaces de noms.

3.7 – Références externes. 

3.7.1 - Création d’une librairie.

3.7.2 - Utiliser une référence externe. 

TP.

VB.NET.

C#.

 

Cours

3.1 - Définition d’un assembly .NET

Un assembly .NET est une collection de types et de ressources qui représente une unité logique de fonctionnalités.

 

Un assembly .NET est un fichier qui contient du code CIL (Common Intermediate Language) et des informations supplémentaires afin de prévenir des erreurs de versions et d’accroitre la sécurité.

Le code CIL contenu dans l’assembly a été compilé par le compilateur VB.NET, C# (ou autre langage supporté par le framework .NET).

Le code CIL contenu dans l’assembly est ensuite compilé par le CLR en code machine exécutable lorsqu’il est chargé en mémoire. C’est pour cela que le compilateur du CLR est aussi appelé compilateur Just In Time ou juste à temps.

L’assembly .NET  est le standard pour les composants développés avec le .NET Microsoft.

 

Les assemblys .NET peuvent être ou non des exécutables, ce sont :

  • Soit des applications (fichiers exécutables) d’extension .exe, aussi appelés PE (Portable Executable)
  • Soit des bibliothèques de types (Dynamic Link Librairies), d’extension .dll

Tous les assemblys contiennent:

  • La définition des types utilisés dans l’assembly
  • Les informations de versions
  • Les métadonnées
  • Le manifeste
  • Le code IL

 

Il existe 2 sortes d’assemblys : private et shared.

3.2 - Private assembly ou assembly privé.

C’est le type par défaut lorsque l’on crée un assembly.

Ce type d’assembly est copié avec chaque assembly appelant dans le répertoire des assemblys à appeler :

  • répertoire \bin dans une application ASP.NET.
  • répertoire \bin\debug ou \bin\release dans une application WinForm.

3.3 - Shared assembly ou assembly avec nom fort.

Ce type d’assembly est copié à un seul endroit (en général dans le Global Assembly Cache, le GAC).

Tous les assemblys d’une même application appelant une même assembly avec nom fort utilisent la même copie de cette assembly depuis son emplacement d’origine.

Ainsi, les assemblys avec nom fort ne sont pas copiés dans chacun des répertoires privés de chaque assembly appelant.

Un assembly avec nom fort possède un nom qualifié complet qui comprend

  • son nom,
  • sa culture,
  • sa clé publique,
  • son numéro de version
  • optionnellement l’architecture de processeur.

MaLibrairie, Version=1.0.500.0, Culture=fr-FR, PublicKeyToken=b77a5c561934e089c

La clé publique et les informations de version rendent donc pratiquement impossible la confusion entre 2 assemblys de même nom ou de même numéro de version.

Un assembly peut contenir dans un seul fichier ou être réparti dans plusieurs fichiers.

Dans ce cas, un seul module maitre contient le manifeste et les autres modules n’en contiennent pas.

3.4 – Le Global Assembly Cache : GAC

Le Global Assembly Cache est un cache de code à l’échelle de l’ordinateur sur lequel est installé le CLR.

Le Global Assembly Cache stocke les assemblys destinés à être partagés entre plusieurs applications sur l'ordinateur.

Vous ne devez partager des assemblys en les installant dans le Global Assembly Cache qu'en cas de nécessité.

En règle générale, vous devez garder les dépendances d'assembly privées et rechercher les assemblys dans le répertoire de l'application, à moins que le partage d'un assembly soit explicitement requis.

On place un assembly dans le GAC :

  • soit par l’utilisation d’un programme d’installation prévu pour le GAC
  • soit par l’utilitaire gacutil.exe fourni avec le SDK de Windows
  • soit par copier/coller avec l’explorateur Windows vers le répertoire du GAC

Les assemblys déployés dans le Global Assembly Cache doivent avoir un nom fort.

Pour lister les assemblys contenus dans le GAC, on peut ouvrir une invite de commande Visual Studio (depuis le menu Démarrer, Visual Studio 2010, Visual Studio Tools) et lancer la commande suivante :

gacutil –l

Cette commande affiche plus de 2500 assemblys placés dans le GAC pour le framework .NET v4.0 !

 

Les principales librairies présentes dans le répertoire du GAC sont optimisées.

En effet, elles ont toutes été compilées en code natif lors de l’installation du framework sur la machine.

Etant donné que le compilateur JIT optimise la compilation en code natif par rapport à la configuration de l’ordinateur sur lequel il est installé, ces librairies sont donc compilées avec une optimisation pour l’ordinateur sur lequel elles ont été installées.

Une même librairie de même version pourra donc présenter, une fois compilée,  un code natif différent d’un ordinateur à un autre.

3.5 - Structure d’une assembly .NET

Soit l’application console Module1.exe générée à partir du code source suivant  vu auparavant:

Public Class Appli1

                Public Sub Main()

                               Console.WriteLine(« Bonjour ! »)

                               Console.Read()

                End Sub

End Class

Compilation depuis VS ou avec ’vbc.exe module1.vb’

Le résultat est un exécutable standard Windows (un Portable Executable ou PE) dont la structure générale est la suivante :

 

 

PE Header

CLR Header

Manifeste

Métadonnées

Code IL

 

Le PE Header contient les informations standard pour Windows et permet à Windows de reconnaitre l’assembly en tant que programme exécutable.

Dans cette section, figure un code qui indique qu’il faut charger la DLL mscoree.dll (MicroSoft Component Object Runtime Execution Engine). Cette DLL va ensuite déterminer quel type et version du CLR elle doit charger.

Le CLR prend ensuite la main.

Il charge les métadonnées dans le CLR Header, recherche le point d’entrée (par exemple la fonction Main() ), compile le code IL de cette fonction avec son compilateur JIT en code natif du CPU et l’exécute.

Pendant la compilation du code, le JIT vérifie le code IL, d’où la notion de code managé.

S’il détecte des opérations non vérifiables telles que accès direct à la mémoire, il refusera d’exécuter l’application.

Le C# permet de créer du code unsafe qui interdit au JIT d’effectuer ces contrôles. VB ne le permet pas.

L’utilitaire PEVerify.exe permet d’effectuer ces vérifications manuellement.

Syntaxe : PEVerify.exe <nom du PE à tester>

 

Le CLR gère une table qui contient des pointeurs vers les fonctions inclues dans l’application.

Au chargement de l’application, le CLR remplit cette table avec des pointeurs vers le code IL de l’application.

Lorsqu’une fonction est appelée pour la première fois, le CLR la compile en code natif, puis remplace le contenu du pointeur correspondant non plus vers le code IL, mais vers le code compilé en code natif.

Lors des appels ultérieurs à cette fonction, le CLR appellera directement la version compilée.

C’est pour cette raison que l’on observe un temps d’exécution plus lent lors du premier appel à une fonction.

 

Il est possible de compiler l’application entière en code natif avec l’utilitaire ngen.exe. Cela permet d’éviter les pertes de vitesse dues à la première compilation de chaque fonction.

Cette opération peut être envisageable si l’application en question n’est utilisée que sur l’ordinateur sur lequel elle est compilée. En effet, comme nous l’avons vu à propos des librairies dans le chapitre du GAC, le CLR optimise le code natif par rapport à l’ordinateur sur lequel s’exécute la compilation.

Le fait de compiler l’application en code natif sur un ordinateur A et de copier cette image sur un ordinateur B annule toute l’optimisation qui pourrait être faite sur l’ordinateur B et peut même entrainer un plantage de l’application.

 

Le Manifeste est  une synthèse des informations contenues dans les métadonnées.

On peut visualiser son contenu grâce à l’outil ILDASM.exe

Le Manifeste indique :

  • La liste des fichiers qui composent l’assembly (1)
  • La liste des autres fichiers nécessaires au fonctionnement de l’assembly (2)
  • La liste des types exportés de l’assembly
  • Le nom, la version et la culture de l’assembly
  • Les informations de nom fort si l’assembly est signé
  • Les informations sur le point d’entrée de l’assembly

 

Pour tout assembly, il n’existe qu’un seul Manifeste.

Pour un assembly composé de plusieurs fichiers, un seul contiendra le manifeste.

 

 

 


 

Les métadonnées contiennent des informations détaillées sur le contenu de l’assembly

Elles indiquent en détail :

  • La description de l’assembly (nom, version, …) telle que synthétisée dans le manifeste.
  • La description des assemblys externes utilisés par l’assembly.
  • La description des fichiers composant l’assembly (si assembly multi-fichiers).
  • La description de tous les types contenus dans l’assembly ainsi que leurs membres.

 

Toutes ces informations permettent au compilateur JIT d’effectuer les vérifications du code IL avant la compilation en code natif. Si le compilateur JIT détecte l’utilisation d’un type qui n’est pas représenté dans les métadonnées, il refusera systématiquement l’exécution de l’assembly.

Les métadonnées fixent en quelques sortes les règles que l’assembly doit respecter afin de pouvoir s’exécuter.

 

On peut visualiser son contenu grâce à l’outil ILDASM.exe (menu Afficher, Méta-informations, Afficher)

Dans cette fenêtre, on peut rechercher la rubrique relative à notre méthode Main()  grâce au menu de recherche.

Prenons par exemple l’application TP010105ProjetCS dont le code principal est le suivant :

usingSystem;

 

  static class MaClasse

  {

    static void Main()

    {

      Console.WriteLine("Salut en CS");

      Console.Read();

    }

  }

 

 

Ouvrons 1.5.2-UniqueCS dans ILDASM. La recherche de Main nous indique :

 

TypeDef #1 (02000002)

-------------------------------------------------------

      TypDefName: MaClasse  (02000002)

      Flags     : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit]  (00100000)

      Extends   : 01000001 [TypeRef] System.Object

      Method #1 (06000001) [ENTRYPOINT]

      -------------------------------------------------------

          MethodName: Main (06000001)

          Flags     : [Private] [Static] [HideBySig] [ReuseSlot]  (00000096)

          RVA       : 0x00002050

          ImplFlags : [IL] [Managed]  (00000000)

          CallCnvntn: [DEFAULT]

          ReturnType: Void

          No arguments.

 

 

On retrouve bien les éléments que nous avons indiqué dans le code C# :

Le nom de la méthode : Main

le type static de la fonction

le type de retour void

le modificateur d’accès Private qui a été placé par défaut par le compilateur

 

Si on modifie explicitement ce modificateur d’accès en Public dans le code, on régénère le projet et on ouvre l’assembly avec ILDASM, nous aurons cette fois la rubrique suivante :

 

      Method #1 (06000001) [ENTRYPOINT]

      -------------------------------------------------------

          MethodName: Main (06000001)

          Flags     : [Public] [Static] [HideBySig] [ReuseSlot]  (00000096)

          RVA       : 0x00002050

          ImplFlags : [IL] [Managed]  (00000000)

          CallCnvntn: [DEFAULT]

          ReturnType: Void

          No arguments.

 

Dans l’application 1.5.2-UniqueCS, ajoutons maintenant une seconde méthode  MaMethode:

usingSystem;

 

  static class MaClasse

  {

    private static void Main()

    {

      Console.WriteLine("Salut en CS");

      Console.Read();

    }

 

    public Int32 MaMethode(Int16 x, Byte y)

    {

      Int32 z = x + y;

      return z;

    }

  }

 

Les métadonnées vues depuis ILDASM nous affichent maintenant :

      Method #1 (06000001) [ENTRYPOINT]

      -------------------------------------------------------

            MethodName: Main (06000001)

            Flags     : [Private] [Static] [HideBySig] [ReuseSlot]  (00000091)

            RVA       : 0x00002050

            ImplFlags : [IL] [Managed]  (00000000)

            CallCnvntn: [DEFAULT]

            ReturnType: Void

            No arguments.

 

      Method #2 (06000002)

      -------------------------------------------------------

            MethodName: MaMethode (06000002)

            Flags     : [Public] [HideBySig] [ReuseSlot]  (00000086)

            RVA       : 0x00002064

            ImplFlags : [IL] [Managed]  (00000000)

            CallCnvntn: [DEFAULT]

            hasThis

            ReturnType: I4

            2 Arguments

                  Argument #1:  I2

                  Argument #2:  UI1

            2 Parameters

                  (1) ParamToken : (08000001) Name : x flags: [none] (00000000)

                  (2) ParamToken : (08000002) Name : y flags: [none] (00000000)

 

On se rend donc bien compte que les métadonnées décrivent avec précision les types ainsi que la signature de chacune de leurs méthodes, de leurs paramètres et de leur valeur de retour.

Le détail de ses métadonnées rend donc la taille de cette section très importante.

On voit ici que les métadonnées occupent 4 à 5 fois plus de place que le code par lui-même !

La taille occupée par les métadonnées dans un assembly peut représenter plus de la moitié de la taille de l’assembly !

C’est le prix à payer pour disposer d’une application rendue plus sûre.

 

Les métadonnées sont aussi utilisées par Visual Studio afin de mettre à disposition l’IntelliSense. Grace aux métadonnées, l’éditeur de code de VS connait à l’avance la signature des types et des fonctions que vous souhaitez utiliser.

 

3.6 – Les Espaces de noms

Les Espaces de noms ou Namespaces permettent de regrouper logiquement des types.

Un Espaces de noms n’est pas un type.

Un Espaces de noms n’étant pas un type, on ne peut pas lui appliquer un modificateur d’accès. Il est traité comme s’il disposait d’un accès Public.

 

Si l’espace de noms n’est pas déclaré explicitement dans le code, un Espaces de noms par défaut est automatiquement créé en prenant comme nom celui indiqué dans les propriétés du projet,  par défaut avec le nom du projet en cours.

 

Il est possible d’imbriquer les Espaces de noms à l’intérieur d’autres Espaces de noms.

Aucune limite n’est fixée quant au nombre de niveaux d’imbrication.

 

Déclaration d’un espace de noms

VB

  NamespaceMonEspaceDeNoms

    code   

    …

  EndNamespace

 

C#

  namespaceMonEspaceDeNoms

  {

    code   

    …

  }

 

L’instruction Namespace ne peut être utilisée qu’au niveau d’un fichier ou d’un autre namespace.

On ne peut donc pas déclarer un Namespace à l’intérieur d’une classe, d’une structure, d’un module, d’une interface ou d’une procédure.

 

Prenons l’exemple d’un développeur qui crée un assembly dans lequel il expose des fonctions dédiées au dessin et d’autres aux fichiers, il pourrait très bien regrouper les fonctions correspondantes dans :

un Namespace nommé MesTypesGraphiques

un Namespace nommé MesTypesFichiers.

Le code pourrait ressembler à celui-ci :

VB

  NamespaceMesTypesGraphiques

    Public Class MaClasseGraphique1

      PublicShared Function MaFonctionGraphique() As Integer

        Return 1

      End Function

    End Class

  EndNamespace

 

C#

  namespace MesTypeGraphiques

  {

    public class MaClasseGraphique1

    {

      public static int MaFonctionGraphique1() { return 1; }

    }

  }

 

Pour utiliser les types définis dans les namespaces, on doit auparavant les référencer dans le fichier depuis lequel on effectue l’appel. Par Exemple :

 

VB

Imports[namespace de l’assembly].MesTypesGraphiques

PublicClass Class2

  Public Sub Test()

    MaClasseGraphique1.MaFonctionGraphique()

  EndSub

EndClass

 

C#

usingMesTypeGraphiques;

 

  class Class2

  {

    public void Test()

    {

      MaClasseGraphique1.MaFonctionGraphique1();

    }

  }

 

Dans les métadonnées, la fonction MaFonctionGraphique1 est maintenant préfixée par l’espace de noms MesTypesGraphiques.

On peut contrôler cela avec ILDASM :

 

TypeDef #3 (02000004)

-------------------------------------------------------

      TypDefName: MesTypeGraphiques.MaClasseGraphique1  (02000004)

      Flags     : [Public] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit]  (00100001)

      Extends   : 01000001 [TypeRef] System.Object

      Method #1 (06000006)

      -------------------------------------------------------

            MethodName: MaFonctionGraphique1 (06000006)

            Flags     : [Public] [Static] [HideBySig] [ReuseSlot]  (00000096)

            RVA       : 0x00002094

            ImplFlags : [IL] [Managed]  (00000000)

            CallCnvntn: [DEFAULT]

            ReturnType: I4

            No arguments.

 

 

Afin d’éviter les confusions au niveau des espaces de noms, Microsoft préconise d’utiliser un nom unique comme nom de namespace principal.

 

Exemple : MaSociete.ThemePrincipal.ThemeSecondaire…

 

 Les types appartenant à un même espace de noms peuvent être compilés dans des assemblys différents.

 

 

 Afin de pouvoir utiliser les types exposés dans l’espace de noms MaSociete.Fichiers, il faut ajouter le fichier Assembly1.dll dans les références du projet et référencer (par la directive using en C# ou Imports en VB) l’espace de nom MaSociete.Fichiers dans chaque fichier ou l’ou souhaite utiliser les types F1, F2 ou F3.

 

Afin de pouvoir utiliser les types G1 à G5 exposés dans l’espace de noms MaSociete.Graphisme, il faut ajouter les 2  fichiers Assembly1.dll  et Assembly2.dll dans les références du projet et référencer (par la directive using en C# ou Imports en VB) l’espace de nom MaSociete.Graphisme dans chaque fichier ou l’ou souhaite utiliser les types G1 à G5.

 

3.7 – Références externes

3.7.1 - Création d’une librairie

Pour créer une librairie de classes, on a 2 solutions :

  • Créer le projet en utilisant le modèle librairie de classes
  • Soit en modifiant les propriétés du projet actuel (onglet Application, type de sortie).

 

3.7.2 - Utiliser une référence externe

Pour pouvoir utiliser une librairie, l’assembly appelant à d’abord besoin de connaitre l’emplacement du fichier qui contient l’assembly de cette librairie.

On doit donc :

  • Référencer l’assembly au niveau de l’assembly appelant
  • Référencer les espaces de noms à utiliser au niveau du fichier source appelant
Référencer l’assembly au niveau de l’assembly appelant

En VB, on affiche les propriétés du projet, Onglet Références et clic sur ajouter

En C#, clic droit sur le dossier Références dans l’explorateur de solutions, puis ajouter une référence

 

On a ensuite le choix entre 4 sortes de référencement :

.NET

Affiche la liste de toutes les librairies présentes dans le framework. Ces librairies sont placées dans le GAC.

COM

Affiche la liste de tous les composants COM disponibles sur le système. Cette liste ne se limite donc pas aux composants Microsoft (ex : Adobe Acrobat Type Library)

Projets

Affiche la liste des projets contenus dans la solution

Parcourir

Permet de charger explicitement un assembly situé sur le disque dur.

 

A ce stade, le compilateur sait comment accéder à l’assembly. Cette information sera stockée dans les métadonnées et le manifeste comme nous l’avons vu auparavant.

S’il s’agit d’une référence externe qui n’est pas dans le GAC, le fichier correspondant sera copié dans le répertoire des références de l’application :

Si projet Console ou WinForms, dans le repertoire bin/debug ou bin/release

Si projet application Web, dans le répertoire bin

Référencer les espaces de noms à utiliser au niveau du fichier source appelant

Pour que le code contenu dans un fichier source puisse accéder aux types contenus dans l’assembly externe, il faut ajouter explicitement au début de ce fichier le nom du namespace contenu dans cet assembly qui contient les types que l’on souhaite utiliser.

 

On déclare cela avec les directives suivantes

En VB : Imports espace_de_nom

En C# : Using espace_de_nom ;

 

Les types définis dans l’espace de nom référencé par ces directives permettent ensuite d’utiliser les types qu’il contient.

Une référence pouvant contenir plusieurs espaces de noms, il faut ajouter une directive Imports ou Using pour chacun des espaces de noms que l’on souhaite utiliser.

 

Il n’y a pas de limite quant au nombre de références et d’espaces de noms externes utilisables.

 
 

 

TP

 

VB.NET

 

Créer une solution TP0301VB avec un projet application console TP0301VBApp et un projet librairie de classe TP0301VBLib (avec 1 classe publique qui contient 1 champ public)

Référencer la librairie dans l’appliTP0301VBApp par l’option Projet.

Modifier la librairie TP0301VBLib (ajouter un champ), générer et voir les conséquences sur l’appli.

 

Créer une solution TP0302VB avec seulement une appli TP0302VBApp qui appelle la librairie TP0301VBLib par l’option Parcourir.

Modifier la librairie TP0301VBLib, générer et voir les conséquences sur l’appli.

 

Dans l’appli TP0302VBApp, tester l’ajout d’une référence COM (exemple Adobe Acrobat Type Library ).

Pendant l’écriture de la directive Imports (ou using en C#), observer que VS nous propose l’arborescence des espaces de noms de cette librairie. Importer l’espace de noms Acrobat.PDDocFlags ou autre si pas présent.

Créer une variable qui va utiliser un type contenu dans cet espace de noms et l’afficher.

 

C#

 

Créer une solution TP0301CS avec un projet application console TP0301CSApp et un projet librairie de classe TP0301CSLib (avec 1 classe publique qui contient 1 champ public)

Référencer la librairie dans l’appliTP0301CSApp par l’option Projet.

Modifier la librairie TP0301CSLib (ajouter un champ), générer et voir les conséquences sur l’appli.

 

Créer une solution TP0302CS avec seulement une appli TP0302CSApp qui appelle la librairie TP0301CSLib par l’option Parcourir.

Modifier la librairie TP0301CSLib, générer et voir les conséquences sur l’appli.

 

Dans l’appli TP0302CSApp, tester l’ajout d’une référence COM (exemple Adobe Acrobat Type Library ).

Pendant l’écriture de la directive Imports (ou using en C#), observer que VS nous propose l’arborescence des espaces de noms de cette librairie. Importer l’espace de noms Acrobat.PDDocFlags ou autre si pas présent.

Créer une variable qui va utiliser un type contenu dans cet espace de noms et l’afficher.

 

 

For additional local odeonbet giriş visit odeonnet.