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"