Développer un module C\C++ natif pour IIS 7.0
Auteur : Mike Volodarsky
Introduction
IIS 7.0 et versions ultérieures permet d’étendre le serveur par des modules développés de deux manières :
- Utilisation du code managé et des API d’extensibilité du serveur ASP.NET
- Utilisation du code natif et des API d’extensibilité du serveur natif IIS
Contrairement aux versions précédentes d'IIS, la majorité des scénarios d’extensibilité du serveur ne nécessitent pas de développement de code natif (C++) et peuvent être pris en charge à l’aide du code managé et des API ASP.NET. L’utilisation de ASP.NET pour étendre le serveur vous permet de réduire considérablement le temps de développement et de tirer parti des fonctionnalités enrichies de ASP.NET et du .NET Framework. Pour en savoir plus sur l’extension d'IIS avec ASP.NET, consultez Développement d’un module IIS avec .NET.
IIS fournit également une API de serveur native (C++), qui remplace le filtre ISAPI et l’API d’extension des versions précédentes d'IIS. Si vous avez des exigences spécifiques qui demandent le développement de code natif, ou si vous souhaitez convertir vos composants ISAPI natifs existants, tirez parti de cette API pour générer des composants de serveur. La nouvelle API serveur native propose un développement orienté objet avec un modèle objet intuitif, offre un contrôle accru sur le traitement des demandes et utilise des modèles de conception plus simples pour vous aider à écrire du code robuste.
Cette procédure pas à pas examine les tâches suivantes :
- Développement d’un module natif à l’aide de l’API de serveur native (C++)
- Déploiement d’un module natif sur le serveur
Pour compiler le module, vous devez installer le Kit de développement logiciel (SDK) platform qui contient les fichiers d’en-tête IIS. Le kit SDK de plateforme Windows Vista le plus récent est disponible ici.
Pour utiliser le Kit de développement logiciel (SDK) de plateforme avec Visual Studio 2005, vous devez inscrire le Kit de développement logiciel (SDK). Une fois que vous avez installé le Kit de développement logiciel (SDK), procédez comme suit via Démarrer > Programmes > Microsoft Windows SDK > Visual Studio Registration > Inscrire des répertoires du SDK Windows auprès de Visual Studio.
Le code source de ce module est disponible dans l’exemple de module natif Microsoft Visual Studio IIS7.
Développer un module natif
Dans cette tâche, nous examinons le développement d’un module natif à l’aide de la nouvelle API de serveur native (C++). Un module natif est une DLL Windows qui contient les éléments suivants :
- Fonction d’exportation RegisterModule. Cette fonction est chargée de créer une fabrique de modules et d’inscrire le module pour un ou plusieurs événements de serveur.
- Implémentation de la classe de module hérite de la classe de base CHttpModule. Cette classe fournit les principales fonctionnalités de votre module.
- Implémentation de la classe de fabrique de module implémentant l’interface IHttpModuleFactory. La classe est chargée de créer des instances de votre module.
Remarque
Dans certains cas, vous pouvez également implémenter l’interface IGlobalModule afin d’étendre certaines fonctionnalités du serveur qui ne sont pas liées au traitement des demandes. Il s’agit d’une rubrique avancée et n’est pas abordée dans cette procédure pas à pas.
Votre module natif a le cycle de vie suivant :
Lorsque le processus de travail du serveur démarre, il charge la DLL contenant votre module et appelle sa fonction RegisterModule exportée. Dans cette fonction, vous procédez comme suit :
a. Créez la fabrique de modules.
b. Inscrivez la fabrique de modules pour les événements de pipeline de requête que votre module implémente.Lorsqu’une demande arrive, le serveur :
a. Crée une instance de votre classe de module à l’aide de la fabrique que vous avez fournie.
b. Appelle la méthode de gestionnaire d’événements appropriée sur l’instance de module pour chacun des événements de requête auxquels vous avez inscrit.
c. Supprime l’instance du module à la fin du traitement des demandes.
Maintenant, pour le générer.
Le code source complet du module est disponible dans l’exemple de module natif Microsoft Visual Studio IIS7. Les étapes ci-dessous sont les plus importantes pour le développement du module et n’incluent pas la prise en charge du code et de la gestion des erreurs.
Implémentez la fonction RegisterModule que le serveur appelle lorsque la DLL du module est chargée. Sa signature et le reste de l’API native sont définis dans le fichier d’en-tête httpserv.h, qui fait partie du Kit de développement logiciel (SDK) de plateforme (si vous n’avez pas le Kit de développement logiciel (SDK) de plateforme, consultez la présentation pour plus d’informations sur l’obtention de celui-ci) :
main.cpp :
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pHttpServer
)
{
// step 1: save the IHttpServer and the module context id for future use
g_pModuleContext = pModuleInfo->GetId();
g_pHttpServer = pHttpServer;
// step 2: create the module factory
pFactory = new CMyHttpModuleFactory();
// step 3: register for server events
hr = pModuleInfo->SetRequestNotifications( pFactory,
RQ_ACQUIRE_REQUEST_STATE,
0 );
}
Le RegisterModule
Il existe trois tâches de base à accomplir dans RegisterModule :
Enregistrer l’état global
Nous allons stocker l’instance de serveur globale et l’identifiant de contexte du module pour une utilisation ultérieure dans des variables globales. Bien que cet exemple n’utilise pas ces informations, de nombreux modules trouvent qu’il est utile d’enregistrer et d’utiliser ultérieurement pendant le traitement des demandes. L’interface IHttpServer permet d’accéder à de nombreuses fonctions serveur, telles que l’ouverture de fichiers et l’accès au cache. L’identifiant de contexte du module est utilisé pour associer l’état du module personnalisé à plusieurs objets serveur, tels que la demande et l’application.
Créer une fabrique de modules
Nous allons implémenter notre classe d’usine, CMyHttpModuleFactory, plus loin dans cette procédure pas à pas. Cette fabrique est responsable des instances de fabrication de notre module pour chaque demande.
Inscrire la fabrique de modules pour les événements de traitement des demandes souhaités
L’inscription est effectuée via la méthode SetRequestNotificatons, qui indique au serveur : créer notre instance de module pour chaque requête à l’aide de la fabrique spécifiée ; et appeler les gestionnaires d’événements appropriés sur celui-ci pour chacune des étapes de traitement des demandes spécifiées.
Dans ce cas, nous ne sommes intéressés que par la phase RQ_ACQUIRE_REQUEST_STATE. La liste complète des étapes qui composent le pipeline de traitement des demandes est définie dans httpserv.h :
#define RQ_BEGIN_REQUEST 0x00000001 // request is beginning
#define RQ_AUTHENTICATE_REQUEST 0x00000002 // request is being authenticated
#define RQ_AUTHORIZE_REQUEST 0x00000004 // request is being authorized
#define RQ_RESOLVE_REQUEST_CACHE 0x00000008 // satisfy request from cache
#define RQ_MAP_REQUEST_HANDLER 0x00000010 // map handler for request
#define RQ_ACQUIRE_REQUEST_STATE 0x00000020 // acquire request state
#define RQ_PRE_EXECUTE_REQUEST_HANDLER 0x00000040 // pre-execute handler
#define RQ_EXECUTE_REQUEST_HANDLER 0x00000080 // execute handler
#define RQ_RELEASE_REQUEST_STATE 0x00000100 // release request state
#define RQ_UPDATE_REQUEST_CACHE 0x00000200 // update cache
#define RQ_LOG_REQUEST 0x00000400 // log request
#define RQ_END_REQUEST 0x00000800 // end request
En outre, vous pouvez vous abonner à plusieurs événements non déterministes qui peuvent se produire pendant le traitement des requêtes en raison d’actions effectuées par d’autres modules, telles que le vidage de la réponse au client :
#define RQ_CUSTOM_NOTIFICATION 0x10000000 // custom notification
#define RQ_SEND_RESPONSE 0x20000000 // send response
#define RQ_READ_ENTITY 0x40000000 // read entity
#define RQ_MAP_PATH 0x80000000 // map a url to a physical path
Pour que notre implémentation RegisterModule soit accessible au serveur, nous devons l’exporter. Utiliser un fichier DEF qui contient le mot clé EXPORTE pour exporter notre fonction RegisterModule.
Ensuite, implémentez la classe de fabrique de module :
mymodulefactory.h :
class CMyHttpModuleFactory : public IHttpModuleFactory
{
public:
virtual HRESULT GetHttpModule(
OUT CHttpModule **ppModule,
IN IModuleAllocator *
)
{
}
virtual void Terminate()
{
}
};
La fabrique de modules implémente l’interface IHttpModuleFactory et sert à créer des instances du module sur chaque requête.
Le serveur appelle la méthode GetHttpModule au début de chaque requête pour obtenir l’instance du module à utiliser pour cette requête. L’implémentation retourne simplement une nouvelle instance de notre classe de module, CMyHttpModule, que nous implémentons ensuite. Comme nous le voyons sous peu, cela nous permet de stocker facilement l’état de la demande sans vous soucier de la sécurité des threads, car le serveur crée et utilise toujours une nouvelle instance du module pour chaque requête.
Les implémentations de fabrique plus avancées peuvent décider d’utiliser un modèle singleton au lieu de créer une nouvelle instance chaque fois, ou utiliser l’interface IModuleAllocator fournie pour allouer la mémoire du module dans le pool de requêtes. Ces modèles avancés ne sont pas abordés dans cette procédure pas à pas.
La méthode Terminate est appelée par le serveur lorsque le processus de travail s’arrête pour effectuer la dernière propre up du module. Si vous initialisez un état global dans RegisterModule, implémentez son propre nettoyage dans cette méthode.
Implémentation de la classe de module
Cette classe est chargée de fournir les principales fonctionnalités du module pendant un ou plusieurs événements de serveur :
myhttpmodule.h :
class CMyHttpModule : public CHttpModule
{
public:
REQUEST_NOTIFICATION_STATUS
OnAcquireRequestState(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
};
La classe de module hérite de la classe de base CHttpModule, qui définit une méthode de gestionnaire d’événements pour chacun des événements serveur abordés précédemment. Lorsque le pipeline de traitement des demandes exécute chaque événement, il appelle la méthode de gestionnaire d’événements associée sur chacune des instances de module inscrites pour cet événement.
Chaque méthode de gestionnaire d’événements a la signature suivante :
REQUEST_NOTIFICATION_STATUS
OnEvent(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
);
L’interface IHttpContext permet d’accéder à l’objet de contexte de requête, qui peut être utilisé pour effectuer des tâches de traitement des demandes telles que l’inspection de la requête et la manipulation de la réponse.
L’interface IHttpEventProvider est remplacée par une interface plus spécifique pour chacun des événements qui fournissent des fonctionnalités spécifiques au module. Par exemple, le gestionnaire d’événements OnAuthenticateRequest reçoit l’interface IAuthenticationProvider qui permet au module de définir l’utilisateur authentifié.
Le retour de chaque méthode de gestionnaire d’événements est l’une des valeurs de l’énumération REQUEST_NOTIFICATION_STATUS. Vous devez retourner RQ_NOTIFICATION_CONTINUE si le module a correctement effectué la tâche ; le pipeline doit continuer l’exécution.
Si un échec s’est produit et que vous souhaitez abandonner le traitement des demandes avec une erreur, vous devez définir l’état de l’erreur et retourner RQ_NOTIFICATION_FINISH_REQUEST. Le retour RQ_NOTIFICATION_PENDING vous permet d’effectuer un travail de manière asynchrone et de laisser le thread traiter la requête afin qu’elle puisse être réutilisée pour une autre requête. L’exécution asynchrone n’est pas abordée dans cet article.
Notre classe de module remplace la méthode du gestionnaire d’événements OnAcquireRequestState. Pour fournir des fonctionnalités dans l’une des phases de pipeline, la classe de module doit remplacer la méthode de gestionnaire d’événements correspondante. Si vous vous inscrivez à un événement dans RegisterModule, mais ne remplacez pas la méthode de gestionnaire d’événements appropriée sur votre classe de module, votre module échoue au moment de l’exécution (et déclenche une assertion au moment du débogage si elle est compilée en mode débogage). Soyez prudent et assurez-vous que la signature de méthode de la méthode de substitution est exactement équivalente à la méthode de classe de base de la classe CHttpModule que vous substituez.
Compilation du module
N’oubliez pas que vous avez besoin du Kit de développement logiciel (SDK) de plateforme pour compiler. Consultez l’introduction pour plus d’informations sur l’obtention et l’activation de Visual Studio pour la référencer.
Déploiement d’un module natif
Une fois que vous avez compilé votre module, vous devez le déployer sur le serveur. Compilez le module, puis copiez IIS7NativeModule.dll (et le fichier de symboles de débogage IIS7NativeModule.pdb si vous le souhaitez) vers n’importe quel emplacement sur l’ordinateur exécutant IIS.
Les modules natifs, contrairement aux modules managés qui peuvent être ajoutés directement à l’application, doivent d’abord être installés sur le serveur. Cela requiert des privilèges administratifs.
Pour installer un module natif, vous avez plusieurs options :
- Utiliser l’outil en ligne de commande APPCMD.EXE
APPCMD simplifie l’installation du module. Accédez à Démarrer>Programmes>Accessoires, cliquez avec le bouton droit sur l’invite de ligne de commande, puis choisissez Exécuter en tant qu’administrateur. Dans la fenêtre de ligne de commande, exécutez les suivantes :
%systemroot%\system32\inetsrv\appcmd.exe install module /name:MyModule /image:[FULL\_PATH\_TO\_DLL]
Où [FULL_PATH_TO_DLL] est le chemin complet de la DLL compilée contenant le module que vous venez de créer. - Utilisez l’outil d'administration IIS
Cela vous permet d’ajouter un module à l’aide d’une interface utilisateur graphique. Accédez à Démarrer>Exécution, tapez inetmgr, puis appuyez sur Entrée. Connecter à localhost, recherchez la tâche Modules, puis double-cliquez pour l’ouvrir. Cliquez ensuite sur la tâche Ajouter un module natif dans le volet droit. - Installez le module manuellement
Installez le module manuellement en l’ajoutant à la section de configuration <system.webServer>/<globalModules> dans le fichier de configuration applicationHost.config, puis ajoutez une référence à celle-ci dans la section de configuration <system.webServer>/<modules> du même fichier afin de l’activer. Nous vous recommandons d’utiliser l’une des deux options précédentes pour installer le module au lieu de modifier la configuration directement.
La tâche est terminée : nous avons terminé la configuration du nouveau module natif.
Résumé
Dans cette procédure pas à pas, vous avez appris à développer et déployer un module natif personnalisé à l’aide des nouvelles API d’extensibilité natives (C++). Consultez la vue d’ensemble du développement native-code pour en savoir plus sur les API serveur natives (C++).
Pour en savoir plus sur l’extension d'IIS à l’aide du code managé et du .NET Framework, consultez Développement d’un module IIS avec .NET. Pour en savoir plus sur la gestion des modules IIS, consultez le livre blanc de présentation du module.