Installer une assembly native dans WinSxS

Plusieurs méthodes sont possibles pour référencer des dépendances natives: consommer des dépendances en utilisant des bibliothèques statiques, des bibliothèques dynamiques, référencer un objet avec une interface COM etc… Suivant le cas d’utilisation, chacune de ces méthodes peut s’avérer plus intéressante qu’une autre. Par exemple, utiliser des bibliothèques dynamiques a le grand avantage d’apporter une modularité à un programme contrairement aux bibliothèques statiques. A l’opposé cette souplesse peut s’avérer être une source d’instabilité pour une application qui pourrait en dépendre si la bibliothèque fait défaut durant l’exécution.

Le répertoire WinSxS dans Windows a pour but d’apporter une solution pour garantir une souplesse quant à la consommation d’une dépendance native pour une application tout en garantissant une certaine robustesse. Ceci afin d’éviter qu’une application ne soit pas trop facilement déstabilisée dans le cas de la mise à jour d’une dépendance.

Le but de cet article est de présenter au moyen d’un exemple simple, l’utilisation d’une DLL ajoutée au répertoire WinSxS. Dans un premier temps, on va expliquer quelques généralités sur le répertoire WinSxS puis illustrer ces généralités avec un exemple.

@oratnin

Quelques explications en préambule

Avant d’expliquer l’intérêt de WinSxS, on peut apporter quelques précisions pour donner un peu de contexte.

Différences entre une bibliothèque statique et une bibliothèque dynamique

Il existe 2 types de bibliothèques pouvant être des dépendances natives: les bibliothèques statiques et les bibliothèques dynamiques.

Bibliothèque statique

Les bibliothèques statiques (i.e. static library) sont des fichiers dont l’extension est .lib. Si un exécutable ou une autre bibliothèque a une dépendance vers une bibliothèque statique, le code utilisé sera directement incorporé dans le fichier de l’exécutable ou de la bibliothèque.

L’intérêt de ces bibliothèques est d’être certain d’inclure les bonnes dépendances dans l’exécutable final. Au moment de l’exécution, il n’y a pas de risques que le fichier de la bibliothèque ne soit pas accessible puisqu’il est inclus dans l’exécutable lui-même. De même, il n’y a pas de risque d’utiliser une mauvaise version.

A l’inverse, pour mettre à jour ce type de bibliothèque, il faut recompiler l’exécutable l’utilisant. D’autre part, si plusieurs exécutables utilisent la même bibliothèque, elle sera chargée en mémoire autant de fois qu’un exécutable l’utilise puisqu’elle fait partie directement de l’exécutable.

Bibliothèque dynamique

Les bibliothèques dynamiques (i.e. dynamic library) se rapprochent des assemblies .NET car elles ont quelques caractéristiques en commun:

  • Il s’agit de fichiers séparés qui ne seront pas inclus dans le fichier exécutable ou dans une bibliothèque l’utilisant comme dépendance.
  • L’extension de ces bibliothèques est .dll pour Dynamic Link Library.

Le gros intérêt de ces bibliothèques est d’être partageables entre plusieurs exécutables. Ainsi, à l’exécution, quand un appel est effectué vers une fonction se trouvant dans une bibliothèque dynamique, elle est chargée en mémoire. Si un autre exécutable utilise la même bibliothèque, le chargement en mémoire n’est effectué qu’une seule fois ce qui permet d’économiser de l’espace mémoire.

L’inconvénient majeur des bibliothèques dynamiques est qu’elles doivent être accessibles au moment de l’exécution. Comme ce sont des fichiers séparés, si l’un d’entre eux n’est pas accessible au moment de l’exécution, il se produira une erreur et l’exécutable va interrompre son exécution.

Dans un article précédent, un exemple avait permis d’illustrer les différences d’utilisation d’une bibliothèque et d’une bibliothèque statique (cf. Référencer une DLL C++ avec une bibliothèque statique d’import).

Objet PE vs Module vs Assembly

Tous ces termes sont utilisés à la fois pour des technologies managées et non managées. D’une façon générale, ils désignent des objets contenant des informations que le système d’exploitation sait interpréter. Ces informations peuvent être des métadonnées et/ou du code (pas forcément du code directement exécutable). Quelque-soit la forme, un objet exécutable ne suffit pas à lui-même pour être exécuté, il a besoin de dépendances et ainsi, il doit indiquer des informations pour que le système d’exploitation soit capable de trouver ses dépendances de façon à exécuter le code qu’il contient.

Objet PE

PE pour Portable Executable est un format de fichier commun utilisé pour structurer des exécutables, des bibliothèques ou des drivers système. Ce format indique une structure connue du système d’exploitation pour qu’il puisse savoir où trouver des informations qui lui permettront de mapper le contenu du fichier organisé en sections à des zones en mémoire. La structure est indiquée dans l’en-tête du fichier (i.e. PE Header).

Lister l’en-tête d’un fichier PE

On peut lister le contenu du PE Header avec dumpbin en exécutant la commande:

dumpbin /headers <nom du fichier .DLL> 

dumpbin est un utilitaire livré avec Visual Studio C++, le chemin de l’exécutable est du type:

C:\Program Files\Microsoft Visual Studio [version VS]\VC\bin  

ou

C:\Program Files (x86)\Microsoft Visual Studio [version VS]\VC\bin  

On peut y accéder directement à partir de la ligne de commandes Visual Studio (ou “Developer Command Prompt for VS2017”).

L’en-tête PE d’un fichier contient des structures au sens C contenant différents types d’informations. La structure de plus haut niveau s’appelle IMAGE_NT_HEADER:

typedef struct _IMAGE_NT_HEADERS  
{ 
    DWORD                 Signature; 
    IMAGE_FILE_HEADER     FileHeader; 
    IMAGE_OPTIONAL_HEADER OptionalHeader; 
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; 

Dans la structure de type IMAGE_OPTIONAL_HEADER peut se trouver une autre structure de type IMAGE_DATA_DIRECTORY dans le cas d’une DLL:

typedef struct _IMAGE_OPTIONAL_HEADER  
{ 
    ... 
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 

L’élément DataDirectory est un tableau de structures de type IMAGE_DATA_DIRECTORY contenant des indications sur l’emplacement et la taille des informations importantes des structures de données du fichier PE:

typedef struct _IMAGE_DATA_DIRECTORY  
{ 
    DWORD VirtualAddress; 
    DWORD Size; 
} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY; 

Ces informations sont rangées de cette façon dans le tableau:

Position Type Description
0 IMAGE_DIRECTORY_ENTRY_EXPORT Table des exports
1 IMAGE_DIRECTORY_ENTRY_IMPORT Table des imports
2 IMAGE_DIRECTORY_ENTRY_RESOURCE Table des ressources
3 IMAGE_DIRECTORY_ENTRY_EXCEPTION Table des exceptions
4 IMAGE_DIRECTORY_ENTRY_SECURITY Table des certificats
5 IMAGE_DIRECTORY_ENTRY_BASERELOC Table des relocalisations
6 IMAGE_DIRECTORY_ENTRY_DEBUG Informations de debug
7 IMAGE_DIRECTORY_ENTRY_COPYRIGHT
/ IMAGE_DIRECTORY_ENTRY_ARCHITECTURE
Copyright
8 IMAGE_DIRECTORY_ENTRY_GLOBALPTR Pointeurs globaux
9 IMAGE_DIRECTORY_ENTRY_TLS Thread local storage
10 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG Load configuration table
11 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT Table des imports liés
12 IMAGE_DIRECTORY_ENTRY_IAT Table des adresses des imports
13 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT Descripteur des imports en différé (Delay loading)
14 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR En-tête runtime COM+

Parmi ces informations se trouvent les adresses des fonctions d’import dans la table des imports. Ces fonctions ne se trouvent pas dans le code de l’appelant mais dans les DLL appelées. Seules les informations concernant ces fonctions sont dans le code de l’appelant.

Ces informations sont:

  • Les noms des fonctions d’import
  • Les noms des DLL où se trouvent ces fonctions.

Comme on peut le voir, la DLL appelante ne contient pas le chemin de ses dépendances mais seulement le nom des DLL à appeler. En fonction du nom de la DLL, le loader du système d’exploitation va chercher l’emplacement exacte du fichier pour le charger.

Module

Un module est aussi un terme utilisé en managée et en non managée. Il désigne une unité de compilation contenant des métadonnées des types qu’il contient et du code compilé. Le code compilé n’est pas forcément du code machine. Il correspond à un niveau d’échelle plus élevé que les objets PE.

Un module ne peut pas être déployé seul et il ne contient pas de manifest c’est-à-dire qu’il n’indique pas quelles sont ses dépendances. Chaque module contient un seul type de code.

L’intérêt des modules est de pouvoir être utilisés indépendamment dans une unité déployable comme les assemblies.

Assembly

Une assembly est une unité déployable en opposition aux modules qui sont des unités de compilation. L’assembly peut ainsi être déployée seule. D’une façon générale, il s’agit de fichiers avec une extension .exe pour un exécutable ou .dll pour une bibliothèque de classes.

En .NET, les assemblies sont assez souples pour contenir un ou plusieurs modules, éventuellement des fichiers de ressources et des métadonnées. Les modules peuvent être compilés dans des langages différents. Il est aussi possible de merger le contenu de plusieurs assemblies dans une seule assembly.

On considère plusieurs types d’assemblies:

  • Assembly managée: objet PE contenant du code managé. En .NET c’est la plus petite unité de déploiement. Dans la pratique, ces fichiers peuvent être des exécutables ou des bibliothèques de classes. Le plus souvent quand on utilise le terme assembly c’est pour désigner les assemblies managées.
  • Assembly mixte: assembly .NET contenant à la fois du code managé et du code natif (cf. C++/CLI).
  • Assembly native: on retrouve le terme assembly native dans la documentation Microsoft concernant WinSxS. Ce terme est ambigu car il laisse penser qu’il désigne d’assemblies .NET contenant seulement du code natif or, dans le cas de WinSxS, on parle bien de DLL natives classiques.
    En effet, en .NET, on distingue les assemblies (sous-entendu les assemblies managées) qui contiennent exclusivement du code managé et les assemblies mixtes contenant, à la fois du code managé et du code natif. Ainsi dans le cas où il n’y a que du code natif, le terme assembly native désigne un groupe d’une ou plusieurs DLL natives, de composant COM ou des collections de ressources, de types ou d’interfaces.
  • Side-by-side assembly: assembly native contenant une liste de ressources ou un groupe de DLL avec un manifest. Le loader du système d’exploitation utilise les informations du manifest pour identifier l’assembly et être capable de la charger quand un exécutable a une dépendance vers cette dernière. Elles ont une identité unique et sont utilisées pour éviter de casser des dépendances.
  • Private assembly: assembly native utilisée seulement par une seule application. Elle peut être inclue en tant que ressource d’une autre DLL ou installer dans le même répertoire que l’exécutable qui l’utilise.
  • Shared assembly: side-by-side assembly déployée dans le répertoire du cache des assemblies du système WinSxS. Ces assemblies peuvent être utilisées par un exécutable si la dépendance est indiquée dans son manifest.

Les assemblies sont composées des éléments suivants:

  • PE Header: l’assembly est structurée dans un objet PE de plus bas niveau. A ce titre, il possède ce type d’en-tête.
  • Un manifest contenant une liste des références externes de l’assembly.
  • Sections contenant du code natif compilé.
  • Dans le cas d’assembly managée:
    • CLR Header: présent dans le cas d’une assembly managée. Ce sont des informations sur la version cible du framework .NET; éventuellement le hash pour signature par nom fort (cf. Signature par nom fort); l’adresse dans le fichier des ressources et le point d’entrée indiquant la table des métadonnées permettant à l’assembly de s’autodécrire.
    • Liste des objets binaires utilisés dans les métadonnées.
    • Liste des chaines de caractères utilisées dans les métadonnées .
    • Liste des chaines de caractères utilisées dans le code IL (i.e. Intermediate Language).
    • Liste des GUID utilisés dans l’assembly.
    • Tables des métadonnées permettant d’indiquer des informations sur tous les types utilisés dans l’assembly.
    • Le code IL (i.e. Intermediate Language).

Ainsi dans le répertoire WinSxS se trouve des assemblies partagées. Ce répertoire permet de remplacer le répertoire dllcache (qui n’est plus présent à partir de Windows 7).

Chargement des DLL natives

Lorsqu’un exécutable ou une DLL appellent une fonction se trouvant dans une DLL tiers, ils ont une dépendance vers cette DLL tiers. A l’exécution, il existe différentes façons pour charger cette dépendance native en fonction de la façon dont la dépendance a été définie: dépendance implicite ou dépendance explicite.

Dépendance implicite

Une dépendance par lien implicite (i.e. implicit linking) vers une DLL tiers est la façon la plus courante de définir une dépendance vers une DLL tiers. Elle est indiquée au préalable dans les paramètres de l’éditeur de liens (i.e. linker). Ainsi, pendant la phase d’éditions de liens (i.e. linking) des informations seront ajoutées dans l’en-tête PE Header de la DLL pour préciser:

  • Le nom de la DLL où se trouve la fonction à appeler. Seulement le nom est indiqué et non le chemin de la DLL.
  • L’adresse à laquelle se trouve la fonction à exécuter dans la DLL.

Ces informations sont rangées au début de l’en-tête PE Header (position 1) dans une partie appelée IMAGE_DIRECTORY_ENTRY_IMPORT. Cette partie est divisée en 2 tables:

  • Import directory table permettant d’indiquer le nom de la DLL comme on l’a vu précédemment. Cette partie est lue par le loader pour savoir quel DLL charger.
  • Import address table pour préciser où se trouve la fonction à exécuter dans la DLL. Cette adresse est lue directement par l’exécutable au runtime pour savoir où exécuter le code de la dépendance.

Les dépendances indiquées par lien implicite sont chargées directement au lancement de l’exécutable.

Voir les dépendances implicites d’une DLL avec DependencyWalker

Sachant que les informations concernant les dépendances sont indiquées dans l’en-tête d’une DLL ou d’un exécutable, il est possible de le lire au préalable et de savoir quelles sont les dépendances.

Par exemple, si on utilise DependencyWalker (i.e. www.dependencywalker.com/) pour lister les dépendances de la DLL NativeCallee.dll obtenue après avoir compilé le code se trouvant dans le repository Github msoft/cpp_dll_reference, on obtient la liste suivante:

Dans l’exemple précédent, la DLL CallerRedirection.dll possède une dépendance vers la DLL NativeCallee.dll. La fonction appelée par CallerRedirection est DisplayTextWithCallee.

DependencyWalker trouve le chemin des dépendances en imitant le parcours des répertoires par le loader.

Dépendance explicite

Une DLL possède une dépendance par lien explicite (i.e. explicit linking) lorsque la DLL tiers est chargée par programmation en appelant la fonction LoadLibrary. Sachant que l’appel est effectué dans le code, il n’est pas indiqué dans l’en-tête de la DLL, il n’est donc pas possible de voir cette dépendance en lisant statiquement le contenu de l’en-tête.

Comme ce type de dépendance est indiqué dans le code, il est possible d’effectuer le chargement d’une DLL juste avant de l’utiliser et non au démarrage de l’exécutable.

Voir les dépendances explicites avec DependencyWalker

DependencyWalker est capable de lire les dépendances explicites d’un exécutable. Il faut utiliser la fonctionnalité de profiling qui va scruter le chargement des dépendances à l’exécution. Un exemple plus bas permet d’illustrer cette fonctionnalité.

Stratégie de recherche d’une assembly partagée ou d’une DLL native

La stratégie de recherche d’une DLL n’est pas la même que la stratégie utilisée pour chercher une assembly se trouvant dans WinSxS.

Dans le cas d’une DLL native, la plupart des cas, cette stratégie s’effectue de cette façon:

  1. Le système cherche si la DLL est déjà chargée en mémoire. Si c’est le cas, il arrête la recherche.
  2. Si la DLL fait partie des DLL connues dans la clé de registre HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs, il utilise la DLL qui y est indiquée.
  3. Le système cherche dans le répertoire de l’application.
  4. Il cherche dans le répertoire système (par exemple: C:\Windows\System32 suivant les versions de Windows ou C:\Windows\SysWow64).
  5. Le répertoire système 16-bit si il existe (par exemple: C:\Windows\System).
  6. Le répertoire de Windows (par exemple C:\Windows).
  7. Le répertoire courant.
  8. Les répertoires listés dans la variable d’environnement PATH.

Pour plus de détails sur la stratégie de recherche d’une DLL native: docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order.

Dans le cas d’assembly partagée dans le répertoire WinSxS, la recherche s’effectue de cette façon:

  1. Dans le répertoire WinSxS,
  2. Dans le répertoire ou dans un sous-répertoire de l’application. La recherche dans les sous-répertoires peut se faire suivant des éléments de culture.

Pour plus de détails sur la stratégie de recherche d’une assembly partagée: docs.microsoft.com/en-us/windows/win32/sbscs/assembly-searching-sequence.

Contexte d’activation

Qu’une dépendance vers une bibliothèque dynamique soit implicite ou explicite, elle doit être chargée pour pouvoir exécuter une fonction qui s’y trouve. Quand elle est chargée, l’ensemble de ses dépendances doivent être chargées aussi. Le chargement de la DLL implique plusieurs étapes:

  • Lire les informations concernant la DLL à charger dans la table d’import de l’objet PE ou dans le manifest d’un exécutable.
  • Trouver le fichier correspondant à la DLL en fonction de ces informations en utilisant une stratégie de recherche.
  • Charger la DLL ainsi que ses dépendances.

Ces différentes étapes sont effectuées par le système d’exploitation. La dernière étape qui consiste à charger la DLL et ses dépendances se fait en utilisant un espèce de “bac à sable” isolé du reste de l’application. Ce “bac à sable” s’appelle le contexte d’activation (i.e. activation context). Il pourrait être assimilé aux domaines d’application (i.e. Application Domains) en .NET toutefois son fonctionnement est très différent. Le but du contexte d’activation est de permettre de charger une DLL et ses dépendances en utilisant son manifest de façon isolée.

Pour un exécutable, il peut exister plusieurs contextes d’activation toutefois un seul peut être actif. Quand un contexte d’activation est actif, les autres sont désactivés. Le manifest d’un exécutable est chargé dans un contexte d’activation appelé contexte d’activation par défaut.

Ainsi si on considère un exécutable possédant une dépendance implicite vers une DLL, le séquencement des utilisations des contextes d’activation sera le suivant:

  1. Au démarrage de l’exécutable, son manifest est utilisé pour créer le contexte d’activation par défaut.
  2. Le contexte d’activation est rendu actif pour chercher la dépendance en fonction des informations indiquées dans le manifest de l’exécutable.
  3. Un contexte d’activation est créé et activé pour charger la DLL et ses dépendances en utilisant son manifest. Le contexte d’activation par défaut est désactivé. Le manifest de la DLL doit être inclus dans cette dernière en tant que ressource avec un ID 1 ou 2. Dans le cas contraire, le manifest de la DLL sera ignoré.
    Si le manifest de la DLL est un fichier séparé, il doit être référencé dans le manifest de l’exécutable.
  4. Quand la DLL et ses dépendances sont chargées en mémoire, le contexte d’activation utilisé pour ce chargement est désactivé et le contexte d’activation par défaut est ré-activé.
  5. L’exécutable continue son exécution et la fonction dans la DLL peut être appelée.

Par défaut, tous ces mécanismes s’exécutent implicitement toutefois il est possible de les provoquer explicitement par code avec des fonctions de l’API Win32 comme CreateActCtx, DeactivateActCtx, GetCurrentActCtx etc…

Comme on a pu le voir, les manifests sont utilisés de façon isolée dans les contextes d’activation. Ainsi si le contexte d’activation par défaut est actif et si on souhaite charger une DLL possédant un manifest en tant que ressource en utilisant la fonction LoadLibrary (dépendance explicite), c’est le contexte actif c’est-à-dire le contexte par défaut qui sera utilisé pour le chargement de la DLL. Au cours du chargement dans le contexte par défaut c’est le manifest de l’exécutable qui sera utilisé et non le manifest inclus dans la DLL.

Pourquoi utiliser WinSxS ?

DLL Hell

L’utilisation de bibliothèques dynamiques apporte une grande souplesse en permettant de consommer des dépendances de façon modulaire. Une DLL peut ainsi être partagée entre plusieurs exécutable. Sachant que le fichier est partagé, il doit donc être accessible pour tous les exécutables qui l’utilisent. Si jamais le fichier n’est plus présent ou s’il est mis à jour, tous les exécutables qui l’utilisent pourront éventuellement être déstabilisés dans le cas où une incompatibilité survient.

Comme on l’a vu précédemment, les dépendances implicites sont indiquées en utilisant le nom de la DLL. On peut ainsi facilement remplacer la DLL par une autre avec le même nom mais de version différente et ainsi introduire un breaking change qui peut empêcher à l’application de s’exécuter.

Jusqu’à Windows XP, si une DLL était partagée entre plusieurs applications et qu’elle subissait une modification soit par remplacement pour une autre version, soit par effacement, elle pouvait compromettre l’exécution de toutes les applications qui l’utilisaient. Il était compliqué de revenir en arrière car les jeux de dépendances pouvaient rendre stable certaines applications utilisant une version précise de la DLL et déstabiliser les autres.

Le DLL hell désigne la complexité que pouvait engendrer le partage d’une DLL entre plusieurs applications et des difficultés à gérer toutes les dépendances qui en découlent.

A partir de Windows XP, une solution pour résoudre le DLL hell a été d’utiliser le répertoire WinSxS.

Répertoire WinSxS

Le répertoire WinSxS (pour Windows Side-by-Side) est une solution de Microsoft introduite à partir de Windows XP pour répondre au problème du DLL Hell. L’idée est de permettre aux applications de partager des DLL tout en évitant qu’une application ne soit déstabilisée quand une de ces dépendances est mise à jour par une autre application. Ainsi pour mettre en place ce type de solution, une application doit être capable:

  • De désigner une dépendance en utilisant un identifiant sans utiliser le nom explicite du fichier de la DLL.
  • D’indiquer une dépendance sans forcément indiquer précisément la version de la dépendance. L’intérêt est de pouvoir changer la version de la DLL sans nécessairement devoir recompiler l’application qui consomme la DLL.
  • D’utiliser une version d’une DLL quand une autre application utilise une autre version de cette DLL.

Le répertoire WinSxS vise à apporter une solution à tous ces problèmes. Ce répertoire contient un ensemble de ressources comme des DLL ou d’objets COM possédant un fichier manifest. Ce manifest permet d’identifier un objet et d’assurer son unicité dans le répertoire.

Le chemin du répertoire WinSxS est du type <répertoire de Windows>\winsxs (par exemple C:\windows\winsxs).

Ajouter une assembly native dans WinSxS

L’ajout d’une DLL dans le répertoire WinSxS ne se fait pas directement. Pour rajouter une DLL dans ce répertoire, il faut d’abord qu’elle possède, au minimum, certaines informations comme:

  • Le type d’assembly: "win32" permet d’indiquer qu’il s’agit d’une assembly. Ce paramètre ne veut pas dire que l’architecture d’exécution est obligatoirement x86.
  • Un nom.
  • Un numéro de version indiquée sous la forme de 4 parties a.b.c.d.
  • Une clé publique obtenue après signature.
  • Une indication sur l’architecture d’exécution de la DLL: "x86" ou "ia64".

Ces informations représentent l’identité de la DLL et permettent d’identifier la DLL de façon unique dans le répertoire WinSxS. Il est possible de rajouter d’autres informations (cf. Assembly Manifests).

Ces informations peuvent être associées à une DLL en utilisant un fichier manifest. Un 3e fichier appelé fichier catalogue (extension .CAT) va décrire l’association entre la DLL et son fichier manifest. On va ensuite signer le fichier catalogue pour garantir l’unicité de l’ensemble formé par la DLL et son fichier manifest. Cet ensemble appelé assembly native pourra être rajouté dans le répertoire WinSxS.

L’ajout dans WinSxS ne se fait pas directement par copier-coller, il faut le faire par l’intermédiaire d’un fichier d’installation ou par programmation.

Exemple d’ajout d’une assembly native dans WinSxS

On va illustrer l’ajout d’une assembly dans le répertoire WinSxS avec un exemple simple d’une application Console qui consomme une dépendance sous forme de DLL native.

Le but de l’application est d’appeler une DLL native pour afficher le contenu d’une chaîne de caractères.

Le code de l’exemple se trouve dans le repo GitHub msoft/cpp_dll_reference. La solution comporte 3 projets :

  • NativeCaller: application Console en C++ qui appelle la méthode Redirect::RedirectCall() se trouvant dans le projet CallerRedirection.
  • CallerRedirection: il s’agit d’un projet C++ permettant de générer une bibliothèque dynamique. Cette bibliothèque ne contient que la classe Redirect qui va appeler la fonction DisplayTextWithCallee() exposée dans le projet NativeCallee.
  • NativeCallee: c’est un projet C++ pour générer une bibliothèque dynamique. Cette bibliothèque expose la méthode DisplayTextWithCallee() pour afficher le contenu d’une chaine de caractères.

Pour résumer les appels se font de cette façon:

NativeCaller (exécutable) CallerRedirection (DLL) NativeCallee (DLL)
Main() Redirect::RedirectCall() DisplayWithCallee()

Si on exécute l’application, le résultat est du type:

Calling other lib:  
Text to display 

"Calling other lib:" est affiché par NativeCaller.exe et "Text to display" est affiché par NativeCallee.dll

Pour une explication plus complète du code de cet exemple, on peut se référer à l’article Référencer une DLL C++ avec une bibliothèque statique d’import.

Après avoir cloné le repository GitHub, il faut l’ouvrir avec Visual Studio et le compiler. Après compilation, il devrait résulter les fichiers suivants dans le répertoire Debug:

  • NativeCaller.exe
  • CallerRedirection.dll
  • NativeCallee.dll

Dans un premier temps, on va signer la DLL NativeCallee après l’avoir associé à un fichier manifest de façon à en faire une assembly native. On va ensuite ajouter l’assembly native au répertoire WinSxS avec un fichier d’installation. Enfin on va appeler l’assembly native dans WinSxS à partir de l’exécutable.

Signer la DLL NativeCallee

La signature d’une DLL native est très semblable à la signature par nom fort en .NET (cf. Signature des assemblies par nom fort en 5 min). La différence est qu’on va signer un fichier catalogue décrivant l’association entre la DLL et son fichier manifest au lieu de signer seulement le fichier DLL comme en .NET.

Pour exécuter les commandes suivantes, il faut ouvrir une invite de commandes développeur Visual Studio (ou “Developer Command Prompt for VS2017”).

Créer un certificat pour une autorité locale de certification

Avant de créer un certificat, on va créer le certificat d’une autorité locale de certification qu’on rajoutera au magasin des autorités de certification de confiance de la machine. On va ensuite créer un certificat provenant de cette fausse autorité de certification de façon à ce que le certificat soit valide.

Pour créer le certificat de certificat de l’autorité locale de certification, on exécute la commande suivante:

makecert -r -pe -n "CN=CACert" -a sha1 -sky signature -cy authority -sv CACert.pvk
CACert.cer 

Le détail des options est:

  • -r permet de générer un certificat autosigné.
  • -pe indique que la clé privée est exportable.
  • -n "CN=CACert" indique le nom de l’entité qui a généré le certificat.
  • -a sha1 permet de préciser l’algorithme utilisé.
  • -cy authority indique le type de certificat.
  • -sv CACert.pvk fichier .PVK contenant la clé privée.

A l’exécution, une 1ère pop-up demande de créer un mot de passe pour créer un clé privée. Une 2e pop-up redemande le mot de passe pour accéder à la clé privée qui vient d’être créée pour générer le certificat.

Après exécution, on obtient les fichiers:

  • Un fichier CACert.pvk contenant la clé privée
  • Un fichier CACert.cer contenant le certificat.

Pour générer un fichier PFX contenant la clé publique et la clé publique, on exécute la commande suivante:

pvk2pfx -pvk CACert.pvk -spc CACert.cer -pfx CACert.pfx -pi <mot de passe> 

A la suite de cette étape, le fichier CACert.pfx a été créé.

Ajouter le certificat de l’autorité locale au magasin des autorités de certification de confiance

Cette étape permettra d’indiquer que l’autorité locale CACert est une autorité de confiance:

  1. Double-cliquer sur le fichier CACert.pfx pour ouvrir l’assistant d’importation du certificat.
  2. Cliquer sur “suivant” puis de nouveau “Suivant”.
  3. Laisser le mot de passe vide et cocher seulement “Inclure toutes les propriétés étendues” (“Include all extended properties“):
  4. Cliquer sur “Suivant”.
  5. Sélectionner “Placer tous les certificats dans le magasin suivant” (“Place all certificates in the following store“).
  6. Cliquer sur “Parcourir” et sélectionner “Autorités de certification racines de confiance” (“Trusted Root Certification Authorities“) puis cliquer sur OK.
  7. Cliquer sur “Suivant” puis “Terminer”.

A la fin de cette étape, le certificat devrait être rajouté au magasin des autorités de certification de confiance. Pour le vérifier, on peut effectuer les étapes suivantes:

  1. Appuyer sur [Win] + [R] et taper mmc.exe
  2. Cliquer sur “Fichier” ⇒ “Ajouter/Supprimer un composant logiciel enfichable…” (“Add/Remove Snap in…“)
  3. Cliquer sur “Certificats” à gauche puis cliquer sur “Ajouter >”
  4. Sélectionner “Mon compte d’utilisateur” (“My user account“) puis cliquer sur Terminer.
  5. Cliquer sur OK pour valider.
  6. Déplier “Certificats = Utilisateur actuel” (“Certificates – Current User“) puis “Autorités de certification racines de confiance” (“Trusted Root Certification Authorities“) puis “Certificats”
  7. Le certificat devrait être présent:

Créer un certificat pour signer l’assembly

Pour créer un certificat valide pour signer l’assembly, on execute la commande suivante:

makecert -pe -n "CN=DllCert" -a sha1 -sky exchange -ic CACert.cer -iv CACert.pvk 
-len 2048 -sv DllCert.pvk DllCert.cer 

Le détail des options est:

  • -pe indique que la clé privée est exportable.
  • -n "CN=DllCert" indique le nom de l’entité qui a généré le certificat.
  • -ic CACert.cer permet d’indiquer le certificat de l’émetteur.
  • -iv CACert.pvk indique le clé privée de l’émetteur.
  • -len 2048 précise la longueur de la clé du certificat. La longueur doit être au minimum de 2048 pour être valide pour WinSxS.
  • -a sha1 permet de préciser l’algorithme utilisé.
  • -sv DllCert.pvk fichier .PVK contenant la clé privée.

A l’exécution, une 1ère pop-up demande de créer un mot de passe pour créer la clé privée; une 2e pop-up redemande le mot de passe pour accéder à la clé privée. Une 3e pop-up demande le mot de passe pour accéder à la clé privée du certificat CACert.cer de l’autorité locale de certification. Il faut indiquer le mot de passe utilisé dans l’étape précédente.

A la fin de cette étape, on obtient 2 nouveaux fichiers:

  • DllCert.cer contenant le certificat et
  • DllCert.pvk contenant la clé privée.

Si on double-clique sur le fichier DllCert.cer nouvellement créé, la description du certificat s’affiche et sur l’onglet “Chemin d’accès de certification” (“Certification path“), on peut voir le lien avec l’autorité local de certification et la validité du certificat:

On génère un fichier .PFX contenant la clé privée et la clé publique en exécutant la commande suivante:

pvk2pfx -pvk DllCert.pvk -spc DllCert.cer -pfx DllCert.pfx 

Il faut préciser le mot de passe utilisé pour accéder à la clé privée du certificat DllCert.cer. A la suite de cette étape, le fichier DllCert.pfx a été créé.

Générer le fichier manifest

Avant de générer le fichier manifest, on extrait la clé publique du certificat en exécutant la commande:

pktextract DllCert.cer 

Le résultat sera du type:

Microsoft (R) Side-By-Side Public Key Token Extractor 
Copyright (C) Microsoft Corporation. All Rights Reserved 

Certificate: "CACert" - 2048 bits long 
        publicKeyToken="3ce09db04e4b62f1" 

On utilise la clé publique pour éditier le fichier manifest, par exemple:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
    <assemblyIdentity 
        type="win32" 
        name="NativeCallee" 
        version="1.0.0.0" 
        processorArchitecture="x86"         
        publicKeyToken="3ce09db04e4b62f1"/> 
    <file name="NativeCallee.dll" hashalg="SHA1" /> 
</assembly>  

On appelle ce fichier NativeCallee.manifest. On indique dans ce fichier les 5 informations obligatoires pour ajouter une assembly native au répertoire WinSxS.

Il faut placer la DLL NativeCallee.dll dans le répertoire du fichier manifest NativeCallee.manifest. Ensuite on exécute la commande suivante pour rajouter au manifest le hash de la DLL:

mt -manifest NativeCallee.manifest -hashupdate -makecdfs 

Le détail des options est:

  • -hashupdate permet de mettre à jour le fichier manifest en ajoutant le hash.
  • -makecdfs permet de générer le fichier NativeCallee.manifest.cdf contenant la description du contenu du catalogue utilisé pour valider le manifest.

A la fin de cette étape, on obtient les fichiers:

  • NativeCallee.manifest correspondant au fichier manifest de la DLL NativeCallee.dll,
  • NativeCallee.manifest.cdf contenant la description du contenu du catalogue.

Génération du fichier catalog .CAT

Ce fichier permet d’associer la DLL NativeCallee.dll avec son fichier manifest NativeCallee.manifest. On peut le générer en exécutant la commande suivante:

makecat NativeCallee.manifest.cdf 

Cette étape va permettre de générer un fichier .CAT supplémentaire.

Signature du fichier catalogue

On signe le fichier catalogue ce qui va permettre de signer l’assembly car le fichier catalogue associe la DLL avec son fichier manifest. Pour signer le fichier catalogue, il faut exécuter la commande suivante:

signtool sign /d DllCert.pfx NativeCallee.cat 

Il faut préciser le mot de passe utilisé pour créer le certificat DllCert.cer.

A la fin de cette étape, on obtient les 3 fichiers suivants:

  • NativeCallee.dll
  • NativeCallee.cat permettant d’associer la DLL avec son manifest.
  • NativeCallee.manifest qui est le fichier manifest de NativeCallee.dll.

Ces 3 fichiers constituent l’assembly native à rajouter dans le répertoire WinSxS.

Générer un fichier d’installation

Cette étape permet de créer un fichier d’installation pour rajouter l’assembly native contenant le fichier NativeCallee.dll dans le répertoire WinSxS.

Installer un modèle de projet Visual Studio pour créer des fichiers d’installation

Pour créer le fichier d’installation, on peut utiliser Visual Studio 2017 Community en ajoutant le modèle de projet “Setup project”. Ce type de projet est disponible dans la catégorie “Autres types de projets” (“Other Project Types“) quand on veut créer un nouveau projet. Si cette catégorie n’est pas disponible, il faut ajouter le modèle de projet “Setup Project”.

Pour ajouter le modèle de projet “Setup Project”, il faut effectuer les étapes suivantes:

  1. Cliquer sur “Outils” puis “Extensions et mises à jour” (“Extensions and Updates“).
  2. Dans la partie “En ligne”, taper Microsoft Visual Studio Installer":
  3. Cliquer sur “Télécharger”.
  4. Il faut fermer toutes les instances de Visual Studio pour que l’installation commence. Une pop-up doit s’afficher après fermeture pour que l’installation de l’extension aboutisse.

Après installation, on doit pouvoir créer des projets de type “Setup Project”:

Créer un projet “Setup Project”

Il faut créer un projet de type “Setup Project” accessible dans la catégorie “Autres types de projets” puis “Visual Studio installer”.

Après création du projet:

  1. Effectuer un clique droit sur le projet puis cliquer sur “Add” pour ajouter les 3 fichiers correspondant à l’assembly native:
    • NativeCallee.cat qui est le catalogue associant la DLL NativeCallee.dll au fichier manifest NativeCallee.manifest.
    • NativeCallee.dll
    • NativeCallee.manifest.

    Le projet doit se présenter de cette façon:

  2. Générer le fichier SetUp en cliquant sur “Générer” ⇒ “Générer la solution”.

Le fichier résultat InstallProject.msi se trouve dans le répertoire de sortie nommé Debug.

Modifier le fichier .MSI avec Orca

Orca est un outil permettant de modifier les fichiers d’installation .MSI. Le but est de modifier le fichier .MSI généré à l’étape précédente pour que les 3 fichiers NativeCallee.manifest, NativeCallee.dll et NativeCallee.cat fassent partie de la même assembly native.

Orca est livré avec le SDK Windows 10 disponible quand on installe Visual Studio 2017 toutefois il n’est pas installé. Le fichier d’installation se trouve dans le répertoire du SDK Windows 10, par exemple:

C:\Program Files\Windows Kits\10\bin\10.0.17134.0\x86\Orca-x86_en-us.msi 

Il faut installer Orca pour être capable de modifier le fichier .MSI.

Après installation, pour modifier le .MSI:

  1. Ouvrir Orca et ouvrir le fichier InstallProject.msi généré précédemment (“file” ⇒ “Ouvrir”).
  2. Dans la partie “File”, il faut modifier les valeurs de la colonne "Component_" pour que toutes les lignes contiennent la valeur de la ligne correspondant à NativeCallee.dll:
  3. Dans la partie “Component”, on doit retrouver 3 lignes. Il ne faut conserver que la ligne dont la valeur de la colonne “Component” correspond à la valeur de la colonne "Component_" de l’étape précédente. On supprime les autres lignes en effectuant un clique droit sur chaque ligne puis “Drop Row”.

    La seule ligne restante devrait se présenter de cette façon:

  4. Dans la partie “FeatureComponents”, il faut, de même, supprimer toutes les lignes exceptée la ligne contenant la valeur dans la colonne "Component_" correspondant à NativeCallee.dll (à l’étape 2).

    La seule ligne restante devrait se présenter de cette façon:

  5. Dans la partie “MsiAssembly”, il faut rajouter une ligne avec le contenu suivant:
    • Component_: indiquer la valeur de la colonne "Component_" de la ligne correspondant à NativeCallee.dll à l’étape 2.
    • Feature_: préciser la valeur: DefaultFeature
    • File_Manifest: indiquer la valeur correspondant à la colonne "File" dans la partie "File" de l’étape 2 pour la ligne correspondant au fichier NativeCallee.manifest.
    • File_Application: ne rien remplir.
    • Attribute: préciser la valeur 1.

    La ligne devrait se présenter de cette façon:

  6. Dans la partie “MsiAssemblyName”, il faut ajouter 5 lignes (en effectuant un clique droit puis “Add Row”) correspondant aux informations obligatoires se trouvant dans le fichier manifest NativeCallee.manifest (name, type, version, processorAttribute et publicKeyToken):
    • La colonne "Component_" de chaque ligne doit contenir la valeur de la colonne "Component_" de la ligne correspondant à NativeCallee.dll à l’étape 2.
    • La colonne "Name" contient le nom de chaque valeur provenant du manifest.
    • La colonne "Value" contient la valeur des informations du manifest.

    Les lignes devraient se présenter de cette façon:

  7. Il faut enregistrer le fichier .MSI en cliquant sur “File” puis “Save”.

Le fichier d’installation .MSI permet de rajouter l’assembly dans le répertoire WinSxS, il suffit de l’exécuter.

Après installation, on peut voir le fichier dans le répertoire WinSxS avec un chemin du type:

C:\Windows\winsxs\x86_nativecallee_3ce09db04e4b62f1_1.0.0.0_none_3f9f7b80072bd670\NativeCallee.dll 

Utiliser l’assembly native dans WinSxS

Pour utiliser l’assembly se trouvant dans le répertoire WinSxS, il faut créer un fichier manifest pour l’exécutable indiquant comment charger l’assembly side-by-side partagée NativeCallee.dll.

Dans un premier temps, on récupère la solution NativeCallee.sln utilisée pour générer les différents projets précédemment:

  1. Pour générer un fichier manifest pour l’exécutable NativeCaller.exe, on effectue un clique droit sur le projet NativeCaller puis on clique sur “Propriétés”.
  2. Dans la partie “Outil Manifeste” (“Manifest Tool“) puis “Entrée et sortie” (“Input and Output“), pour le paramètre “Incorporer le manifeste” (“Embed Manifest“), on sélectionne la valeur “Non”.

Avec ce paramètre, le fichier manifest NativeCaller.exe.manifest sera généré dans le répertoire de sortie.

On édite ce fichier pour rajouter les éléments suivants:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <dependency> 
    <dependentAssembly> 
      <assemblyIdentity type="win32"  
                        name="NativeCallee"  
                        version="1.0.0.0"  
                        processorArchitecture="x86"  
                        publicKeyToken="3ce09db04e4b62f1"  
                        language="*" 
      /> 
    </dependentAssembly> 
  </dependency> 
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> 
    <security> 
      <requestedPrivileges> 
        <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
      </requestedPrivileges> 
    </security> 
  </trustInfo> 
</assembly> 

On précise les éléments qui définissent l’identité de l’assembly native contenant NativeCallee.dll.

On place ensuite les fichiers suivants dans un répertoire séparé:

  • NativeCaller.exe
  • NativeCaller.exe.manifest
  • CallerRedirection.dll
  • NativeCallee.dll

Si on lance l’exécution de NativeCaller.exe, le résultat devrait être le même qu’au début:

Calling other lib: 
Text to display 

Analyser les dépendances avec DependencyWalker

Si on ouvre le fichier NativeCaller.exe avec DependencyWalker, on ne remarque rien de particulier, toutes les dépendances sont présentes:

Si on supprime la DLL NativeCallee.dll, en ouvrant de nouveau NativeCaller.exe avec DependencyWalker, on voit que la dépendance NativeCallee.dll est manquante:

Toutefois si on execute NativeCaller.exe, on a le même résultat que précédemment. La DLL NativeCallee.dll est bien chargée.

Pour s’en convaincre, on peut utiliser la fonctionnalité de profiling de DependencyWalker en cliquant sur “Profile” puis “Start Profiling…” Puis “OK”. Cette fonctionnalité permet de profiler l’exécution pour vérifier comment les dépendances sont chargées. Si on appuie sur [F9] pour afficher les chemins des fichiers, on peut voir que NativeCallee.dll provient de WinSxS:

Sxstrace

Il est possible de débugger le chargement d’assemblies dans le répertoire WinSxS en utilisant l’outil sxstrace. Cet utilitaire se trouve dans un répertoire du type:

C:\Windows\System32\sxstrace.exe. 

Cet outil va permettre de logguer les différentes étapes de chargement du manifest, il est ainsi possible de voir les erreurs éventuelles qui peuvent se produire lors de ce chargement.

Pour lancer la capture de traces avec sxstrace, il faut exécuter la commande:

sxstrace trace –logfile:<chemin du fichier à générer> 

Pendant que sxstrace est en cours de capture, on peut lancer l’exécutable pour scruter les chargements des DLL à partir de WinSxS. Pour arrêter la capture, il suffit d’appuyer sur [Entrée].

Le fichier généré est un fichier binaire. Pour lire le contenu de ce fichier dans un fichier texte, il faut exécuter la commande:

sxstrace parse –logfile:<chemin du fichier capturé> -outfile:<chemin du fichier texte> 

Par exemple dans l’exemple de l’exécutable NativeCaller.exe, on peut lire les informations concernant la DLL NativeCallee.dll:

Début de la génération du contexte d’activation. 
Paramètre d’entrée : 
    Flags = 0 
    ProcessorArchitecture = x86 
    CultureFallBacks = fr-FR;fr 
    ManifestPath = C:\Windows\WinSxS\x86_nativecallee_3ce09db04e4b62f1_1.0.0.0_none_3f9f7b80072bd670\NativeCallee.dll 
    AssemblyDirectory = C:\Windows\WinSxS\x86_nativecallee_3ce09db04e4b62f1_1.0.0.0_none_3f9f7b80072bd670\ 
    Application Config File =  
----------------- 
Information : analyse du fichier manifeste C:\Windows\WinSxS\x86_nativecallee_3ce09db04e4b62f1_1.0.0.0_none_3f9f7b80072bd670\NativeCallee.dll. 
    Information : l’identité de la définition du manifeste est (null). 
Information : réussite de la génération du contexte d’activation. 
Fin de la génération du contexte d’activation. 

Si on modifie dans le fichier manifest NativeCaller.exe.manifest la version de la DLL NativeCallee.dll de façon à faire échouer le chargement:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <dependency> 
    <dependentAssembly> 
      <assemblyIdentity type="win32"  
                        name="NativeCallee"  
                        version="2.0.0.0"
                        processorArchitecture="x86"  
                        publicKeyToken="3ce09db04e4b62f1"  
                        language="*" 
      /> 
    </dependentAssembly> 
  </dependency> 
  <!-- ... --/> 
</assembly> 

On peut observer les différentes tentatives de recherche de la DLL:

Information : analyse du fichier manifeste C:\TEST\NativeCaller.exe.Manifest. 
    Information : l’identité de la définition du manifeste est (null). 
    Information : référence : NativeCallee,language="*",processorArchitecture="x86",publicKeyToken="3ce09db04e4b62f1",type="win32",version="2.0.0.0" 
Information : résolution de la référence NativeCallee,language="*",processorArchitecture="x86",publicKeyToken="3ce09db04e4b62f1",type="win32",version="2.0.0.0". 
    Information : résolution de la référence pour l’architecture ProcessorArchitecture x86. 
        Information : résolution de la référence pour la culture fr-FR. 
            Information : application de la stratégie de liaison. 
                Information : aucune stratégie de serveur de publication trouvée. 
                Information : aucune redirection de la stratégie de liaison trouvée. 
            Information : début de la recherche d’assemblys. 
                Information : impossible de trouver l’assembly dans WinSxS. 
                Information : tentative de recherche du manifeste sur C:\Windows\assembly\GAC_32\NativeCallee\2.0.0.0_fr-FR_3ce09db04e4b62f1\NativeCallee.DLL. 
                Information : manifeste pour la culture fr-FR introuvable. 
            Information : fin de la recherche d’assemblys. 
        Information : résolution de la référence pour la culture fr. 
            Information : application de la stratégie de liaison. 
                Information : aucune stratégie de serveur de publication trouvée. 
                Information : aucune redirection de la stratégie de liaison trouvée. 
            Information : début de la recherche d’assemblys. 
                Information : impossible de trouver l’assembly dans WinSxS. 
                Information : tentative de recherche du manifeste sur C:\Windows\assembly\GAC_32\NativeCallee\2.0.0.0_fr_3ce09db04e4b62f1\NativeCallee.DLL. 
                Information : manifeste pour la culture fr introuvable. 
            Information : fin de la recherche d’assemblys. 
        Information : résolution de la référence pour la culture Neutral. 
            Information : application de la stratégie de liaison. 
                Information : aucune stratégie de serveur de publication trouvée. 
                Information : aucune redirection de la stratégie de liaison trouvée. 
            Information : début de la recherche d’assemblys. 
                Information : impossible de trouver l’assembly dans WinSxS. 
                Information : tentative de recherche du manifeste sur C:\Windows\assembly\GAC_32\NativeCallee\2.0.0.0__3ce09db04e4b62f1\NativeCallee.DLL. 
                Information : tentative de recherche du manifeste sur C:\TEST\NativeCallee.DLL. 
                Information : tentative de recherche du manifeste sur C:\TEST\NativeCallee.MANIFEST. 
                Information : tentative de recherche du manifeste sur C:\TEST\NativeCallee\NativeCallee.DLL. 
                Information : tentative de recherche du manifeste sur C:\TEST\NativeCallee\NativeCallee.MANIFEST. 
                Information : manifeste pour la culture Neutral introuvable. 
            Information : fin de la recherche d’assemblys. 
    Erreur : impossible de résoudre la référence NativeCallee,language="*",processorArchitecture="x86",publicKeyToken="3ce09db04e4b62f1",type="win32",version="2.0.0.0". 
Erreur : échec de la génération du contexte d’activation. 
Fin de la génération du contexte d’activation. 

Pour résumer

En résumé, le répertoire WinSxS est apparu dans Windows XP pour permettre d’enregistrer des dépendances au niveau du système d’exploitation. Ainsi quand une application possède une dépendance enregistrée dans WinSxS, elle est moins facilement déstabilisée si la mise à jour d’une autre application modifie cette dépendance. WinSxS permet d’éviter les problèmes liés au DLL Hell.

On peut enregistrer une dépendance sous forme d’assembly native dans le répertoire WinSxS en utilisant un fichier d’installation ou par programmation. Dans le cas où l’assembly native est une DLL, il faut que la DLL possède un fichier manifest pour indiquer des éléments définissant son identité et l’assembly doit être signée en utilisant un certificat avec une clé de longueur minimum 2048 pour garantir son unicité.

L’enregistrement dans le répertoire WinSxS est fastidieux et de plus en plus souvent, les applications évitent ce type de déploiement en privilégiant l’ajout des dépendances dans le même repertoire que l’exécutable. Cette méthode permet d’isoler les dépendances et de garantir la stabilité de l’application au détriment d’une utilisation de plus de mémoire.

Références

Manifest:

Certificat:

Installateur:

DependencyWalker:

Enregistrement DLL:

Leave a Reply