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
Aprire Microsoft Visual Studio. Scegliere Nuovo > progetto dal menu File.
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.
Selezionare Driver in modalità kernel, Vuoto (KMDF) nell'elenco dei tipi di progetto. Selezionare Avanti.
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.
Nel campo Percorso immettere la directory in cui si vuole creare il nuovo progetto.
Selezionare Inserisci soluzione e progetto nella stessa directory e selezionare Crea.
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.
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.
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.
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.
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.
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 aggiungereC:\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).
Specificare quindi le dichiarazioni per i due callback che verranno usati:
DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
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.
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.
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; }
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
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.
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.
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.
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.
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).
Nel computer host aprire la soluzione in Visual Studio. È possibile fare doppio clic sul file della soluzione, KmdfHelloWorld.sln, nella cartella KmdfHelloWorld.
Nella finestra Esplora soluzioni selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) sul progetto KmdfHelloWorld e scegliere Proprietà.
Nella finestra Pagine delle proprietà KmdfHelloWorld passare a Proprietà > di configurazione Installazione driver>, come illustrato di seguito.
Selezionare Rimuovi versioni precedenti del driver prima della distribuzione.
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.
Selezionare Aggiornamento driver ID hardware e immettere l'ID hardware per il driver. Per questo esercizio, l'ID hardware è Root\KmdfHelloWorld. Selezionare OK.
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
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.
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.
Nel computer di destinazione installare il driver passando alla cartella contenente i file del driver, quindi eseguendo lo strumento DevCon.
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.
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.
Eseguire il debug del driver
Dopo aver installato il driver KmdfHelloWorld nel computer di destinazione, si collega un debugger in remoto dal computer host.
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
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
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>.
A questo punto, è possibile sperimentare con il debugger immettendo i comandi al prompt kd> . Ad esempio, è possibile provare questi comandi:
Per consentire l'esecuzione del computer di destinazione, scegliere Vai dal menu Debug oppure premere "g", quindi premere "INVIO".
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.
Articoli correlati
Strumenti di debug per Windows
Eseguire il debug dei driver universali - Step by Step Lab (Echo Kernel-Mode)