Partager via


activation Registration-Free des composants COM : procédure pas à pas

 

Steve White
Premier Support pour les développeurs, Microsoft Royaume-Uni

Leslie Muller
Global IT Research & Development, Credit Suisse First Boston

Juillet 2005

Résumé: Le Kit de développement logiciel (SDK) de plateforme Microsoft fait un excellent travail en documentant les sujets des applications isolées et des assemblys côte à côte. Toutefois, tout le monde n’associe pas cette rubrique à celle de l’activation sans inscription des composants COM. COM sans inscription est une fonctionnalité de plateforme très intéressante pour les entreprises avec des serveurs verrouillés et des applications isolés sur des infrastructures partagées. Cet article décrit un exemple pratique d’activation sans inscription d’un composant COM natif par des clients natifs et, via l’interopérabilité COM, par un client managé. (18 pages imprimées)

S’applique à :
   Microsoft Windows Server 2003
   Microsoft Windows XP
   Microsoft .NET Framework version 1.1
   Microsoft Visual Studio .NET 2003
   Microsoft Visual Studio 6.0

Téléchargez l’exemple qui accompagne cet article ,MSDNRegFreeCOM.msi.

Contenu

Introduction
terminologie com Registration-Free
Exécution de l'exemple
Génération du serveur COM Visual C++
Génération du client C++/.NET
Activation Registration-Free
Serveur et client COM Visual Basic 6.0
Appartements incompatibles
Utilisation de l’API de contexte d’activation
Dépannage
Conclusion
En savoir plus

Introduction

COM sans inscription est un mécanisme disponible sur Microsoft Windows XP (SP2 pour . Composants basés sur NET) et plateformes Microsoft Windows Server 2003. Comme son nom l’indique, le mécanisme permet un déploiement facile (par exemple, à l’aide de XCOPY) de composants COM sur une machine sans avoir à les inscrire.

Sur les plateformes cibles, l’une des étapes de l’initialisation d’un processus et de ses modules dépendants consiste à charger tous les fichiers manifeste associés dans une structure de mémoire appelée contexte d’activation. En l’absence d’entrées de Registre correspondantes, il s’agit d’un contexte d’activation qui fournit les informations de liaison et d’activation dont l’exécution COM a besoin. Aucun code spécial n’est requis dans le serveur COM ou dans le client, sauf si vous choisissez d’annuler l’utilisation de fichiers en créant vous-même des contextes d’activation à l’aide de l’API de contexte d’activation.

Dans cette procédure pas à pas, je vais créer un composant COM natif simple et l’utiliser, à la fois inscrit et non inscrit, à partir de clients natifs et managés. Le composant et le client natif seront présentés dans Visual C++ et Visual Basic 6.0 ; le client managé sera présenté dans C# et Visual Basic .NET. Vous pouvez télécharger le code source et les exemples et les voir en action immédiatement, ou vous pouvez suivre la procédure pas à pas et les créer vous-même pas à pas.

terminologie com Registration-Free

Toute personne familiarisée avec la technologie .NET Framework sera habituée au terme assembly, qui désigne un ensemble d’un ou plusieurs modules déployés, nommés et versionnés en tant qu’unité, avec un module contenant un manifeste qui définit l’ensemble. Dans COM sans inscription, les termes assembly et manifest sont empruntés pour les idées qui sont similaires dans le concept mais qui ne sont pas identiques à leurs équivalents .NET.

COM sans inscription utilise l’assembly pour désigner un ensemble d’un ou plusieurs modules PE (c’est-à-dire natifs ou managés) déployés, nommés et versionnés en tant qu’unité. COM sans inscription utilise le manifeste pour faire référence à des fichiers texte avec l’extension .manifest contenant du code XML, qui définit l’identité d’un assembly (manifeste d’assembly), ainsi que les détails de liaison et d’activation de ses classes, ou définit l’identité d’une application (manifeste d’application), ainsi qu’une ou plusieurs références d’identité d’assembly. Un fichier manifeste d’assembly est nommé pour l’assembly et un fichier manifeste d’application est nommé pour l’application.

Le terme assemblys côte à côte (SxS) fait référence à la configuration de différentes versions du même composant COM, via des fichiers manifestes, afin qu’elles puissent être chargées simultanément par différents threads sans avoir à être inscrites. SxS active et est peu synonyme de COM sans inscription.

Exécution de l'exemple

Une fois que vous avez téléchargé et extrait l’exemple de code, vous trouverez un dossier appelé \deployed. Voici la version Visual C++ de l’application cliente (client.exe), son manifeste (client.exe.manifest), la version Visual C++ du serveur COM (SideBySide.dll) et son manifeste (SideBySide.X.manifest). Exécutez client.exe. Le résultat attendu est que client.exe active une instance de SideBySideClass (implémentée dans SideBySide.dll) et affiche le résultat de l’appel de sa méthode Version, qui doit ressembler à « 1.0.0-CPP ».

Génération du serveur COM Visual C++

Étape 1

La première étape consiste à créer un serveur COM. Dans Visual Studio, créez un projet ATL Visual C++ et appelez-le SideBySide. Dans l’Assistant Projet ATL, sous l’onglet Paramètres de l’application, désélectionnez la zone Case activée attributs et sélectionnez la zone Autoriser la fusion du code proxy/stub case activée.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le nœud du projet et choisissez Ajouter | Ajouter une classe.... Sélectionnez OBJET SIMPLE ATL, puis choisissez Ouvrir. Donnez à la classe un nom court de SideBySideClass , puis cliquez sur Terminer.

Dans l’affichage de classes, cliquez avec le bouton droit sur le nœud ISideBySideClass et choisissez Ajouter | Add, méthode.... Dans l’Assistant Ajouter une méthode, entrez Version comme nom de la méthode, choisissez un type de paramètre BSTR*, entrez pVer comme nom de paramètre, sélectionnez la zone retval case activée, cliquez sur Ajouter, puis sur Terminer.

Dans l’affichage de classes, développez le nœud CSideBySideClass et double-cliquez sur la méthode Version . Remplacez la ligne // TODO par :

*pVer = SysAllocString(L"1.0.0-CPP");

Produisez une build de mise en production et copiez\release\SideBySide.dll dans \deployed.

Génération du client C++/.NET

L’étape suivante consiste à générer le client. Dans cette partie de la procédure pas à pas, vous avez la possibilité de générer un client Visual C++ ou .NET pour le serveur COM Visual C++. Il est inutile de dire qu’il est possible de combiner des clients et des serveurs écrits en Visual C++, Visual Basic 6.0 et .NET. Si vous le souhaitez, vous trouverez les échantillons trivials à modifier pour qu’ils fonctionnent ensemble. Les ensembles de clients et de serveurs sont organisés tels qu’ils sont dans cette procédure pas à pas afin de présenter du code qui fonctionne tel quelle.

Étape 2 (Option A : Visual C++)

Créez un projet de console Win32 Visual C++ appelé client dans un dossier frère relatif au dossier du projet SideBySide . Dans l’Assistant Application Win32, sous l’onglet Paramètres de l’application, case activée la zone Ajouter la prise en charge d’ATL case activée.

Modifiez stdafx.h et ajoutez la ligne suivante en haut du fichier, immédiatement après :#pragma once

#define _WIN32_DCOM

Dans stdafx.h , ajoutez également la ligne suivante en bas du fichier :

#import "..\deployed\SideBySide.dll" no_namespace

Remplacez le contenu de client.cpp par ce code :

#include "stdafx.h"
#include <iostream>
using namespace std;

void ErrorDescription(HRESULT hr)
{
    TCHAR* szErrMsg;
    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
      FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
      (LPTSTR)&szErrMsg, 0, NULL) != 0)
   {
        cout << szErrMsg << endl;
        LocalFree(szErrMsg);
    }
   else
        cout << "Could not find a description for error 0x" 
          << hex << hr << dec << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
   CoInitializeEx(0, COINIT_MULTITHREADED);

   {
      ISideBySideClassPtr ptr;
      HRESULT hr = ptr.CreateInstance(__uuidof(SideBySideClass));
      if (SUCCEEDED(hr))
      {
         cout << ptr->Version() << endl;
      }
      ErrorDescription(hr);

      char c;
      cin >> c;
   }

   CoUninitialize();

   return 0;
}

Produisez une build de mise en production et copiez\release\client.exe dans \deployed.

Étape 2 (Option B : .NET Framework)

Dans Visual Studio .NET 2003, créez une application console C# ou Visual Basic .NET appelée client dans un dossier frère relatif au dossier du projet SideBySide . Ajoutez une référence à la bibliothèque de types SideBySide 1.0 à partir de l’onglet COM de la boîte de dialogue Ajouter une référence.

Collez le code suivant dans la méthode Main :

code C#

   SideBySideLib.ISideBySideClass obj = 
        new SideBySideLib.SideBySideClassClass();
   Console.WriteLine(obj.Version());
   Console.ReadLine();

Code Visual Basic .NET

    Dim obj As SideBySideLib.ISideBySideClass = 
      New SideBySideLib.SideBySideClassClass
    Console.WriteLine(obj.Version())
    Console.ReadLine()

Produisez une build de mise en production et copiezclient.exe dans \deployed.

Étape 3 :

À l’heure actuelle, le dossier \deployed doit contenir, en dehors de certains fichiers intermédiaires, uniquement client.exe et SideBySide.dll, et ce dernier aura été inscrit par son processus de génération. Pour case activée que votre serveur et votre client fonctionnent ensemble dans ces circonstances normales, exécutez \deployed\client.exe et notez la sortie attendue « 1.0.0-CPP ».

Étape 4

Cette procédure pas à pas concerne COM sans inscription . Nous devons donc annuler l’inscription de l’assembly SideBySide . À l’invite de commandes, accédez au dossier \deployed et exécutez la commande : regsvr32 /u SideBySide.dll.

Étape 5

Pour voir l’effet de l’étape précédente, réexécutez\deployed\client.exe et le message « Classe non inscrite » s’affiche. À ce stade, nous avons frustré le runtime COM de trouver les informations dont il a besoin dans le registre, mais nous n’avons pas encore mis ces informations à disposition par d’autres moyens. Nous y remédierons dans les étapes suivantes.

Activation Registration-Free

Étape 6

Dans le dossier \deployed , créez un fichier manifeste d’application (un fichier texte) pour l’application client.exe et appelez-le client.exe.manifest. Collez ce qui suit dans le fichier :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

Étape 7

Dans le dossier \déployé , créez un fichier manifeste d’assembly privé (un fichier texte) pour le composant SideBySide.dll et appelez-le SideBySide.X.manifest. Collez ce qui suit dans le fichier :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

J’ai écrit les valeurs GUID, qui seront propres à votre projet, sous la forme d’espaces réservés. Les valeurs respectives de ces espaces réservés se trouvent comme suit, soit dans le fichier SideBySide.idl du projet SideBySide, soit en ouvrant SideBySide.dll dans l’outil OLE/COM ObjectViewer (Oleview.exe).

[
   object,
   uuid([IID_ISideBySideClass]),
   dual,
   nonextensible,
   helpstring("ISideBySideClass Interface"),
   pointer_default(unique)
]
interface ISideBySideClass : IDispatch{
   [id(1), helpstring("method Version")] HRESULT 
        Version([out,retval] BSTR* pVer);
};
[
   uuid([LIBID_SideBySide]),
   version(1.0),
   helpstring("SideBySide 1.0 Type Library")
]
library SideBySideLib
{
   importlib("stdole2.tlb");
   [
      uuid([CLSID_SideBySideClass]),
      helpstring("SideBySideClass Class")
   ]
   coclass SideBySideClass
   {
      [default] interface ISideBySideClass;
   };
};

Étape 8

À ce stade, je devrais aborder le sujet de l’incorporation de fichiers manifeste d’assembly en tant que ressources Win32. Lorsque les développeurs sont à la fois capables et disposés à reconstruire leurs composants COM, il est conseillé qu’un manifeste d’assembly (comme celui créé à l’étape précédente) soit incorporé dans la DLL COM en tant que ressource Win32 de type RT_MANIFEST (défini dans windows.h). Dans les cas où cela n’est pas possible, veillez à donner à votre assembly (et par conséquent à votre manifeste d’assembly) un nom différent de celui du nom de fichier de la DLL COM. Ainsi, dans le cas ci-dessus, la DLL COM est appelée SideBySide , mais l’assembly est appelé SideBySide.X. Si la raison de cette contrainte vous intéresse, elle est expliquée dans la section Résolution des problèmes . Dans cette procédure pas à pas, le manifeste d’assembly n’est pas incorporé pour refléter les nombreux cas réels dans lesquels cela n’est pas possible.

Étape 9

Pour vérifier que, grâce aux fichiers manifestes, votre client est à nouveau en mesure d’activer la classe SideBySideClass , d’exécuter\deployed\client.exe et de noter la sortie attendue « 1.0.0-CPP ».

Serveur et client COM Visual Basic 6.0

Étape 1

La première étape consiste à créer un serveur COM. Créez un projet de DLL ActiveX Visual Basic 6.0. Dans le Explorer projet, sélectionnez le nœud Project1 et, dans la fenêtre Propriétés, remplacez son nom par SideBySide. Dans le projet Explorer sélectionnez le nœud Class1 et, dans la fenêtre Propriétés, remplacez son nom par SideBySideClass.

Collez la sous-routine suivante dans la fenêtre Code :

Public Function Version()
Version = "1.0.0-VB6"
End Function

Choisir fichier | Faites SideBySide.dll... accédez au dossier \déployé , puis cliquez sur OK. Pour empêcher la génération de nouveaux GUID chaque fois que vous créez la dll, choisissez Projet | Propriétés SideBySide..., puis cliquez sur l’onglet Composant et, dans le groupe Compatibilité des versions, sélectionnez la case d’option Compatibilité binaire .

Étape 2

Créez un projet VISUAL Basic 6.0 Standard EXE. Dans project Explorer sélectionnez le nœud Project1 et, dans la fenêtre Propriétés, remplacez son nom par client. Choisir fichier | Enregistrez project as et enregistrez le fichier formulaire et le fichier projet dans un dossier frère relatif au dossier du projet SideBySide . Choisissez Projet | Références, sélectionnez la zone case activée en regard de SideBySide, puis cliquez sur OK.

Double-cliquez sur le formulaire main dans le concepteur de formulaires et collez le code suivant dans Sub Form_Load() :

    Dim obj As New SideBySideClass
    MsgBox obj.Version()

Choisir fichier | Faites client.exe... accédez au dossier \déployé , puis choisissez OK.

Étape 3 :

À l’heure actuelle, le dossier \déployé ne doit contenir, à l’exception de certains fichiers intermédiaires, que client.exe et SideBySide.dll; et ce dernier aura été inscrit par son processus de génération. Pour case activée que votre serveur et votre client fonctionnent ensemble dans ces circonstances normales, exécutez \deployed\client.exe et notez la sortie attendue « 1.0.0-VB6 ».

Étape 4

Cette procédure pas à pas concerne COM sans inscription . Nous devons donc annuler l’inscription de l’assembly SideBySide . À l’invite de commandes, accédez au dossier \déployé et exécutez la commande : regsvr32 /u SideBySide.dll.

Étape 5

Pour voir l’effet de l’étape précédente, réexécutez \deployed\client.exe et vous verrez le message « Erreur d’exécution '429' : le composant ActiveX ne peut pas créer d’objet ». À ce stade, nous avons empêché le runtime COM de trouver les informations dont il a besoin dans le registre, mais nous n’avons pas encore mis ces informations à disposition par d’autres moyens. Nous y remédierons dans les étapes suivantes.

Étape 6

Dans le dossier \déployé , créez un fichier manifeste d’application (un fichier texte) pour l’application client.exe et appelez-leclient.exe.manifest. Collez ce qui suit dans le fichier :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

Étape 7

Dans le dossier \déployé , créez un fichier manifeste d’assembly privé (un fichier texte) pour le composant SideBySide.dll et appelez-le SideBySide.X.manifest. Collez ce qui suit dans le fichier :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="_SideBySideClass" 
    iid="{[IID__SideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

J’ai écrit les valeurs GUID, qui seront propres à votre projet, sous la forme d’espaces réservés. Pour obtenir les valeurs respectives de ces espaces réservés, ouvrez SideBySide.dll dans l’outil Ole/COM ObjectViewer (Oleview.exe).

[
  uuid([LIBID_SideBySide]),
  version(1.0),
  custom(50867B00-BB69-11D0-A8FF-00A0C9110059, 8169)

]
library SideBySide
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-
C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _SideBySideClass;

    [
      odl,
      uuid([IID__SideBySideClass]),
      version(1.0),
      hidden,
      dual,
      nonextensible,
      oleautomation
    ]
    interface _SideBySideClass : IDispatch {
        [id(0x60030000)]
        HRESULT Version([out, retval] VARIANT* );
    };

    [
      uuid([CLSID_SideBySideClass]),
      version(1.0)
    ]
    coclass SideBySideClass {
        [default] interface _SideBySideClass;
    };
};

Étape 8

Pour vérifier que, grâce aux fichiers manifestes, votre client est à nouveau en mesure d’activer la classe SideBySideClass , d’exécuter \deployed\client.exe et de noter la sortie attendue « 1.0.0-VB6 ».

Appartements incompatibles

Tous les serveurs COM de cette procédure pas à pas sont conçus pour s’exécuter dans un Single-Threaded Apartment (c’est-à-dire, il s’agit de composants STA ou apartment-threaded). Tous les clients sont STA, à l’exception du client C++, qui est MTA (son thread s’exécute dans un appartement multithread). Il existe donc un cas dans lequel le client et le serveur vivent dans des appartements incompatibles, et dans ce cas, le marshaling inter-appartements des appels COM doit avoir lieu entre un proxy et un stub. C’est ce cas qui utilise la section suivante du fichier manifeste d’assembly :

...
<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />
...

Ces éléments fournissent des informations qui seraient sinon présentes dans le registre. L’élément comInterfaceExternalProxyStub fournit suffisamment d’informations pour que le marshaling de bibliothèque de types se produise et il convient pour les interfaces COM qui dérivent d’IDispatch (qui inclut toutes les interfaces Automation). Dans ce cas ,ole32.dll fournit le stub de proxy externe utilisé (c’est-à-dire externe aux fichiers de l’assembly). Si vos composants COM implémentent uniquement des interfaces de répartition ou doubles , il s’agit de l’élément que vous devez utiliser.

Les interfaces personnalisées sont un peu plus rares et pour celles-ci, vous devez faire un peu plus de travail. Considérez une interface appelée ISxSCustom qui dérive directement d’IUnknown et n’est pas compatible avec Automation. Pour que SideBySideClass implémente ISxSCustom et que les clients appellent ses méthodes dans un scénario sans inscription, je dois ajouter un élément comInterfaceProxyStub à mon manifeste d’assembly. Comme suggéré par le nom de l’élément, cette fois le proxy-stub n’est pas externe : il est fourni soit par SideBySide.dll (si j’ai coché la case Autoriser la fusion du code proxy/stub case activée dans l’Assistant Projet ATL) soit dans SideBySidePS.dll. Si mon proxy-stub est fusionné, le nouvel élément est un enfant de l’élément de fichier existant :

<file name = "SideBySide.dll">
   ...
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

Sinon, je dois ajouter un autre élément de fichier déclarant la dll proxy-stub :

<file name = "SideBySide.dll"> ... </file>
<file name = "SideBySidePS.dll">
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

Si je n’avais pas eu besoin d’illustrer les éléments décrits ci-dessus , j’aurais créé le serveur COM Visual C++ de sorte que ses coclasses aient été activées dans le MTA du client. Dans le cas où le client et le serveur se trouvent dans le même appartement, les éléments qui viennent d’être abordés sont ignorés. Cependant, je vous encourage à ne pas dépendre de cette circonstance, car il peut y avoir des situations hors de votre contrôle où un composant est activé dans un appartement différent de son client, même si leurs modèles de threading sont cohérents. Compte tenu de l’effort relativement faible requis, je pense qu’il est recommandé d’inclure toujours la configuration proxy-stub dans vos manifestes.

Utilisation de l’API de contexte d’activation

Dans la section Introduction , j’ai mentionné que l’une des étapes de l’initialisation du processus d’une application, sur les plateformes auxquelles s’applique cet article, consiste à localiser un fichier manifeste d’application. Le fichier manifeste de l’application contient des références aux assemblys sur lesquels l’application a des dépendances. Dans le cas de l’activation sans inscription de composants COM natifs, ce qu’on entend par assembly est un ensemble d’une ou plusieurs DLL COM collectées sous une identité et une version communes.

Si votre application connaît a priori l’ensemble potentiel de coclasses à activer directement ou indirectement pendant sa durée de vie, les manifestes d’application sont pratiques. Mais il existe une classe d’application relativement rare (par exemple, les serveurs de grille) qui, par conception, ne connaissent pas les modules qu’ils vont charger avant l’exécution. Dans ce cas, une méthode de référence des fichiers manifeste d’assembly après l’initialisation du processus est appelée. Cela est satisfait par l’API de contexte d’activation, dont l’utilisation évite un fichier manifeste d’application. La technique la plus simple consiste à initialiser une structure ACTCTX avec l’emplacement du fichier manifeste d’assembly, puis à créer et à activer un contexte d’activation à partir de celle-ci. Les instructions suivantes supposent que vous avez déjà généré l’application cliente Visual C++ décrite à l’étape 2 (option A).

Modifiez stdafx.h et ajoutez la ligne suivante immédiatement après votre définition de _WIN32_DCOM:

#define _WIN32_FUSION 0x0100 // this causes activation context 
structs and APIs to be included.

Si vous examinez la fonction _tmain dans client.cpp, entre les appels d’initialisation COM et de non initialisation, vous verrez une instruction composée qui active et appelle sideBySideClass. Nous devons déplacer cette instruction composée (tout entre les accolades) à l’intérieur d’une nouvelle section de code qui initialise le contexte d’activation comme suit :

   ACTCTX actCtx;
   memset((void*)&actCtx, 0, sizeof(ACTCTX));
   actCtx.cbSize = sizeof(ACTCTX);
   actCtx.lpSource = "SideBySide.X.manifest";

   HANDLE hCtx = ::CreateActCtx(&actCtx);
   if (hCtx == INVALID_HANDLE_VALUE)
      cout << "CreateActCtx returned: INVALID_HANDLE_VALUE" 
             << endl;
   else
   {
      ULONG_PTR cookie;
      if (::ActivateActCtx(hCtx, &cookie))
      {
         // previous compound statement goes here...
         ::DeactivateActCtx(0, cookie);
      }
   }

Le code ci-dessus s’exécute avant l’activation des coclasses. Le code lit simplement le fichier manifeste d’assembly (dont le nom est codé en dur dans cet exemple, mais doit correspondre à l’assembly que vous souhaitez charger dynamiquement) dans un contexte d’activation qu’il active ensuite (c’est-à-dire, rend actif). À partir de ce point, l’activation des coclasses se produit comme avant. Vous pouvez maintenant ignorer client.exe.manifest.

L’objet Microsoft.Windows.ActCtx constitue une alternative à l’API de contexte d’activation directe, mais disponible uniquement sur Windows Server 2003.

Inutile de dire que l’API de contexte d’activation est bien plus importante que ce que j’ai montré ici et vous trouverez un lien vers la documentation complète de l’API dans la section Lecture supplémentaire .

Dépannage

Comme nous l’avons vu, l’activation sans inscription des composants COM ne nécessite aucun code spécial dans le serveur ou dans le client. Tout ce qui est nécessaire est une paire de fichiers manifeste correspondants.

Je vous suggère d’aborder votre propre développement sans inscription comme le fait cette procédure pas à pas. Plus précisément : accédez d’abord à un état connu en voyant votre client travailler avec un serveur inscrit ; annulez ensuite l’inscription du serveur et vérifiez que votre message d’erreur correspond à ce que vous attendiez ; enfin, corrigez la situation en créant et en déployant des fichiers manifestes. De cette façon, vos efforts de résolution des problèmes liés à l’activation sans inscription seront limités à la structure de vos fichiers manifestes (et à l’incorporation correcte du manifeste d’assembly si vous choisissez de le faire).

Lors de la résolution des problèmes COM sans inscription, le observateur d'événements sur Windows Server 2003 est votre ami. Lorsque Windows XP ou Windows Server 2003 détecte une erreur de configuration, il affiche généralement une zone de message d’erreur intitulée pour l’application que vous avez lancée et contenant le message « Cette application n’a pas pu démarrer, car la configuration de l’application est incorrecte. La réinstallation de l’application peut résoudre ce problème. » Je conseille que chaque fois que vous voyez ce message que vous reproduisez le problème sur Windows Server 2003, consultez le journal des événements système et recherchez les événements de la source SideBySide . La raison pour laquelle je ne suggère pas que vous examiniez le journal des événements Windows XP dans ces cas est qu’il contiendra invariablement un message tel que « Générer le contexte d’activation a échoué pour [chemin]\[nom de fichier de l’application]. Manifeste. Message d’erreur de référence : L’opération s’est terminée correctement », ce qui ne permet pas d’identifier le problème.

Les schémas des différents fichiers manifeste sont documentés dans le Kit de développement logiciel (SDK) de plateforme sous le titre Informations de référence des fichiers manifestes, et l’outil de validation de schémaManifestchk.vbs est disponible. Par conséquent, je n’appelle ici que quelques points pertinents pour la procédure pas à pas. Examinons d’abord le fichier manifeste d’assembly. Pour obtenir un exemple, revenez à l’étape 7.

Vous vous souviendrez que, dans le sens COM libre d’inscription , un assembly est une idée abstraite à laquelle vous associez un ou plusieurs fichiers physiques au moyen du contenu du fichier manifeste d’assembly . Le nom de l’assembly apparaît à trois emplacements et doit être identique dans chacun d’eux : dans l’attribut name de l’élément assemblyIdentity du fichier manifeste d’assembly ; dans l’attribut name de l’élément dependentAssembly/assemblyIdentity du fichier manifeste d’application et le nom du fichier manifeste d’assembly lui-même, à l’exclusion de l’extension .manifest . Si le nom du fichier manifeste ne correspond pas au nom du manifeste de l’application , le message suivant s’affiche dans le journal des événements système Windows Server 2003 : « L’assembly dépendant [valeur de l’attribut de nom dans le manifeste de l’application] est introuvable et la dernière erreur est L’assembly référencé n’est pas installé sur votre système ». Si l’élément name dans le manifeste d’assembly est incorrect, le message suivant s’affiche dans le journal des événements système Windows Server 2003 : « L’identité du composant trouvé dans le manifeste ne correspond pas à l’identité du composant demandé ».

Si SideBySide.dll avait été un composant .NET Framework, nous aurions été obligés d’incorporer le fichier manifeste d’assembly dans l’assembly SideBySide en tant que ressource Win32 (et nous aurions nommé le fichier manifeste après l’assembly .NET, c’est-à-dire SideBySide.manifest). Toutefois, en raison de la séquence de recherche du chargeur d’assembly, pour les composants COM natifs, il est facultatif d’incorporer le manifeste dans le module. Avant que le chargeur d’assembly recherche [AssemblyName].manifest, il recherche [AssemblyName].dll et y recherche une ressource Win32 de type RT_MANIFEST. La configuration à l’intérieur de la ressource doit avoir un élément AssemblyIdentity qui correspond à [AssemblyName] ainsi que les autres attributs dans la référence AssemblyIdentity du manifeste de l’application.

Toutefois, si [AssemblyName].dll est trouvé mais qu’il ne contient pas de manifeste correspondant, le mécanisme de chargement d’assembly s’arrête et ne continue pas à rechercher [AssemblyName].manifest. Au moment de la rédaction de cet article, cela est vrai pour le chargeur d’assemblys sur Windows XP (qui, dans ce cas, affiche son message habituel indiquant que « la configuration de l’application est incorrecte »), mais pas sur Windows Server 2003. Sur Windows Server 2003, la recherche sepoursuit et recherche le fichier manifeste même s’il correspond au nom d’un module. Cependant, je vous invite à ne pas dépendre de ce comportement. Au lieu de cela, pour vous assurer une prise en charge cohérente des deux plateformes, je vous recommande d’incorporer votre manifeste d’assembly dans votre assembly en tant que ressource RT_MANIFEST chaque fois que cela est possible. Lorsque cela n’est pas possible, vous devez donner à votre fichier manifeste d’assembly un nom différent de celui de n’importe quel module dans le même dossier. Vous pouvez également placer votre composant COM dans un dossier enfant du dossier du manifeste d’assembly et référencer ce chemin d’accès enfant dans l’élément fichier du manifeste d’assembly.

L’élément assemblyIdentity définit l’identité de l’assembly. Pour les composants COM natifs, ni son nom ni son attribut de version ne doivent correspondre à celui d’un fichier physique, bien qu’il soit judicieux d’appliquer une certaine cohérence.

L’élément file est le parent des éléments comClass, typelib et comInterfaceProxyStub. Son objectif est de localiser les fichiers physiques qui composent l’assembly. Si l’attribut name de l’élément file ne référence pas correctement les fichiers dans le système de fichiers, CoCreateInstance retourne un HRESULT correspondant à « Classe non inscrite » ou « Le module spécifié est introuvable ». Pour pouvoir installer différentes versions de votre composant COM dans différents dossiers, l’attribut name peut inclure un chemin d’accès. Sur Windows XP SP2, le chemin d’accès peut être relatif ou absolu et peut référencer un dossier n’importe où dans le système de fichiers. Windows Server 2003 nécessite le chemin d’accès pour faire référence à la racine de l’application ou à un dossier enfant. Si vous essayez de violer cette règle, le message suivant s’affiche dans le journal des événements système Windows Server 2003 : « Erreur de syntaxe dans le manifeste ou le fichier de stratégie [nom du manifeste d’assembly] [...] La valeur [...] n’est pas valide. »

L’élément comClass n’a qu’un seul attribut obligatoire : clsid. Si l’application tente d’activer une coclasse non enregistrée dont le CLSID n’est pas répertorié dans un élément comClass dans le manifeste d’assembly, CoCreateInstance retourne un HRESULT avec la valeur REGDB_E_CLASSNOTREG (0x80040154), le texte du message pour lequel est « Classe non inscrite ».

Comme je l’ai dit, les éléments typelib et comInterface[External]ProxyStub sont requis dans le cas où le client et le serveur existent dans des appartements différents, de sorte que les erreurs suivantes ne peuvent être visibles que lorsque ces éléments sont traités. Les erreurs de configuration dans ces éléments entraînent le retour d’un HRESULT qui correspond aux messages « Bibliothèque non inscrite », « Erreur de chargement de type bibliothèque/DLL » ou « aucune interface de ce type prise en charge ». Si vous voyez l’un de ces messages, case activée vos GUID et vérifiez que tous les attributs obligatoires sont présents. Vous trouverez le schéma du fichier manifeste dans le Kit de développement logiciel (SDK) de plateforme.

Nous allons maintenant nous intéresser au fichier manifeste de l’application. Pour obtenir un exemple, revenez à l’étape 6. Le manifeste de l’application doit être nommé au format [nom_fichier de l’application].manifest. Ainsi, dans la procédure pas à pas, il a été nommé client.exe.manifest pour qu’il soit clair qu’il doit être lu chaque fois que client.exe est chargé dans un processus. Si cette opération n’est pas effectuée correctement, CoCreateInstance retourne un HRESULT avec la valeur REGDB_E_CLASSNOTREG (0x80040154), le texte du message pour lequel est « Classe non inscrite ».

L’élément le plus important dans le manifeste d’application est l’élémentassemblyIdentitydependentAssembly/. Cet élément est une référence à l’équivalent dans le manifeste de l’assembly et les deux doivent correspondre exactement. Un bon moyen de s’assurer qu’ils le font est de copier l’élément à partir du manifeste de l’assembly et de le coller ici. En cas de différence, le message suivant s’affiche dans le journal des événements système Windows Server 2003 : « L’identité du composant trouvé dans le manifeste ne correspond pas à l’identité du composant demandé ».

Conclusion

COM sans inscription est une technologie qui libère les composants COM d’une dépendance vis-à-vis du registre Windows et, par conséquent, libère les applications qui les utilisent d’avoir besoin de serveurs dédiés. Il permet aux applications ayant des dépendances sur différentes versions du même composant COM de partager une infrastructure et de charger ces différentes versions de composants COM côte à côte dans un écho du mécanisme de contrôle de version et de déploiement .NET Framework.

Cet article vous guide tout au long d’une démonstration de l’activation sans inscription de composants COM natifs par des applications clientes natives écrites dans Visual C++ et Visual Basic 6.0 et par un client managé. Il explique certains des fonctionnements du mécanisme et souligne certaines erreurs de configuration possibles et comment les résoudre.

En savoir plus

 

À propos de l’auteur

Steve White est consultant en développement d’applications au sein de l’équipe Premier Support for Developers de Microsoft UK. Il prend en charge le développement de clients avec Visual C#, Windows Forms et ASP.NET. Son blog contient plus d’informations sur ses intérêts dans la musique, les visualisations et la programmation.

Leslie Muller est technologue au sein de l’équipe de développement de la recherche & au Credit Suisse First Boston. Leslie a 12 ans d’expérience en tant que développeur et architecte technique, travaillant dans des environnements tels que les services financiers, les start-ups technologiques, l’automatisation industrielle et la défense. Lorsqu’il ne programme pas ou ne fait pas de recherche, il aime le ski, le hockey sur glace et, si possible, faire des choses un peu folles avec des véhicules motorisés dans des environnements extrêmes comme l’Islande ou les Rocheuses.