Compartir a través de


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

  1. Abra Microsoft Visual Studio. En el menú Archivo , elija Nuevo > proyecto.

  2. 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.

  3. Seleccione Controlador de modo kernel, Vacío (KMDF) en la lista de tipos de proyecto. Seleccione Next (Siguiente).

    Captura de pantalla del cuadro de diálogo Nuevo proyecto de Visual Studio con la opción controlador en modo kernel seleccionada.

  4. 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.

  5. En el campo Ubicación , escriba el directorio donde desea crear el nuevo proyecto.

  6. Active Colocar solución y proyecto en el mismo directorio y seleccione Crear.

    Captura de pantalla del cuadro de diálogo Configurar el nuevo proyecto con el botón Crear resaltado.

    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.

    Captura de pantalla de la ventana del Explorador de soluciones de Visual Studio que muestra la solución y el proyecto de controlador vacío denominado KmdfHelloWorld.

  7. 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.

  8. 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.

  9. 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í.

    Captura de pantalla de la ventana del Explorador de soluciones de Visual Studio que muestra el archivo driver.c agregado al proyecto de controlador.

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.

  1. 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 agregue C:\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).

  2. A continuación, proporcione declaraciones para las dos devoluciones de llamada que usará:

    DRIVER_INITIALIZE DriverEntry;
    EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
    
  3. 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.

  4. 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.

  5. 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;
    }
    
  6. 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

  1. 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.

  2. 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).

  3. 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.

  4. 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.

  1. 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).

  2. 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.

  3. 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.

  4. 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í.

  5. Active Quitar versiones anteriores del controlador antes de la implementación.

  6. 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.

  7. 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.

    Captura de pantalla de la ventana de páginas de propiedades kmdfhelloworld con la opción de instalación del controlador de implementación seleccionada.

    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
    
  8. 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.

  1. 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.

  2. 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.

    1. 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.

    2. 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.

      Captura de pantalla de la advertencia de seguridad que se muestra durante el proceso de instalación del controlador.

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.

  1. 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

  2. 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

  3. 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>.

  4. En este momento, puede experimentar con el depurador escribiendo comandos en el símbolo del sistema kd> . Por ejemplo, podría probar estos comandos:

  5. Para permitir que el equipo de destino vuelva a ejecutarse, elija Ir en el menú Depurar o presione "g" y presione "entrar".

  6. 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.

Herramientas de depuración para Windows

Depuración de controladores universales: laboratorio paso a paso (modo kernel de eco)

Escribir el primer controlador