Freigeben über


Entwickeln eines nativen C\C++-Moduls für IIS 7.0

von Mike Volodarsky

Einführung

IIS 7.0 und höher ermöglichen das Erweitern des Servers durch Module, die auf zwei Arten entwickelt werden:

  • Verwenden von verwaltetem Code und der ASP.NET Servererweiterungs-APIs
  • Verwenden von systemeigenem Code und der NATIVEn IIS-Servererweiterungs-APIs

Im Gegensatz zu den vorherigen Versionen von IIS ist für die meisten Servererweiterungsszenarien keine systemeigene (C++)-Codeentwicklung erforderlich und kann mit verwaltetem Code und den ASP.NET-APIs berücksichtigt werden. Wenn Sie ASP.NET verwenden, um den Server zu erweitern, können Sie die Entwicklungszeit erheblich reduzieren und die umfangreichen Funktionen von ASP.NET und .NET Framework nutzen. Weitere Informationen zum Erweitern von IIS mit ASP.NET finden Sie unter Entwickeln eines IIS-Moduls mit .NET.

IIS stellt auch eine systemeigene C++-Kernserver-API bereit, die ISAPI-Filter- und Erweiterungs-API aus früheren IIS-Versionen ersetzt. Wenn Sie bestimmte Anforderungen haben, die systemeigene Codeentwicklung erfordern oder Ihre vorhandenen systemeigenen ISAPI-Komponenten konvertieren möchten, nutzen Sie diese API zum Erstellen von Serverkomponenten. Die neue systemeigene Server-API bietet objektorientierte Entwicklung mit einem intuitiven Objektmodell, bietet mehr Kontrolle über die Anforderungsverarbeitung und verwendet einfachere Entwurfsmuster, um robusten Code zu schreiben.

In dieser Schritt-für-Schritt-Anleitung werden die folgenden Aufgaben erläutert:

  • Entwickeln eines systemeigenen Moduls mithilfe der systemeigenen Server-API (C++)
  • Bereitstellen eines systemeigenen Moduls auf dem Server

Um das Modul zu kompilieren, müssen Sie das Platform SDK installieren, das die IIS-Headerdateien enthält. Das neueste Windows Vista Platform SDK ist hier verfügbar.

Um das Platform SDK mit Visual Studio 2005 zu verwenden, müssen Sie das SDK registrieren. Nachdem Sie das SDK installiert haben, führen Sie dies über Start > Programme > Microsoft Windows SDK > Visual Studio-Registrierung > Windows SDK-Verzeichnisse mit Visual Studio registrieren aus.

Der Quellcode für dieses Modul ist im Visual Studio IIS7 Native Module-Beispiel verfügbar.

Entwickeln eines nativen Moduls

In dieser Aufgabe untersuchen wir die Entwicklung eines systemeigenen Moduls mithilfe der neuen nativen Server-API (C++). Ein systemeigenes Modul ist eine Windows-DLL, die Folgendes enthält:

  • RegisterModule exportierte Funktion. Diese Funktion ist für das Erstellen einer Modulfabrik und die Registrierung des Moduls für mindestens ein Serverereignis verantwortlich.
  • Implementierung der Modulklasse, die von der CHttpModule-Basisklasse erbt. Diese Klasse stellt die Hauptfunktionalität Ihres Moduls bereit.
  • Implementierung der Modulfactoryklasse, die die IHttpModuleFactory-Schnittstelle implementiert. Die Klasse ist für das Erstellen von Instanzen Ihres Moduls verantwortlich.

Hinweis

In einigen Fällen können Sie auch die IGlobalModule-Schnittstelle implementieren, um einige der Serverfunktionen zu erweitern, die nicht mit der Anforderungsverarbeitung verbunden sind. Dies ist ein erweitertes Thema und wird in dieser exemplarischen Vorgehensweise nicht behandelt.

Ihr systemeigenes Modul verfügt über den folgenden Lebenszyklus:

  1. Wenn der Serverarbeitsprozess gestartet wird, wird die DLL geladen, die Ihr Modul enthält, und die exportierte RegisterModule-Funktion wird aufgerufen. In dieser Funktion:

    a) Erstellen Sie die Modulfabrik.
    b. Registrieren Sie die Modulfabrik für die Anforderungs-Pipeline-Ereignisse, die Ihr Modul implementiert.

  2. Wenn eine Anforderung eingeht, reagiert der Server:

    a) Erstellt eine Instanz Ihrer Modulklasse mithilfe der von Ihnen bereitgestellten Factory.
    b. Ruft die entsprechende Ereignishandlermethode für die Modulinstanz für jedes der Anforderungsereignisse auf, für die Sie registriert haben.
    c. Entfernt die Instanz des Moduls am Ende der Anforderungsverarbeitung.

Bauen Sie es jetzt.

Der vollständige Quellcode für das Modul ist im Visual Studio IIS7 Native Module-Beispiel verfügbar. Die folgenden Schritte sind für die Entwicklung des Moduls am wichtigsten und enthalten keine unterstützenden Code und Fehlerbehandlung.

Implementieren Sie die RegisterModule-Funktion , die der Server aufruft, wenn die Modul-DLL geladen wird. Die Signatur und der Rest der nativen API werden in der Headerdatei "httpserv.h " definiert, die Teil des Platform SDK ist (wenn Sie nicht über das Platform SDK verfügen, finden Sie in der Einführung Informationen zum Abrufen):

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 );            
}

The RegisterModule

Es gibt drei grundlegende Aufgaben, die wir innerhalb von RegisterModule ausführen müssen:

Speichern des globalen Zustands

Wir speichern die globale Serverinstanz und die Modulkontext-ID für die spätere Verwendung in globalen Variablen. Obwohl in diesem Beispiel diese Informationen nicht verwendet werden, sind viele Module hilfreich, um sie später während der Anforderungsverarbeitung zu speichern und zu verwenden. Die IHttpServer-Schnittstelle bietet Zugriff auf viele Serverfunktionen, z. B. das Öffnen von Dateien und den Zugriff auf den Cache. Die Modulkontext-ID wird verwendet, um den benutzerdefinierten Modulstatus mehreren Serverobjekten wie Anforderung und Anwendung zuzuordnen.

Modul-Factory erstellen

Wir implementieren unsere Factoryklasse CMyHttpModuleFactory später in diesem Beispiel. Diese Fabrik ist für die Herstellung von Instanzen unseres Moduls für jede Anforderung verantwortlich.

Registrieren der Modulfabrik für die gewünschten Ereignisse bei der Anforderungsverarbeitung

Die Registrierung erfolgt über die SetRequestNotificatons-Methode , die den Server anweist: die Modulinstanz für jede Anforderung mithilfe der angegebenen Factory zu erstellen; und um die entsprechenden Ereignishandler für jede der angegebenen Anforderungsverarbeitungsphasen aufzurufen.

In diesem Fall interessieren wir uns nur für die RQ_ACQUIRE_REQUEST_STATE Stufe. Die vollständige Liste der Phasen, die die Anforderungsverarbeitungspipeline umfassen, wird in httpserv.h definiert:

#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

Darüber hinaus können Sie mehrere nicht deterministische Ereignisse abonnieren, die während der Anforderungsverarbeitung auftreten können, aufgrund von Aktionen, die andere Module ausführen, z. B. das Leeren der Antwort auf den 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

Damit auf unsere RegisterModule-Implementierung auf den Server zugegriffen werden kann, müssen wir sie exportieren. Verwenden Sie ein . DEF-Datei, die das EXPORT-Schlüsselwort enthält, um unsere RegisterModule-Funktion zu exportieren.

Implementieren Sie als Nächstes die Modul-Factory-Klasse.

mymodulefactory.h:

class CMyHttpModuleFactory : public IHttpModuleFactory
{
public:
    virtual HRESULT GetHttpModule(
        OUT CHttpModule            **ppModule, 
        IN IModuleAllocator        *
    )
            
    {
    }

   virtual void Terminate()
    {
    }

};

Die Modulfactory implementiert die IHttpModuleFactory-Schnittstelle und dient zum Erstellen von Instanzen des Moduls auf jeder Anforderung.

Der Server ruft die GetHttpModule-Methode am Anfang jeder Anforderung auf, um die Instanz des Moduls abzurufen, das für diese Anforderung verwendet werden soll. Die Implementierung gibt einfach eine neue Instanz unserer Modulklasse CMyHttpModule zurück, die wir als Nächstes implementieren. Wie wir kurz sehen, können wir den Anforderungsstatus problemlos speichern, ohne sich Gedanken über threadsicherheit zu machen, da der Server immer eine neue Instanz des Moduls für jede Anforderung erstellt und verwendet.

Komplexere Factoryimplementierungen können entscheiden, ein Singleton-Muster zu verwenden, anstatt jedes Mal eine neue Instanz zu erstellen, oder die bereitgestellte IModuleAllocator-Schnittstelle verwenden, um Modulspeicher im Anforderungspool zuzuweisen. Diese erweiterten Muster werden in dieser Schritt-für-Schritt-Anleitung nicht besprochen.

Die Terminate-Methode wird vom Server aufgerufen, wenn der Arbeitsprozess heruntergefahren wird, um die endgültige Bereinigung des Moduls durchzuführen. Wenn Sie einen globalen Zustand in RegisterModule initialisieren, implementieren Sie die Bereinigung in dieser Methode.

Implementieren der Modulklasse

Diese Klasse ist für die Bereitstellung der Hauptfunktionalität des Moduls während eines oder mehrerer Serverereignisse verantwortlich:

myhttpmodule.h:

class CMyHttpModule : public CHttpModule
{
public:
    REQUEST_NOTIFICATION_STATUS
    OnAcquireRequestState(
        IN IHttpContext *                       pHttpContext,
        IN OUT IHttpEventProvider *             pProvider
    );
};

Die Modulklasse erbt von der CHttpModule-Basisklasse , die eine Ereignishandlermethode für jedes der zuvor erläuterten Serverereignisse definiert. Wenn die Anforderungsverarbeitungspipeline jedes Ereignis ausführt, ruft sie die zugeordnete Ereignishandlermethode für jede der Modulinstanzen auf, die für dieses Ereignis registriert wurden.

Jede Ereignishandlermethode weist die folgende Signatur auf:

REQUEST_NOTIFICATION_STATUS
    OnEvent(
        IN IHttpContext *                       pHttpContext,
        IN OUT IHttpEventProvider *             pProvider
    );

Die IHttpContext-Schnittstelle bietet Zugriff auf das Anforderungskontextobjekt, das verwendet werden kann, um Anforderungsverarbeitungsaufgaben auszuführen, z. B. das Überprüfen der Anforderung und das Bearbeiten der Antwort.

Die IHttpEventProvider-Schnittstelle wird durch eine spezifischere Schnittstelle für jedes Ereignis ersetzt, das bestimmte Funktionen für das Modul bereitstellt. Beispielsweise empfängt der OnAuthenticateRequest-Ereignishandler die IAuthenticationProvider-Schnittstelle , mit der das Modul den authentifizierten Benutzer festlegen kann.

Die Rückgabe jeder Ereignishandlermethode ist einer der Werte der REQUEST_NOTIFICATION_STATUS-Aufzählung. Sie müssen RQ_NOTIFICATION_CONTINUE zurückgeben, wenn das Modul die Aufgabe erfolgreich ausgeführt hat; die Pipeline sollte die Ausführung fortsetzen.

Wenn ein Fehler aufgetreten ist und Sie die Anforderungsverarbeitung mit einem Fehler abbrechen möchten, müssen Sie den Fehlerstatus festlegen und RQ_NOTIFICATION_FINISH_REQUEST zurückgeben. Mit dem RQ_NOTIFICATION_PENDING-Rückgabewert können Sie Arbeiten asynchron ausführen und den Thread, der die Anforderung verarbeitet, freigeben, damit er für eine andere Anforderung wiederverwendet werden kann. Die asynchrone Ausführung wird in diesem Artikel nicht behandelt.

Unsere Modulklasse setzt die OnAcquireRequestState-Ereignishandlermethode außer Kraft. Um Funktionen in einer der Pipelinephasen bereitzustellen, muss die Modulklasse die entsprechende Ereignishandlermethode überschreiben. Wenn Sie sich für ein Ereignis in RegisterModule registrieren und die entsprechende Ereignishandlermethode nicht in der Modulklasse überschreiben, schlägt Ihr Modul zur Laufzeit fehl (und löst eine Debug-Zeit-Assertion aus, wenn es im Debug-Modus kompiliert wird). Achten Sie darauf, dass die Methodensignatur der Überschreibungsmethode genau der Basisklassenmethode der CHttpModule-Klasse entspricht, die Sie überschreiben.

Kompilieren des Moduls

Denken Sie daran, dass Sie das Platform SDK zum Kompilieren benötigen. Weitere Informationen darüber, wie Sie es abrufen und Visual Studio so einrichten, dass es darauf verweisen kann, finden Sie in der Einführung.

Bereitstellen eines nativen Moduls

Nachdem Sie das Modul kompiliert haben, müssen Sie es auf dem Server bereitstellen. Kompilieren Sie das Modul, und kopieren Sie dann IIS7NativeModule.dll (und die IIS7NativeModule.pdb Debugsymboldatei, falls gewünscht), an einen beliebigen Speicherort auf dem Computer, auf dem IIS ausgeführt wird.

Native Module müssen im Gegensatz zu verwalteten Modulen, die direkt zur Anwendung hinzugefügt werden können, zuerst auf dem Server installiert werden. Dies erfordert Administratorrechte.

Um ein systemeigenes Modul zu installieren, haben Sie mehrere Optionen:

  • Verwenden des Befehlszeilentools APPCMD.EXE
    APPCMD vereinfacht die Modulinstallation. Wechseln Sie zu Start>Programme>Zubehör, klicken Sie mit der rechten Maustaste auf die Eingabeaufforderung, und wählen Sie Als Administrator ausführen. Führen Sie im Befehlszeilenfenster Folgendes aus:
    %systemroot%\system32\inetsrv\appcmd.exe install module /name:MyModule /image:[FULL\_PATH\_TO\_DLL]
    Dabei ist [FULL_PATH_TO_DLL] der vollständige Pfad zur kompilierten DLL, die das soeben erstellte Modul enthält.
  • Verwenden des IIS-Verwaltungstools
    Auf diese Weise können Sie ein Modul mithilfe einer GUI hinzufügen. Wechseln Sie zu "Start" und dann "Ausführen...", geben Sie "inetmgr" ein, und drücken Sie die EINGABETASTE. Stellen Sie eine Verbindung mit localhost her, suchen Sie die Aufgabe "Module", und doppelklicken Sie darauf, um sie zu öffnen. Klicken Sie dann im rechten Bereich auf die Aufgabe "Systemeigenes Modul hinzufügen ".
  • Manuelles Installieren des Moduls
    Installieren Sie das Modul manuell, indem Sie es dem <Konfigurationsabschnitt "system.webServer>/<globalModules> " in applicationHost.config Konfigurationsdatei hinzufügen und einen Verweis darauf im <Konfigurationsabschnitt "system.webServer>/<modules> " in derselben Datei hinzufügen, um es zu aktivieren. Es wird empfohlen, dass Sie eine der beiden vorherigen Optionen verwenden, um das Modul zu installieren, anstatt die Konfiguration direkt zu bearbeiten.

Die Aufgabe ist abgeschlossen – wir haben die Konfiguration des neuen nativen Moduls abgeschlossen.

Zusammenfassung

In dieser exemplarischen Vorgehensweise haben Sie erfahren, wie Sie ein benutzerdefiniertes systemeigenes Modul mithilfe der neuen nativen Erweiterbarkeits-APIs (C++) entwickeln und bereitstellen. Weitere Informationen zu den systemeigenen Server-APIs (C++) finden Sie in der Native-Code Entwicklungsübersicht .

Informationen zum Erweitern von IIS mithilfe von verwaltetem Code und .NET Framework finden Sie unter Entwickeln eines IIS-Moduls mit .NET. Weitere Informationen zum Verwalten von IIS-Modulen finden Sie im Whitepaper "Modulübersicht".