Share via


Sviluppare un modulo C\C++ nativo per IIS 7.0

di Mike Volodarsky

Introduzione

IIS 7.0 e versioni successive consente di estendere il server in base ai moduli sviluppati in due modi:

  • Uso del codice gestito e delle API di estendibilità del server ASP.NET
  • Uso del codice nativo e delle API di estendibilità del server nativo IIS

A differenza delle versioni precedenti di IIS, la maggior parte degli scenari di estendibilità del server non richiede lo sviluppo di codice nativo (C++) e può essere gestito usando il codice gestito e le API ASP.NET. L'uso di ASP.NET per estendere il server consente di ridurre notevolmente i tempi di sviluppo e di sfruttare le funzionalità avanzate di ASP.NET e .NET Framework. Per altre informazioni sull'estensione di IIS con ASP.NET, vedere Sviluppo di un modulo IIS con .NET.

IIS fornisce anche un'API server core nativa (C++), che sostituisce il filtro ISAPI e l'API di estensione delle versioni precedenti di IIS. Se si hanno requisiti specifici che richiedono lo sviluppo di codice nativo o si vuole convertire i componenti ISAPI nativi esistenti, sfruttare questa API per compilare i componenti server. La nuova API server nativa include lo sviluppo orientato agli oggetti con un modello a oggetti intuitivo, offre un maggiore controllo sull'elaborazione delle richieste e usa modelli di progettazione più semplici per semplificare la scrittura di codice.

Questa procedura dettagliata esamina le attività seguenti:

  • Sviluppo di un modulo nativo tramite l'API server nativa (C++)
  • Distribuzione di un modulo nativo nel server

Per compilare il modulo, è necessario installare Platform SDK che contiene i file di intestazione IIS. La versione più recente di Windows Vista Platform SDK è disponibile qui.

Per usare Platform SDK con Visual Studio 2005, è necessario registrare l'SDK. Dopo aver installato l'SDK, eseguire questa operazione tramite Start Programs > Microsoft Windows SDK > Visual Studio Registration Register > Windows SDK Directoryies with Visual Studio (Registrare > directory di Windows SDK con Visual Studio).

Il codice sorgente per questo modulo è disponibile nell'esempio di modulo nativo di Visual Studio IIS7.

Sviluppare un modulo nativo

In questa attività viene esaminato lo sviluppo di un modulo nativo usando la nuova API server nativa (C++). Un modulo nativo è una DLL di Windows che contiene quanto segue:

  • Funzione esportata RegisterModule . Questa funzione è responsabile della creazione di una factory di moduli e della registrazione del modulo per uno o più eventi del server.
  • Implementazione della classe del modulo che eredita dalla classe di base CHttpModule . Questa classe fornisce la funzionalità principale del modulo.
  • Implementazione della classe factory del modulo che implementa l'interfaccia IHttpModuleFactory . La classe è responsabile della creazione di istanze del modulo.

Nota

In alcuni casi, è anche possibile implementare l'interfaccia IGlobalModule , per estendere alcune delle funzionalità del server non correlate all'elaborazione delle richieste. Questo è un argomento avanzato e non è trattato in questa procedura dettagliata.

Il modulo nativo ha il ciclo di vita seguente:

  1. All'avvio del processo di lavoro del server, caricherà la DLL contenente il modulo e richiamerà la funzione RegisterModule esportata. In questa funzione si:

    a. Creare la factory del modulo.
    b. Registrare la factory del modulo per gli eventi della pipeline di richiesta implementati dal modulo.

  2. Quando arriva una richiesta, il server:

    a. Crea un'istanza della classe del modulo usando la factory specificata.
    b. Chiama il metodo del gestore eventi appropriato nell'istanza del modulo per ogni evento di richiesta registrato.
    c. Elimina l'istanza del modulo alla fine dell'elaborazione della richiesta.

A questo momento, per compilarlo.

Il codice sorgente completo per il modulo è disponibile nell'esempio di modulo nativo di Visual Studio IIS7. I passaggi seguenti sono i più importanti per lo sviluppo del modulo e non includono il supporto del codice e della gestione degli errori.

Implementare la funzione RegisterModule richiamata dal server quando viene caricata la DLL del modulo. La firma e il resto dell'API nativa sono definiti nel file di intestazione httpserv.h , che fa parte di Platform SDK (se non si dispone di Platform SDK, vedere l'introduzione per informazioni su come ottenerlo):

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

Esistono tre attività di base da eseguire all'interno di RegisterModule:

Salvare lo stato globale

Verranno archiviati l'istanza del server globale e l'ID del contesto del modulo per usarli successivamente nelle variabili globali. Anche se questo esempio non usa queste informazioni, molti moduli lo trovano utile per salvare e usarle in un secondo momento durante l'elaborazione della richiesta. L'interfaccia IHttpServer consente l'accesso a molte funzioni del server, ad esempio l'apertura di file e l'accesso alla cache. L'ID contesto del modulo viene usato per associare lo stato del modulo personalizzato a diversi oggetti server, ad esempio richiesta e applicazione.

Creare una factory del modulo

La classe factory CMyHttpModuleFactory verrà implementata più avanti in questa procedura dettagliata. Questa factory è responsabile della produzione di istanze del modulo per ogni richiesta.

Registrare module factory per gli eventi di elaborazione delle richieste desiderati

La registrazione viene eseguita tramite il metodo SetRequestNotificatons , che indica al server di creare l'istanza del modulo per ogni richiesta usando la factory specificata; e, per richiamare i gestori eventi appropriati su di esso per ognuna delle fasi di elaborazione delle richieste specificate.

In questo caso, siamo interessati solo alla fase RQ_ACQUIRE_REQUEST_STATE. L'elenco completo delle fasi che comprendono la pipeline di elaborazione delle richieste è definito in 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

È anche possibile sottoscrivere diversi eventi non deterministici che possono verificarsi durante l'elaborazione delle richieste a causa di azioni eseguite da altri moduli, ad esempio lo scaricamento della risposta al 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

Affinché l'implementazione RegisterModule sia accessibile al server, è necessario esportarla. Usare un oggetto . File DEF contenente la parola chiave EXPORTS per esportare la funzione RegisterModule.

Implementare quindi la classe module factory:

mymodulefactory.h:

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

   virtual void Terminate()
    {
    }

};

La factory del modulo implementa l'interfaccia IHttpModuleFactory e serve per creare istanze del modulo in ogni richiesta.

Il server chiama il metodo GetHttpModule all'inizio di ogni richiesta per ottenere l'istanza del modulo da usare per questa richiesta. L'implementazione restituisce semplicemente una nuova istanza della classe del modulo CMyHttpModule, che verrà implementata successivamente. Come si vede a breve, ciò consente di archiviare facilmente lo stato della richiesta senza preoccuparsi della thread safety, perché il server crea e usa sempre una nuova istanza del modulo per ogni richiesta.

Le implementazioni di factory più avanzate possono decidere di usare un modello singleton anziché creare una nuova istanza ogni volta o usare l'interfaccia IModuleAllocator fornita per allocare la memoria del modulo nel pool di richieste. Questi modelli avanzati non vengono descritti in questa procedura dettagliata.

Il metodo Terminate viene chiamato dal server quando il processo di lavoro viene arrestato per eseguire la pulizia finale del modulo. Se si inizializza uno stato globale in RegisterModule, implementarne la pulizia in questo metodo.

Implementazione della classe Module

Questa classe è responsabile della fornitura della funzionalità principale del modulo durante uno o più eventi del server:

myhttpmodule.h:

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

La classe module eredita dalla classe di base CHttpModule , che definisce un metodo del gestore eventi per ognuno degli eventi del server descritti in precedenza. Quando la pipeline di elaborazione delle richieste esegue ogni evento, richiama il metodo del gestore eventi associato in ognuna delle istanze del modulo registrate per tale evento.

Ogni metodo del gestore eventi ha la firma seguente:

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

L'interfaccia IHttpContext consente di accedere all'oggetto contesto della richiesta, che può essere usato per eseguire attività di elaborazione delle richieste, ad esempio l'ispezione della richiesta e la modifica della risposta.

L'interfaccia IHttpEventProvider viene sostituita con un'interfaccia più specifica per ognuno degli eventi che forniscono funzionalità specifiche al modulo. Ad esempio, il gestore eventi OnAuthenticateRequest riceve l'interfaccia IAuthenticationProvider che consente al modulo di impostare l'utente autenticato.

La restituzione di ogni metodo del gestore eventi è uno dei valori dell'enumerazione REQUEST_NOTIFICATION_STATUS. È necessario restituire RQ_NOTIFICATION_CONTINUE se il modulo ha eseguito correttamente l'attività; la pipeline deve continuare l'esecuzione.

Se si è verificato un errore e si desidera interrompere l'elaborazione della richiesta con un errore, è necessario impostare lo stato di errore e restituire RQ_NOTIFICATION_FINISH_REQUEST. Il RQ_NOTIFICATION_PENDING restituito consente di eseguire il lavoro in modo asincrono e di lasciare che il thread esegua l'elaborazione della richiesta in modo che possa essere riutilizzato per un'altra richiesta. L'esecuzione asincrona non è descritta in questo articolo.

La classe del modulo esegue l'override del metodo del gestore eventi OnAcquireRequestState. Per fornire funzionalità in una delle fasi della pipeline, la classe module deve eseguire l'override del rispettivo metodo del gestore eventi. Se si esegue la registrazione per un evento in RegisterModule, ma non si esegue l'override del metodo del gestore eventi appropriato nella classe del modulo, il modulo avrà esito negativo in fase di esecuzione e attiverà un'asserzione in fase di debug se compilata nella modalità di debug. Prestare attenzione e assicurarsi che la firma del metodo di override sia esattamente equivalente al metodo della classe base della classe CHttpModule di cui si esegue l'override.

Compilazione del modulo

Tenere presente che è necessario Platform SDK per la compilazione. Per altre informazioni su come ottenerlo e abilitare Visual Studio per farvi riferimento, vedere Introduzione .

Distribuzione di un modulo nativo

Dopo aver compilato il modulo, è necessario distribuirlo nel server. Compilare il modulo e quindi copiare IIS7NativeModule.dll (e il file di simboli di debug IIS7NativeModule.pdb, se necessario) in qualsiasi posizione del computer che esegue IIS.

I moduli nativi, a differenza dei moduli gestiti che possono essere aggiunti direttamente all'applicazione, devono prima essere installati nel server. Questo richiede privilegi amministrativi.

Per installare un modulo nativo, sono disponibili diverse opzioni:

  • Usare lo strumento da riga di comando APPCMD.EXE
    APPCMD semplifica l'installazione del modulo. Passare a Avvia>programmi>Accessori, fare clic con il pulsante destro del mouse sul prompt della riga di comando e scegliere Esegui come amministratore. Nella finestra della riga di comando eseguire quanto segue:
    %systemroot%\system32\inetsrv\appcmd.exe install module /name:MyModule /image:[FULL\_PATH\_TO\_DLL]
    Dove [FULL_PATH_TO_DLL] è il percorso completo della DLL compilata contenente il modulo appena compilato.
  • Usare lo strumento di amministrazione iis
    In questo modo è possibile aggiungere un modulo usando un'interfaccia utente grafica. Passare a Avvia>esecuzione, digitare inetmgr e premere INVIO. Connettersi a localhost, individuare l'attività Moduli e fare doppio clic per aprirla. Fare quindi clic sull'attività Aggiungi modulo nativo nel riquadro destro.
  • Installare il modulo manualmente
    Installare manualmente il modulo aggiungendolo alla <sezione di configurazione system.webServer>/<globalModules> in applicationHost.config file di configurazione e aggiungendo un riferimento alla <sezione di configurazione system.webServer>/<modules> nello stesso file per abilitarla. È consigliabile usare una delle due opzioni precedenti per installare il modulo anziché modificare direttamente la configurazione.

L'attività è stata completata. È stata completata la configurazione del nuovo modulo nativo.

Riepilogo

In questa procedura dettagliata si è appreso come sviluppare e distribuire un modulo nativo personalizzato usando le nuove API di estendibilità native (C++). Per altre informazioni sulle API server native (C++) vedere La panoramica dello sviluppo di codice nativo.

Per informazioni sull'estensione di IIS tramite codice gestito e .NET Framework, vedere Sviluppo di un modulo IIS con .NET. Per altre informazioni sulla gestione dei moduli IIS, vedere il white paper sulla panoramica del modulo.