Escribir un controlador de Windows (KMDF) de Hola mundo
En este artículo se describe cómo escribir un pequeño controlador universal de Windows mediante Kernel-Mode Driver Framework (KMDF) y, a continuación, implementar e instalar el controlador en un equipo independiente.
Antes de continuar, complete los pasos de instalación enumerados en Descargar el Kit de controladores de Windows (WDK).
Las herramientas de depuración para Windows se incluyen al instalar el WDK.
Creación y compilación de un controlador
Abra Microsoft Visual Studio. En el menú Archivo , elija Nuevo > proyecto.
En el cuadro de diálogo Crear un proyecto , seleccione C++ en la lista desplegable de la izquierda, elija Windows en la lista desplegable central y elija Controlador en la lista desplegable derecha.
Seleccione Controlador de modo kernel, Vacío (KMDF) en la lista de tipos de proyecto. Seleccione Next (Siguiente).
En el cuadro de diálogo Configurar el nuevo proyecto , escriba "KmdfHelloWorld" en el campo Nombre del proyecto.
Nota
Al crear un nuevo controlador KMDF o UMDF, debe seleccionar un nombre de controlador que tenga 32 caracteres o menos. Este límite de longitud se define en wdfglobals.h.
En el campo Ubicación , escriba el directorio donde desea crear el nuevo proyecto.
Active Colocar solución y proyecto en el mismo directorio y seleccione Crear.
Visual Studio crea un proyecto y una solución. Puede verlos en la ventana Explorador de soluciones. (Si la ventana Explorador de soluciones no está visible, elija Explorador de soluciones en el menú Ver). La solución tiene un proyecto de controlador denominado KmdfHelloWorld.
En la ventana Explorador de soluciones, seleccione y mantenga presionada (o haga clic con el botón derecho) en la solución KmdfHelloWorld y elija Configuration Manager. Elija una configuración y una plataforma para el proyecto de controlador. Por ejemplo, elija Depurar y x64.
En la ventana de Explorador de soluciones, vuelva a seleccionar y mantenga presionado (o seleccione con el botón derecho) el proyecto KmdfHelloWorld, elija Agregar y, a continuación, seleccione Nuevo elemento.
En el cuadro de diálogo Agregar nuevo elemento , seleccione Archivo de C++. En Nombre, escriba "Driver.c".
Nota
La extensión de nombre de archivo es .c, no .cpp.
Seleccione Agregar. El archivo Driver.c se agrega en Archivos de código fuente, como se muestra aquí.
Escribir el primer código de controlador
Ahora que ha creado el proyecto de Hola mundo vacío y ha agregado el archivo de código fuente Driver.c, escribirá el código más básico necesario para que el controlador se ejecute mediante la implementación de dos funciones básicas de devolución de llamada de eventos.
En Driver.c, empiece por incluir estos encabezados:
#include <ntddk.h> #include <wdf.h>
Sugerencia
Si no puede agregar
Ntddk.h
, abra Configuración -> C/C++ -> General -> Directorios de inclusión adicionales y agregueC:\Program Files (x86)\Windows Kits\10\Include\<build#>\km
, reemplazando por<build#>
el directorio adecuado en la instalación de WDK.Ntddk.h contiene definiciones básicas del kernel de Windows para todos los controladores, mientras que Wdf.h contiene definiciones de controladores basados en Windows Driver Framework (WDF).
A continuación, proporcione declaraciones para las dos devoluciones de llamada que usará:
DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
Use el código siguiente para escribir 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 es el punto de entrada de todos los controladores, como
Main()
es para muchas aplicaciones en modo de usuario. El trabajo de DriverEntry es inicializar recursos y estructuras para todo el controlador. En este ejemplo, imprimió "Hola mundo" para DriverEntry, configuró el objeto de controlador para registrar el punto de entrada de la devolución de llamada evtDeviceAdd y, a continuación, creó el objeto de controlador y devolvió.El objeto de controlador actúa como el objeto primario para todos los demás objetos de marco que puede crear en el controlador, que incluyen objetos de dispositivo, colas de E/S, temporizadores, interbloqueos, etc. Para obtener más información sobre los objetos de marco, vea Introduction to Framework Objects.
Sugerencia
Para DriverEntry, se recomienda mantener el nombre como "DriverEntry" para ayudar con el análisis y la depuración de código.
A continuación, use el código siguiente para escribir 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; }
El sistema invoca EvtDeviceAdd cuando detecta que el dispositivo ha llegado. Su trabajo consiste en inicializar estructuras y recursos para ese dispositivo. En este ejemplo, simplemente imprimió un mensaje de "Hola mundo" para EvtDeviceAdd, creó el objeto de dispositivo y devolvió. En otros controladores que escriba, puede crear colas de E/S para el hardware, configurar un espacio de almacenamiento de contexto de dispositivo para información específica del dispositivo o realizar otras tareas necesarias para preparar el dispositivo.
Sugerencia
En el caso de la devolución de llamada de adición de dispositivo, observe cómo se le ha asignado el nombre del controlador como prefijo (KmdfHelloWorldEvtDeviceAdd). Por lo general, se recomienda asignar un nombre a las funciones del controlador de esta manera para diferenciarlas de otras funciones de los controladores. DriverEntry es el único que debe nombrar exactamente.
El archivo Driver.c completo ahora tiene el siguiente aspecto:
#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; }
Guarde Driver.c.
En este ejemplo se muestra un concepto fundamental de los controladores: son una "colección de devoluciones de llamada" que, una vez inicializada, se sienta y espere a que el sistema los llame cuando necesite algo. Una llamada del sistema podría ser un nuevo evento de llegada de dispositivos, una solicitud de E/S de una aplicación en modo de usuario, un evento de apagado de energía del sistema, una solicitud de otro controlador o un evento de eliminación sorpresa cuando un usuario desconecta el dispositivo inesperadamente. Afortunadamente, para decir "Hola mundo", solo necesitaba preocuparse por la creación de controladores y dispositivos.
A continuación, compilará el controlador.
Compilación del controlador
En la ventana Explorador de soluciones, seleccione y mantenga presionada (o seleccione el botón derecho) Solución 'KmdfHelloWorld' (1 proyecto) y elija Configuration Manager. Elija una configuración y una plataforma para el proyecto de controlador. En este ejercicio, se elige Depurar y x64.
En la ventana Explorador de soluciones, seleccione y mantenga pulsado (o seleccione con el botón derecho) KmdfHelloWorld y elija Propiedades. En Wpp Tracing > All Options (Ejecutar seguimiento de Wpp ) en No. Seleccione Apply (Aplicar) y, después, OK (Aceptar).
Para compilar el controlador, elija Compilar solución en el menú Compilar . Visual Studio muestra el progreso de la compilación en la ventana Salida . (Si la ventana Salida no está visible, elija Salida en el menú Ver ). Cuando haya comprobado que la solución se ha compilado correctamente, puede cerrar Visual Studio.
Para ver el controlador compilado, en Explorador de archivos, vaya a la carpeta KmdfHelloWorld y, a continuación, a x64\Debug\KmdfHelloWorld. La carpeta incluye:
- KmdfHelloWorld.sys: el archivo de controlador en modo kernel
- KmdfHelloWorld.inf: un archivo de información que Windows usa al instalar el controlador.
- KmdfHelloWorld.cat: un archivo de catálogo que el instalador usa para comprobar la firma de prueba del controlador.
Sugerencia
Si ve DriverVer set to a date in the future
al compilar el controlador, cambie la configuración del proyecto de controlador para que Inf2Cat establezca /uselocaltime
. Para ello, use Propiedades de configuración-Inf2Cat-General-Usar>>> hora local. Ahora Stampinf e Inf2Cat usan la hora local.
Implementación del controlador
Normalmente, al probar y depurar un controlador, el depurador y el controlador se ejecutan en equipos independientes. El equipo que ejecuta el depurador se denomina equipo host y el equipo que ejecuta el controlador se denomina equipo de destino. El equipo de destino también se denomina equipo de prueba.
Hasta ahora ha usado Visual Studio para compilar un controlador en el equipo host. Ahora debe configurar un equipo de destino.
Siga las instrucciones de Aprovisionamiento de un equipo para la implementación y pruebas de controladores (WDK 10).
Sugerencia
Cuando siga los pasos para aprovisionar el equipo de destino automáticamente mediante un cable de red, anote el puerto y la clave. Los usará más adelante en el paso de depuración. En este ejemplo, usaremos 50000 como puerto y 1.2.3.4 como clave.
En escenarios de depuración de controladores reales, se recomienda usar una clave generada por KDNET. Para obtener más información sobre cómo usar KDNET para generar una clave aleatoria, consulte el tema Controladores de depuración: laboratorio paso a paso (modo kernel sysvad).
En el equipo host, abra la solución en Visual Studio. Puede hacer doble clic en el archivo de solución, KmdfHelloWorld.sln, en la carpeta KmdfHelloWorld.
En la ventana Explorador de soluciones, seleccione y mantenga presionado (o haga clic con el botón derecho) en el proyecto KmdfHelloWorld y elija Propiedades.
En la ventana Páginas de propiedades de KmdfHelloWorld , vaya a Propiedades de configuración > Instalación > de implementación del controlador, como se muestra aquí.
Active Quitar versiones anteriores del controlador antes de la implementación.
En Nombre del dispositivo de destino, seleccione el nombre del equipo que configuró para probar y depurar. En este ejercicio, usamos un equipo denominado MyTestComputer.
Seleccione Hardware ID Driver Update (Actualización del controlador de identificador de hardware) y escriba el identificador de hardware del controlador. En este ejercicio, el identificador de hardware es Root\KmdfHelloWorld. Seleccione Aceptar.
Nota
En este ejercicio, el identificador de hardware no identifica un elemento real de hardware. Identifica un dispositivo imaginario que se asignará a un lugar en el árbol de dispositivos como elemento secundario del nodo raíz. Para hardware real, no seleccione Hardware ID Driver Update; en su lugar, seleccione Instalar y comprobar. Verá el identificador de hardware en el archivo de información del controlador (INF). En la ventana Explorador de soluciones, vaya a Archivos de controlador KmdfHelloWorld y haga doble clic en KmdfHelloWorld.inf>. El identificador de hardware se encuentra en [Standard.NT$ARCH$].
[Standard.NT$ARCH$] %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
En el menú Compilar , elija Implementar solución. Visual Studio copia automáticamente los archivos necesarios para instalar y ejecutar el controlador en el equipo de destino. La implementación puede tardar un minuto o dos.
Al implementar un controlador, los archivos de controlador se copian en la carpeta %Systemdrive%\drivertest\drivers del equipo de prueba. Si algo va mal durante la implementación, puede comprobar si los archivos se copian en el equipo de prueba. Compruebe que los archivos .inf, .cat, cert de prueba y .sys, y cualquier otro archivo necesario, estén presentes en la carpeta %systemdrive%\drivertest\drivers.
Para obtener más información sobre cómo implementar controladores, consulte Implementación de un controlador en un equipo de prueba.
Instalación del controlador
Con el controlador Hola mundo implementado en el equipo de destino, ahora instalará el controlador. Cuando aprovisionó previamente el equipo de destino con Visual Studio con la opción automática , Visual Studio configuró el equipo de destino para ejecutar controladores firmados de prueba como parte del proceso de aprovisionamiento. Ahora solo tiene que instalar el controlador mediante la herramienta DevCon.
En el equipo host, vaya a la carpeta Herramientas de la instalación de WDK y busque la herramienta DevCon. Por ejemplo, busque en la carpeta siguiente:
C:\Archivos de programa (x86)\Windows Kits\10\Tools\x64\devcon.exe
Copie la herramienta DevCon en el equipo remoto.
En el equipo de destino, instale el controlador; para ello, vaya a la carpeta que contiene los archivos de controlador y, a continuación, ejecute la herramienta DevCon.
Esta es la sintaxis general de la herramienta devcon que usará para instalar el controlador:
devcon install <INF file><hardware ID>
El archivo INF necesario para instalar este controlador es KmdfHelloWorld.inf. El archivo INF contiene el identificador de hardware para instalar el binario del controlador, KmdfHelloWorld.sys. Recuerde que el identificador de hardware, ubicado en el archivo INF, es Root\KmdfHelloWorld.
Abra una ventana del símbolo del sistema como administrador. Vaya a la carpeta que contiene el archivo .sys del controlador compilado y escriba este comando:
devcon install kmdfhelloworld.inf root\kmdfhelloworld
Si recibe un mensaje de error sobre devcon que no se reconoce, intente agregar la ruta de acceso a la herramienta devcon . Por ejemplo, si lo copió en una carpeta del equipo de destino llamado C:\Tools, pruebe a usar el siguiente comando:
c:\tools\devcon install kmdfhelloworld.inf root\kmdfhelloworld
Aparecerá un cuadro de diálogo que indica que el controlador de prueba es un controlador sin firmar. Seleccione Instalar este controlador de todos modos para continuar.
Depuración del controlador
Ahora que ha instalado el controlador KmdfHelloWorld en el equipo de destino, asociará un depurador de forma remota desde el equipo host.
En el equipo host, abra una ventana del símbolo del sistema como administrador. Cambie al directorio WinDbg.exe. Usaremos la versión x64 de WinDbg.exe del Kit de controladores de Windows (WDK) que se instaló como parte de la instalación del kit de Windows. Esta es la ruta de acceso predeterminada a WinDbg.exe:
C:\Archivos de programa (x86)\Windows Kits\10\Debuggers\x64
Inicie WinDbg para conectarse a una sesión de depuración del kernel en el equipo de destino mediante el siguiente comando. El valor del puerto y la clave deben ser los mismos que los que usó para aprovisionar el equipo de destino. Usaremos 50000 para el puerto y 1.2.3.4 para la clave, los valores que usamos durante el paso de implementación. La marca k indica que se trata de una sesión de depuración del kernel.
WinDbg -k net:port=50000,key=1.2.3.4
En el menú Depurar , elija Interrumpir. El depurador del equipo host se dividirá en el equipo de destino. En la ventana Comando del depurador , puede ver el símbolo del sistema de depuración del kernel: kd>.
En este momento, puede experimentar con el depurador escribiendo comandos en el símbolo del sistema kd> . Por ejemplo, podría probar estos comandos:
Para permitir que el equipo de destino vuelva a ejecutarse, elija Ir en el menú Depurar o presione "g" y presione "entrar".
Para detener la sesión de depuración, elija Desasociar depurador en el menú Depurar .
Importante
Asegúrese de usar el comando "go" para permitir que el equipo de destino vuelva a ejecutarse antes de salir del depurador, o el equipo de destino seguirá sin responder a la entrada del mouse y del teclado porque sigue hablando con el depurador.
Para obtener un tutorial detallado paso a paso del proceso de depuración de controladores, consulte Depuración de controladores universales: laboratorio paso a paso (modo kernel de eco) .
Para obtener más información sobre la depuración remota, vea Depuración remota mediante WinDbg.
Artículos relacionados
Herramientas de depuración para Windows
Depuración de controladores universales: laboratorio paso a paso (modo kernel de eco)