Virtual HID Touch Driver (VHF) Fails to Start (VhfCreate returns 0xC0000010 / Device Manager Code 10)

I am trying to create a Virtual HID Touch driver using the Virtual HID Framework (VHF). The goal is to enumerate a “fake” touchscreen device under Human Interface Devices and send touch events without moving the mouse cursor. However, after installing the INF, the driver ends up in Device Manager as:

This device cannot start. (Code 10)
STATUS_DEVICE_POWER_FAILURE

What am I doing wrong?

…and the call to VhfCreate(&cfg, &g_Vhf) consistently returns 0xC0000010 (STATUS_INVALID_DEVICE_REQUEST).

1. Environment

  • OS: Windows 11 (Build 26000+), 64-bit
  • Visual Studio: 2019/2022 with WDK 10.0.26100.xxxx installed
  • WDK Version: 10.0.26100.xxxx (installed via Visual Studio Installer)
  • Driver Model: KMDF (KMDF 1.33)
  • Target Hardware ID (root-enumerated): Root\KMDFDriver3
  • Signing: Test certificate loaded into “Trusted Publishers” and “Trusted Root Certification Authorities”
  • bcdedit /set testsigning on

driver.c:

// Driver.c
// Minimal KMDF-based Virtual HID Touch driver using VHF

#include <ntddk.h>
#include <wdf.h>
#include <vhf.h>         // Virtual HID Framework
#include <initguid.h>
#include <wdmguid.h>     // For GUID_DEVINTERFACE_HID

// Forward declarations
DRIVER_INITIALIZE                         DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD                 Vhid_EvtDriverDeviceAdd;
EVT_WDF_DRIVER_UNLOAD                     Vhid_EvtDriverUnload;
EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT       Vhid_EvtDeviceSelfManagedIoInit;
EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP    Vhid_EvtDeviceSelfManagedIoCleanup;

// Global VHF handle
VHFHANDLE g_Vhf = NULL;

// HID report descriptor for a single-touch contact
const UCHAR TouchReportDescriptor[] = {
    0x05, 0x0D,                    // USAGE_PAGE (Digitizers)
    0x09, 0x04,                    // USAGE (Touch Screen)
    0xA1, 0x01,                    // COLLECTION (Application)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x09, 0x22,                    //   USAGE (Finger)
    0xA1, 0x00,                    //   COLLECTION (Physical)
    0x09, 0x42,                    //     USAGE (Tip Switch)
    0x09, 0x32,                    //     USAGE (In Range)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)

    0x95, 0x06,                    //     REPORT_COUNT (6) padding
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)

    0x09, 0x51,                    //     USAGE (Contact Identifier)
    0x25, 0x7F,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x26, 0xFF, 0x0F,              //     LOGICAL_MAXIMUM (4095)
    0x75, 0x10,                    //     REPORT_SIZE (16)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xC0,                          //   END_COLLECTION (Physical)
    0xC0                           // END_COLLECTION (Application)
};


// Ensure this line is included to define GUID_DEVINTERFACE_HID  
DEFINE_GUID(GUID_DEVINTERFACE_HID,
    0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);


//-----------------------------------------------------------------------------
// Vhid_EvtDriverUnload: clean up on driver unload
//-----------------------------------------------------------------------------
_Use_decl_annotations_
VOID
Vhid_EvtDriverUnload(
    WDFDRIVER Driver
)
{
    UNREFERENCED_PARAMETER(Driver);
    if (g_Vhf) {
        VhfDelete(g_Vhf, TRUE);
        g_Vhf = NULL;
    }
}


_Use_decl_annotations_
NTSTATUS
Vhid_EvtDriverDeviceAdd(
    WDFDRIVER         Driver,
    PWDFDEVICE_INIT   DeviceInit
)
{
    NTSTATUS status;
    WDFDEVICE device;

    UNREFERENCED_PARAMETER(Driver);

  
    KdPrint(("Vhid: entering Vhid_EvtDriverDeviceAdd\n"));
    
    // 1) Install Self-Managed I/O callbacks for VHF
    {
        WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
        WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);
        pnpCallbacks.EvtDeviceSelfManagedIoInit = Vhid_EvtDeviceSelfManagedIoInit;
        pnpCallbacks.EvtDeviceSelfManagedIoCleanup = Vhid_EvtDeviceSelfManagedIoCleanup;
        WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks);
    }

    // 2) Create the KMDF device object
    KdPrint(("Vhid: calling WdfDeviceCreate\n"));
    status = WdfDeviceCreate(
        &DeviceInit,
        WDF_NO_OBJECT_ATTRIBUTES,
        &device
    );
    
    if (!NT_SUCCESS(status)) {
        KdPrint(("Vhid: WdfDeviceCreate failed 0x%x\n", status));
        return status;
    }

    KdPrint(("Vhid: WdfDeviceCreate succedded 0x%x\n", status));

    // 3) Publish a HID device interface so apps can find us

    KdPrint(("Vhid: calling WdfDeviceCreateDeviceInterface"));
    status = WdfDeviceCreateDeviceInterface(
        device,
        (LPCGUID)&GUID_DEVINTERFACE_HID, // Correct type cast to match function signature
        NULL
    );
    KdPrint(("Vhid: WdfDeviceCreateDeviceInterface status 0x%x\n", status));

    return status;
}

//-----------------------------------------------------------------------------
// Vhid_EvtDeviceSelfManagedIoInit:
//   PnP has started us—all dependencies are up.  Now we can create/start
//   the VHF virtual-hid interface.
//-----------------------------------------------------------------------------
_Use_decl_annotations_
NTSTATUS
Vhid_EvtDeviceSelfManagedIoInit(
    WDFDEVICE device
)
{
    PDEVICE_OBJECT pdo = WdfDeviceWdmGetDeviceObject(device);
    VHF_CONFIG cfg;

    VHF_CONFIG_INIT(
        &cfg,
        pdo,
        (USHORT)sizeof(TouchReportDescriptor),
        (PUCHAR)TouchReportDescriptor
    );

    // Set HardwareIDs to match INF and devcon
    static const WCHAR hwIds[] = L"Root\\KMDFDriver3";
    cfg.HardwareIDs = (PWSTR)hwIds;
    cfg.HardwareIDsLength = sizeof(hwIds);

    cfg.InstanceID = NULL;
    cfg.InstanceIDLength = 0;

    // Optional: set VID/PID/Version
    cfg.VendorID = 0x045E;
    cfg.ProductID = 0x1234;
    cfg.VersionNumber = 0x0001;

    // Explicitly set all optional callbacks to NULL
    cfg.EvtVhfReadyForNextReadReport = NULL;
    cfg.EvtVhfAsyncOperationGetFeature = NULL;
    cfg.EvtVhfAsyncOperationSetFeature = NULL;
    cfg.EvtVhfAsyncOperationWriteReport = NULL;
    cfg.EvtVhfAsyncOperationGetInputReport = NULL;
    cfg.EvtVhfCleanup = NULL;

    KdPrint(("Vhid: VHF_CONFIG Size=%u, PDO=%p, RptLen=%u\n",
        cfg.Size, pdo, cfg.ReportDescriptorLength));

    NTSTATUS st = VhfCreate(&cfg, &g_Vhf);
    KdPrint(("Vhid: VhfCreate = 0x%x\n", st));
    if (NT_SUCCESS(st)) {
        VhfStart(g_Vhf);
    }
    return st;
}

//-----------------------------------------------------------------------------
// Vhid_EvtDeviceSelfManagedIoCleanup:
//   Called when PnP is tearing us down—delete the VHF interface.
//-----------------------------------------------------------------------------
_Use_decl_annotations_
VOID
Vhid_EvtDeviceSelfManagedIoCleanup(
    WDFDEVICE device
)
{
    UNREFERENCED_PARAMETER(device);
    if (g_Vhf) {
        VhfDelete(g_Vhf, TRUE);
        g_Vhf = NULL;
    }
}

//-----------------------------------------------------------------------------
// DriverEntry: standard KMDF setup
//-----------------------------------------------------------------------------
_Use_decl_annotations_
NTSTATUS
DriverEntry(
    PDRIVER_OBJECT   DriverObject,
    PUNICODE_STRING  RegistryPath
)
{
    WDF_DRIVER_CONFIG config;
    WDF_DRIVER_CONFIG_INIT(&config, Vhid_EvtDriverDeviceAdd);
    config.EvtDriverUnload = Vhid_EvtDriverUnload;

    return WdfDriverCreate(
        DriverObject,
        RegistryPath,
        WDF_NO_OBJECT_ATTRIBUTES,
        &config,
        WDF_NO_HANDLE
    );
}

Inf File:


[Version]
Signature    = "$WINDOWS NT$"
Class        = HIDClass
ClassGuid    = {745A17A0-74D3-11D0-B6FE-00A0C90F57DA}
Provider     = %Mfg%
DriverVer    = 05/28/2025,1.0.0.0
CatalogFile  = KMDFDriver3.cat

[DestinationDirs]
DefaultDestDir = 12  ; %SystemRoot%\System32\drivers

[SourceDisksNames]
1 = %DiskName%

[SourceDisksFiles]
KMDFDriver3.sys = 1

[Manufacturer]
%Mfg% = Std,NT$ARCH$

[Std.NT$ARCH$]
%DeviceDesc% = KMDFTouch_Install, Root\KMDFDriver3

[KMDFTouch_Install.NT]
CopyFiles    = DriverCopy
Include      = hidclass.inf
Needs        = HID_Inst.NT

[DriverCopy]
KMDFDriver3.sys

[KMDFTouch_Install.NT.Services]
AddService   = KMDFDriver3,0x00000002,KMDFTouch_Svc

[KMDFTouch_Svc]
DisplayName  = %SvcName%
ServiceType  = 1
StartType    = 3
ErrorControl = 1
ServiceBinary= %12%\KMDFDriver3.sys

[KMDFTouch_Install.NT.Wdf]
KmdfService         = KMDFDriver3,KMDFTouch_Wdf

[KMDFTouch_Wdf]
KmdfLibraryVersion  = 1.33

[Strings]
Mfg         = "Virtual HID Manufacturer"
DeviceDesc  = "Virtual HID Touch Device"
SvcName     = "Virtual HID Touch Service"
DiskName    = "Virtual HID Touch Driver Disk"