Share via


Scrivere un driver windows Hello World (KMDF)

Questo articolo descrive come scrivere un piccolo driver di Windows universale usando Kernel-Mode Driver Framework (KMDF) e quindi distribuire e installare il driver in un computer separato.

Prima di procedere, completare i passaggi di installazione elencati in Scaricare Windows Driver Kit (WDK).

Gli strumenti di debug per Windows vengono inclusi quando si installa WDK.

Creare e compilare un driver

  1. Aprire Microsoft Visual Studio. Scegliere Nuovo > progetto dal menu File.

  2. Nella finestra di dialogo Crea un nuovo progetto selezionare C++ nell'elenco a discesa a sinistra, scegliere Windows nell'elenco a discesa centrale e scegliere Driver nell'elenco a discesa a destra.

  3. Selezionare Driver in modalità kernel, Vuoto (KMDF) nell'elenco dei tipi di progetto. Selezionare Avanti.

    Screenshot della finestra di dialogo Nuovo progetto di Visual Studio con l'opzione driver in modalità kernel selezionata.

  4. Nella finestra di dialogo Configura il nuovo progetto immettere "KmdfHelloWorld" nel campo Nome progetto .

    Nota

    Quando si crea un nuovo driver KMDF o UMDF, è necessario selezionare un nome di driver con 32 caratteri o meno. Questo limite di lunghezza è definito in wdfglobals.h.

  5. Nel campo Percorso immettere la directory in cui si vuole creare il nuovo progetto.

  6. Selezionare Inserisci soluzione e progetto nella stessa directory e selezionare Crea.

    Screenshot della finestra di dialogo Configura il nuovo progetto di Visual Studio con il pulsante Crea evidenziato.

    Visual Studio crea un progetto e una soluzione. I file sono visibili nella finestra Esplora soluzioni. Se la finestra di Esplora soluzioni non è visibile, scegliere Esplora soluzioni dal menu Visualizza. La soluzione ha un progetto driver denominato KmdfHelloWorld.

    Screenshot della finestra Esplora soluzioni di Visual Studio che mostra la soluzione e il progetto driver vuoto denominato KmdfHelloWorld.

  7. Nella finestra Esplora soluzioni selezionare e tenere premuto (o selezionare con il pulsante destro del tasto destro) la soluzione KmdfHelloWorld e scegliere Configuration Manager. Scegliere una configurazione e una piattaforma per il progetto driver. Ad esempio, scegliere Debug e x64.

  8. Nella finestra Esplora soluzioni selezionare di nuovo e tenere premuto (o selezionare con il pulsante destro del tasto destro) il progetto KmdfHelloWorld, scegliere Aggiungi e quindi selezionare Nuovo elemento.

  9. Nella finestra di dialogo Aggiungi nuovo elemento selezionare File C++. In Nome immettere "Driver.c".

    Nota

    L'estensione del nome file è .c, non .cpp.

    Selezionare Aggiungi. Il file Driver.c viene aggiunto in File di origine, come illustrato di seguito.

    Screenshot della finestra Esplora soluzioni di Visual Studio che mostra il file driver.c aggiunto al progetto driver.

Scrivere il primo codice driver

Dopo aver creato il progetto Hello World vuoto e aggiunto il file di origine Driver.c, si scriverà il codice più semplice necessario per l'esecuzione del driver implementando due funzioni di callback di eventi di base.

  1. In Driver.c iniziare includendo queste intestazioni:

    #include <ntddk.h>
    #include <wdf.h>
    

    Suggerimento

    Se non è possibile aggiungere Ntddk.h, aprire Configurazione -> C/C++ - Generale ->> Directory di inclusione aggiuntive e aggiungere C:\Program Files (x86)\Windows Kits\10\Include\<build#>\km, sostituendo <build#> con la directory appropriata nell'installazione di WDK.

    Ntddk.h contiene le definizioni del kernel Windows di base per tutti i driver, mentre Wdf.h contiene definizioni per i driver basati su Windows Driver Framework (WDF).

  2. Specificare quindi le dichiarazioni per i due callback che verranno usati:

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. Usare il codice seguente per scrivere DriverEntry:

    NTSTATUS 
    DriverEntry(
        _In_ PDRIVER_OBJECT     DriverObject, 
        _In_ PUNICODE_STRING    RegistryPath
    )
    {
        // NTSTATUS variable to record success or failure
        NTSTATUS status = STATUS_SUCCESS;
    
        // Allocate the driver configuration object
        WDF_DRIVER_CONFIG config;
    
        // Print "Hello World" for DriverEntry
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" ));
    
        // Initialize the driver configuration object to register the
        // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
        WDF_DRIVER_CONFIG_INIT(&config, 
                               KmdfHelloWorldEvtDeviceAdd
                               );
    
        // Finally, create the driver object
        status = WdfDriverCreate(DriverObject, 
                                 RegistryPath, 
                                 WDF_NO_OBJECT_ATTRIBUTES, 
                                 &config, 
                                 WDF_NO_HANDLE
                                 );
        return status;
    }
    

    DriverEntry è il punto di ingresso per tutti i driver, come Main() per molte applicazioni in modalità utente. Il processo di DriverEntry consiste nell'inizializzare le strutture e le risorse a livello di driver. In questo esempio è stato stampato "Hello World" per DriverEntry, è stato configurato l'oggetto driver per registrare il punto di ingresso del callback EvtDeviceAdd, quindi è stato creato l'oggetto driver e restituito.

    L'oggetto driver funge da oggetto padre per tutti gli altri oggetti framework che è possibile creare nel driver, che includono oggetti dispositivo, code di I/O, timer, spinlock e altro ancora. Per altre informazioni sugli oggetti framework, vedere Introduzione agli oggetti framework.

    Suggerimento

    Per DriverEntry, è consigliabile mantenere il nome "DriverEntry" per semplificare l'analisi del codice e il debug.

  4. Usare quindi il codice seguente per scrivere kmdfHelloWorldEvtDeviceAdd:

    NTSTATUS 
    KmdfHelloWorldEvtDeviceAdd(
        _In_    WDFDRIVER       Driver, 
        _Inout_ PWDFDEVICE_INIT DeviceInit
    )
    {
        // We're not using the driver object,
        // so we need to mark it as unreferenced
        UNREFERENCED_PARAMETER(Driver);
    
        NTSTATUS status;
    
        // Allocate the device object
        WDFDEVICE hDevice;    
    
        // Print "Hello World"
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" ));
    
        // Create the device object
        status = WdfDeviceCreate(&DeviceInit, 
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &hDevice
                                 );
        return status;
    }
    

    EvtDeviceAdd viene richiamato dal sistema quando rileva che il dispositivo è arrivato. Il processo consiste nell'inizializzare strutture e risorse per tale dispositivo. In questo esempio è stato semplicemente stampato un messaggio "Hello World" per EvtDeviceAdd, è stato creato l'oggetto dispositivo e restituito. In altri driver scritti è possibile creare code di I/O per l'hardware, configurare uno spazio di archiviazione del contesto di dispositivo per informazioni specifiche del dispositivo o eseguire altre attività necessarie per preparare il dispositivo.

    Suggerimento

    Per aggiungere il callback del dispositivo, si noti come è stato denominato con il nome del driver come prefisso (KmdfHelloWorldEvtDeviceAdd). In genere, è consigliabile denominare le funzioni del driver in questo modo per distinguerle dalle funzioni di altri driver. DriverEntry è l'unico che dovresti assegnare esattamente un nome.

  5. Il file Driver.c completo è ora simile al seguente:

    #include <ntddk.h>
    #include <wdf.h>
    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
    NTSTATUS 
    DriverEntry(
        _In_ PDRIVER_OBJECT     DriverObject, 
        _In_ PUNICODE_STRING    RegistryPath
    )
    {
        // NTSTATUS variable to record success or failure
        NTSTATUS status = STATUS_SUCCESS;
    
        // Allocate the driver configuration object
        WDF_DRIVER_CONFIG config;
    
        // Print "Hello World" for DriverEntry
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" ));
    
        // Initialize the driver configuration object to register the
        // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
        WDF_DRIVER_CONFIG_INIT(&config, 
                               KmdfHelloWorldEvtDeviceAdd
                               );
    
        // Finally, create the driver object
        status = WdfDriverCreate(DriverObject, 
                                 RegistryPath, 
                                 WDF_NO_OBJECT_ATTRIBUTES, 
                                 &config, 
                                 WDF_NO_HANDLE
                                 );
        return status;
    }
    
    NTSTATUS 
    KmdfHelloWorldEvtDeviceAdd(
        _In_    WDFDRIVER       Driver, 
        _Inout_ PWDFDEVICE_INIT DeviceInit
    )
    {
        // We're not using the driver object,
        // so we need to mark it as unreferenced
        UNREFERENCED_PARAMETER(Driver);
    
        NTSTATUS status;
    
        // Allocate the device object
        WDFDEVICE hDevice;    
    
        // Print "Hello World"
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" ));
    
        // Create the device object
        status = WdfDeviceCreate(&DeviceInit, 
                                 WDF_NO_OBJECT_ATTRIBUTES,
                                 &hDevice
                                 );
        return status;
    }
    
  6. Salvare Driver.c.

In questo esempio viene illustrato un concetto fondamentale di driver: si tratta di una "raccolta di callback" che, una volta inizializzata, sedersi e attendere che il sistema li chiami quando ha bisogno di qualcosa. Una chiamata di sistema può essere un nuovo evento di arrivo del dispositivo, una richiesta di I/O da un'applicazione in modalità utente, un evento di arresto dell'alimentazione del sistema, una richiesta da un altro driver o un evento di rimozione a sorpresa quando un utente scollega il dispositivo in modo imprevisto. Fortunatamente, per dire "Hello World", è necessario solo preoccuparsi della creazione di driver e dispositivi.

Successivamente, si creerà il driver.

Compilare il driver

  1. Nella finestra Esplora soluzioni selezionare e tenere premuto (o selezionare con il pulsante destro del tasto destro) Soluzione 'KmdfHelloWorld' (1 progetto) e scegliere Configuration Manager. Scegliere una configurazione e una piattaforma per il progetto driver. Per questo esercizio si sceglie Debug e x64.

  2. Nella finestra Esplora soluzioni selezionare e tenere premuto (o selezionare con il pulsante destro del tasto destro) KmdfHelloWorld e scegliere Proprietà. In Wpp Tracing > All Options impostare Esegui traccia Wppsu No. Selezionare Apply (Applica) e quindi OK.

  3. Per compilare il driver, scegliere Compila soluzione dal menu Compila . Visual Studio mostra lo stato di avanzamento della compilazione nella finestra Output . Se la finestra Output non è visibile, scegliere Output dal menu Visualizza . Dopo aver verificato che la soluzione è stata compilata correttamente, è possibile chiudere Visual Studio.

  4. Per visualizzare il driver compilato, in Esplora file passare alla cartella KmdfHelloWorld e quindi a x64\Debug\KmdfHelloWorld. La cartella include:

    • KmdfHelloWorld.sys : il file del driver in modalità kernel
    • KmdfHelloWorld.inf - un file di informazioni usato da Windows durante l'installazione del driver
    • KmdfHelloWorld.cat : file di catalogo usato dal programma di installazione per verificare la firma di test del driver

Suggerimento

Se viene visualizzato DriverVer set to a date in the future quando si compila il driver, modificare le impostazioni del progetto driver in modo che Inf2Cat imposti /uselocaltime. A tale scopo, usare Proprietà di configurazione-Inf2Cat-General-Use>>> Local Time. Ora sia Stampinf che Inf2Cat usano l'ora locale.

Distribuire il driver

In genere, quando si esegue il test e il debug di un driver, il debugger e il driver vengono eseguiti in computer separati. Il computer che esegue il debugger viene chiamato computer host e il computer che esegue il driver viene chiamato computer di destinazione. Il computer di destinazione viene chiamato anche computer di test.

Finora è stato usato Visual Studio per compilare un driver nel computer host. A questo momento è necessario configurare un computer di destinazione.

  1. Seguire le istruzioni riportate in Effettuare il provisioning di un computer per la distribuzione e il test dei driver (WDK 10).Follow the instructions in Provision a computer for driver deployment and testing (WDK 10).

    Suggerimento

    Quando si seguono i passaggi per effettuare automaticamente il provisioning del computer di destinazione usando un cavo di rete, prendere nota della porta e della chiave. Verranno usati più avanti nel passaggio di debug. In questo esempio si userà 50000 come porta e 1.2.3.4 come chiave.

    Negli scenari di debug dei driver reali è consigliabile usare una chiave generata da KDNET. Per altre informazioni su come usare KDNET per generare una chiave casuale, vedere l'argomento Debug Drivers - Step by Step by Step Lab (Sysvad Kernel Mode).

  2. Nel computer host aprire la soluzione in Visual Studio. È possibile fare doppio clic sul file della soluzione, KmdfHelloWorld.sln, nella cartella KmdfHelloWorld.

  3. Nella finestra Esplora soluzioni selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) sul progetto KmdfHelloWorld e scegliere Proprietà.

  4. Nella finestra Pagine delle proprietà KmdfHelloWorld passare a Proprietà > di configurazione Installazione driver>, come illustrato di seguito.

  5. Selezionare Rimuovi versioni precedenti del driver prima della distribuzione.

  6. In Target Device Name (Nome dispositivo di destinazione) selezionare il nome del computer configurato per il test e il debug. In questo esercizio viene usato un computer denominato MyTestComputer.

  7. Selezionare Aggiornamento driver ID hardware e immettere l'ID hardware per il driver. Per questo esercizio, l'ID hardware è Root\KmdfHelloWorld. Selezionare OK.

    Screenshot della finestra delle pagine delle proprietà kmdfhelloworld con l'opzione di installazione del driver di distribuzione selezionata.

    Nota

    In questo esercizio l'ID hardware non identifica un componente hardware reale. Identifica un dispositivo immaginario che verrà assegnato un posto nell'albero dei dispositivi come figlio del nodo radice. Per hardware reale, non selezionare Aggiornamento driver ID hardware; selezionare invece Installa e verifica. L'ID hardware verrà visualizzato nel file delle informazioni del driver (INF). Nella finestra Esplora soluzioni passare a KmdfHelloWorld > Driver Files e fare doppio clic su KmdfHelloWorld.inf. L'ID hardware si trova in [Standard.NT$ARCH$].

    [Standard.NT$ARCH$]
    %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
    
  8. Scegliere Distribuisci soluzione dal menu Compila. Visual Studio copia automaticamente i file necessari per installare ed eseguire il driver nel computer di destinazione. La distribuzione può richiedere un minuto o due.

    Quando si distribuisce un driver, i file del driver vengono copiati nella cartella %Systemdrive%\drivertest\drivers nel computer di test. Se si verifica un errore durante la distribuzione, è possibile verificare se i file vengono copiati nel computer di test. Verificare che i file con estensione inf, cat, cert di test e .sys e tutti gli altri file necessari siano presenti nella cartella %systemdrive%\drivertest\drivers.

    Per altre informazioni sulla distribuzione dei driver, vedere Distribuzione di un driver in un computer di test.

Installare il driver

Con il driver Hello World distribuito nel computer di destinazione, ora si installerà il driver. Quando in precedenza è stato effettuato il provisioning del computer di destinazione con Visual Studio usando l'opzione automatica , Visual Studio configura il computer di destinazione per eseguire driver firmati di test come parte del processo di provisioning. Ora è sufficiente installare il driver usando lo strumento DevCon.

  1. Nel computer host passare alla cartella Strumenti nell'installazione di WDK e individuare lo strumento DevCon. Ad esempio, cercare nella cartella seguente:

    C:\Programmi (x86)\Windows Kits\10\Tools\x64\devcon.exe

    Copiare lo strumento DevCon nel computer remoto.

  2. Nel computer di destinazione installare il driver passando alla cartella contenente i file del driver, quindi eseguendo lo strumento DevCon.

    1. Ecco la sintassi generale per lo strumento devcon che verrà usato per installare il driver:

      devcon installare l'ID <hardware del file><INF>

      Il file INF necessario per l'installazione di questo driver è KmdfHelloWorld.inf. Il file INF contiene l'ID hardware per l'installazione del file binario del driver ,KmdfHelloWorld.sys. Tenere presente che l'ID hardware, che si trova nel file INF, è Root\KmdfHelloWorld.

    2. Aprire una finestra del prompt dei comandi come amministratore. Passare alla cartella contenente il file .sys driver compilato e immettere questo comando:

      devcon install kmdfhelloworld.inf root\kmdfhelloworld

      Se viene visualizzato un messaggio di errore relativo a devcon non riconosciuto, provare ad aggiungere il percorso allo strumento devcon . Ad esempio, se è stato copiato in una cartella nel computer di destinazione denominato C:\Tools, provare a usare il comando seguente:

      c:\tools\devcon install kmdfhelloworld.inf root\kmdfhelloworld

      Verrà visualizzata una finestra di dialogo che indica che il driver di test è un driver non firmato. Selezionare Installa questo driver comunque per continuare.

      Screenshot dell'avviso di sicurezza visualizzato durante il processo di installazione del driver.

Eseguire il debug del driver

Dopo aver installato il driver KmdfHelloWorld nel computer di destinazione, si collega un debugger in remoto dal computer host.

  1. Nel computer host aprire una finestra del prompt dei comandi come amministratore. Passare alla directory WinDbg.exe. Useremo la versione x64 di WinDbg.exe da Windows Driver Kit (WDK) installata come parte dell'installazione del kit di Windows. Ecco il percorso predefinito per WinDbg.exe:

    C:\Programmi (x86)\Windows Kits\10\Debuggers\x64

  2. Avviare WinDbg per connettersi a una sessione di debug del kernel nel computer di destinazione usando il comando seguente. Il valore della porta e della chiave deve corrispondere a quello usato per effettuare il provisioning del computer di destinazione. Si userà 50000 per la porta e 1.2.3.4 per la chiave, i valori usati durante il passaggio di distribuzione. Il flag k indica che si tratta di una sessione di debug del kernel.

    WinDbg -k net:port=50000,key=1.2.3.4

  3. Scegliere Interrompi dal menu Debug. Il debugger nel computer host si interromperà nel computer di destinazione. Nella finestra Comando debugger è possibile visualizzare il prompt dei comandi di debug del kernel: kd>.

  4. A questo punto, è possibile sperimentare con il debugger immettendo i comandi al prompt kd> . Ad esempio, è possibile provare questi comandi:

  5. Per consentire l'esecuzione del computer di destinazione, scegliere Vai dal menu Debug oppure premere "g", quindi premere "INVIO".

  6. Per arrestare la sessione di debug, scegliere Scollega debugger dal menu Debug .

    Importante

    Assicurarsi di usare il comando "vai" per consentire l'esecuzione del computer di destinazione prima di uscire dal debugger oppure il computer di destinazione rimarrà non rispondente all'input del mouse e della tastiera perché sta ancora parlando con il debugger.

Per una procedura dettagliata del processo di debug dei driver, vedere Debug dei driver universali - Step by Step Lab (Echo Kernel-Mode).

Per altre informazioni sul debug remoto, vedere Debug remoto tramite WinDbg.

Strumenti di debug per Windows

Eseguire il debug dei driver universali - Step by Step Lab (Echo Kernel-Mode)

Scrivere il primo driver