How to simulate virtual keyboard input in windows kernel driver?

I’m making my windows kernel driver to simulate keyboard input when timer is triggered, and using devcon.exe to install/uninstall the driver for testing.
According to the Microsoft guide, I have relevant codes to my basic KMDF project. Currently I can install the driver and confirm that timer is correctly triggered, but virtual keyboard simulation doesn’t work.

Here is my driver code.

#pragma once

#include <ntddk.h>
#include <wdf.h>
#include <vhf.h>
#include <initguid.h>

DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD WakeUp_Mon_KMDFEvtDeviceAdd;
EVT_WDF_OBJECT_CONTEXT_CLEANUP WakeUp_Mon_KMDFEvtDriverContextCleanup;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL WakeUp_Mon_KMDFEvtIoDeviceControl;
EVT_WDF_TIMER WakeUp_Mon_KMDFEvtTimerFunc;

typedef struct _DEVICE_CONTEXT {
    WDFTIMER Timer;
    ULONG TimerPeriod;
    VHFHANDLE VhfHandle;
    BOOLEAN TimerRunning;
} DEVICE_CONTEXT, * PDEVICE_CONTEXT;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext);

#define IOCTL_SET_TIMER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CANCEL_TIMER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

DEFINE_GUID(GUID_DEVINTERFACE_WAKEUP_MON_KMDF,
    0xe66df535, 0x8ae7, 0x4216, 0x9b, 0xc3, 0x92, 0x0b, 0x32, 0xb8, 0x21, 0x3b);

typedef struct _TIMER_DATA {
    ULONG DueTimeInSeconds;
} TIMER_DATA, * PTIMER_DATA;

UCHAR g_KeyboardReportDescriptor[] = {
    0x05, 0x01,        // Usage Page (Generic Desktop)
    0x09, 0x06,        // Usage (Keyboard)
    0xa1, 0x01,        // Collection (Application)
    0x05, 0x07,        // Usage Page (Key Codes)
    0x19, 0xe0,        // Usage Minimum (224)
    0x29, 0xe7,        // Usage Maximum (231)
    0x15, 0x00,        // Logical Minimum (0)
    0x25, 0x01,        // Logical Maximum (1)
    0x75, 0x01,        // Report Size (1)
    0x95, 0x08,        // Report Count (8)
    0x81, 0x02,        // Input (Data, Variable, Absolute) ; Modifier byte
    0x95, 0x01,        // Report Count (1)
    0x75, 0x08,        // Report Size (8)
    0x81, 0x01,        // Input (Constant) ; Reserved byte
    0x95, 0x06,        // Report Count (6)
    0x75, 0x08,        // Report Size (8)
    0x15, 0x00,        // Logical Minimum (0)
    0x25, 0x65,        // Logical Maximum (101)
    0x05, 0x07,        // Usage Page (Key Codes)
    0x19, 0x00,        // Usage Minimum (0)
    0x29, 0x65,        // Usage Maximum (101)
    0x81, 0x00,        // Input (Data, Array) ; Key arrays (6 bytes)
    0xc0               // End Collection
};


VOID SendHIDInput(PDEVICE_CONTEXT DeviceContext, UCHAR KeyCode, BOOLEAN KeyDown);

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
)
{
    WDF_DRIVER_CONFIG config;
    NTSTATUS status;

    DbgPrint("DriverEntryn");

    WDF_DRIVER_CONFIG_INIT(&config, WakeUp_Mon_KMDFEvtDeviceAdd);
    config.EvtDriverUnload = WakeUp_Mon_KMDFEvtDriverContextCleanup;

    status = WdfDriverCreate(DriverObject,
        RegistryPath,
        WDF_NO_OBJECT_ATTRIBUTES,
        &config,
        WDF_NO_HANDLE);

    if (!NT_SUCCESS(status)) {
        DbgPrint("WdfDriverCreate failed with status 0x%xn", status);
        return status;
    }

    DbgPrint("DriverEntry finished with status 0x%xn", status);
    return status;
}

NTSTATUS
WakeUp_Mon_KMDFEvtDeviceAdd(
    _In_    WDFDRIVER       Driver,
    _Inout_ PWDFDEVICE_INIT DeviceInit
)
{
    WDF_OBJECT_ATTRIBUTES deviceAttributes;
    WDFDEVICE device;
    WDF_IO_QUEUE_CONFIG ioQueueConfig;
    NTSTATUS status;
    PDEVICE_CONTEXT deviceContext;
    WDF_TIMER_CONFIG timerConfig;
    VHF_CONFIG vhfConfig;

    UNREFERENCED_PARAMETER(Driver);

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
    deviceAttributes.EvtCleanupCallback = WakeUp_Mon_KMDFEvtDriverContextCleanup;

    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        DbgPrint("WdfDeviceCreate failed with status 0x%xn", status);
        return status;
    }

    deviceContext = DeviceGetContext(device);
    deviceContext->TimerRunning = FALSE;  // Initialize timer state

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchParallel);
    ioQueueConfig.EvtIoDeviceControl = WakeUp_Mon_KMDFEvtIoDeviceControl;

    status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE);
    if (!NT_SUCCESS(status)) {
        DbgPrint("WdfIoQueueCreate failed with status 0x%xn", status);
        return status;
    }

    WDF_TIMER_CONFIG_INIT(&timerConfig, WakeUp_Mon_KMDFEvtTimerFunc);
    WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes);
    deviceAttributes.ParentObject = device;
    status = WdfTimerCreate(&timerConfig, &deviceAttributes, &deviceContext->Timer);
    if (!NT_SUCCESS(status)) {
        DbgPrint("Failed to create Wdf timer with status 0x%xn", status);
        return status;
    }

    VHF_CONFIG_INIT(&vhfConfig,
        WdfDeviceWdmGetDeviceObject(device),
        sizeof(g_KeyboardReportDescriptor),
        g_KeyboardReportDescriptor);

    status = VhfCreate(&vhfConfig, &deviceContext->VhfHandle);
    if (!NT_SUCCESS(status)) {
        DbgPrint("VhfCreate failed with status 0x%xn", status);
        return status;
    }

    status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_WAKEUP_MON_KMDF, NULL);
    if (!NT_SUCCESS(status)) {
        DbgPrint("WdfDeviceCreateDeviceInterface failed with status 0x%xn", status);
        return status;
    }

    status = VhfStart(deviceContext->VhfHandle);
    if (!NT_SUCCESS(status)) {
        DbgPrint("VhfStart failed with status 0x%xn", status);
        return status;
    }

    DbgPrint("DeviceAdd callback finished with status 0x%xn", status);
    return STATUS_SUCCESS;
}

VOID
WakeUp_Mon_KMDFEvtDriverContextCleanup(
    _In_ WDFOBJECT DriverObject
)
{
    PDEVICE_CONTEXT deviceContext = DeviceGetContext(DriverObject);

    // Clean up the VHF handle if it was created
    if (deviceContext->VhfHandle != WDF_NO_HANDLE)
    {
        VhfDelete(deviceContext->VhfHandle, TRUE);
        deviceContext->VhfHandle = NULL;
    }

    // Ensure the timer is stopped and deleted
    if (deviceContext->TimerRunning) {
        WdfTimerStop(deviceContext->Timer, TRUE);
    }
    WdfObjectDelete(deviceContext->Timer);
}

VOID
WakeUp_Mon_KMDFEvtIoDeviceControl(
    _In_ WDFQUEUE Queue,
    _In_ WDFREQUEST Request,
    _In_ size_t OutputBufferLength,
    _In_ size_t InputBufferLength,
    _In_ ULONG IoControlCode
)
{
    NTSTATUS status = STATUS_SUCCESS;
    PDEVICE_CONTEXT deviceContext;
    PTIMER_DATA timerData;
    size_t bytesReturned;

    deviceContext = DeviceGetContext(WdfIoQueueGetDevice(Queue));

    UNREFERENCED_PARAMETER(OutputBufferLength);

    switch (IoControlCode) {
    case IOCTL_SET_TIMER:
        if (InputBufferLength < sizeof(TIMER_DATA)) {
            status = STATUS_BUFFER_TOO_SMALL;
            DbgPrint("Input buffer too small. Required size: %zu, Provided size: %zun", sizeof(TIMER_DATA), InputBufferLength);
            break;
        }

        status = WdfRequestRetrieveInputBuffer(Request, sizeof(TIMER_DATA), (PVOID*)&timerData, &bytesReturned);
        if (!NT_SUCCESS(status)) {
            DbgPrint("WdfRequestRetrieveInputBuffer failed with status 0x%xn", status);
            break;
        }

        // Set the timer
        deviceContext->TimerPeriod = (ULONGLONG)(timerData->DueTimeInSeconds) * 1000;
        WdfTimerStart(deviceContext->Timer, WDF_REL_TIMEOUT_IN_MS(deviceContext->TimerPeriod));
        deviceContext->TimerRunning = TRUE;
        DbgPrint("Set wake timer with due time %lldn", deviceContext->TimerPeriod);
        break;

    case IOCTL_CANCEL_TIMER:
        if (WdfTimerStop(deviceContext->Timer, TRUE)) {
            deviceContext->TimerRunning = FALSE;
            DbgPrint("Timer canceledn");
        }
        else {
            status = STATUS_INVALID_DEVICE_STATE;
            DbgPrint("No running timer existsn");
        }
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

    WdfRequestComplete(Request, status);
}

VOID
WakeUp_Mon_KMDFEvtTimerFunc(
    WDFTIMER Timer
)
{
    PDEVICE_CONTEXT deviceContext = DeviceGetContext(WdfTimerGetParentObject(Timer));

    if (!deviceContext->TimerRunning) {
        DbgPrint("Timer function called but Timer is not running. Ignoring...n");
        return;
    }

    DbgPrint("Timer triggeredn");

    SendHIDInput(deviceContext, 0x04, TRUE);  // Simulate pressing 'A'
    SendHIDInput(deviceContext, 0x04, FALSE); // Simulate releasing 'A'

    deviceContext->TimerRunning = FALSE;
    DbgPrint("Timer DPC Routine finishedn");
}

VOID SendHIDInput(PDEVICE_CONTEXT DeviceContext, UCHAR KeyCode, BOOLEAN KeyDown)
{
    UCHAR Report[] = {
        0x01, // Report ID
        0x00, // Modifier keys
        0x00, // Reserved
        0x00, // Keycode 1 (will be set dynamically)
        0x00, // Keycode 2
        0x00, // Keycode 3
        0x00, // Keycode 4
        0x00, // Keycode 5
        0x00  // Keycode 6
    };

    if (KeyDown) {
        Report[3] = KeyCode; // Set the key code
    }

    HID_XFER_PACKET packet;
    packet.reportBuffer = Report;
    packet.reportBufferLen = sizeof(Report);
    packet.reportId = Report[0]; // The Report ID

    NTSTATUS status = VhfReadReportSubmit(DeviceContext->VhfHandle, &packet);
    if (!NT_SUCCESS(status)) {
        DbgPrint("VhfReadReportSubmit failed with status 0x%xn", status);
    }
}
```
`
And this is driver inf file;
`;
; WakeUp_Mon_KMDF.inf
;

[Version]
Signature="$WINDOWS NT$"
Class=System ; TODO: specify appropriate Class
ClassGuid={e66df535-8ae7-4216-9bc3-920b32b8213b}
Provider=%ManufacturerName%
CatalogFile=WakeUp_Mon_KMDF.cat
DriverVer = 08/12/2024,12.9.24.578
PnpLockdown=1

[DestinationDirs]
DefaultDestDir = 12
WakeUp_Mon_KMDF_Device_CoInstaller_CopyFiles = 11

[SourceDisksNames]
1 = %DiskName%,,,""

[SourceDisksFiles]
WakeUp_Mon_KMDF.sys  = 1,,
;


;*****************************************
; Install Section
;*****************************************

[Manufacturer]
%ManufacturerName%=Standard,NTamd64

[Standard.NTamd64]
%WakeUp_Mon_KMDF.DeviceDesc%=WakeUp_Mon_KMDF_Device, RootWakeUp_Mon_KMDF ; TODO: edit hw-id

[WakeUp_Mon_KMDF_Device.NT]
CopyFiles=Drivers_Dir

[Drivers_Dir]
WakeUp_Mon_KMDF.sys

;-------------- Service installation
[WakeUp_Mon_KMDF_Device.NT.Services]
AddService = WakeUp_Mon_KMDF,%SPSVCINST_ASSOCSERVICE%, WakeUp_Mon_KMDF_Service_Inst

; -------------- WakeUp_Mon_KMDF driver install sections
[WakeUp_Mon_KMDF_Service_Inst]
DisplayName    = %WakeUp_Mon_KMDF.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%WakeUp_Mon_KMDF.sys

;
;--- WakeUp_Mon_KMDF_Device Coinstaller installation ------
;

[WakeUp_Mon_KMDF_Device.NT.CoInstallers]
AddReg=WakeUp_Mon_KMDF_Device_CoInstaller_AddReg
CopyFiles=WakeUp_Mon_KMDF_Device_CoInstaller_CopyFiles

[WakeUp_Mon_KMDF_Device_CoInstaller_AddReg]
;


[WakeUp_Mon_KMDF_Device_CoInstaller_CopyFiles]
;


[WakeUp_Mon_KMDF_Device.NT.Wdf]
KmdfService =  WakeUp_Mon_KMDF, WakeUp_Mon_KMDF_wdfsect
[WakeUp_Mon_KMDF_wdfsect]
KmdfLibraryVersion = 1.15

; --- Add the required VHF LowerFilters registry setting ---
[WakeUp_Mon_KMDF_Device.NT.HW]
AddReg = WakeUp_Mon_KMDF_Device_HW_AddReg

[WakeUp_Mon_KMDF_Device_HW_AddReg]
HKR,,LowerFilters,0x00010000,"vhf"

[Strings]
SPSVCINST_ASSOCSERVICE= 0x00000002
ManufacturerName="<Your manufacturer name>" ;TODO: Replace with your manufacturer name
DiskName = "WakeUp_Mon_KMDF Installation Disk"
WakeUp_Mon_KMDF.DeviceDesc = "WakeUp_Mon_KMDF Device"
WakeUp_Mon_KMDF.SVCDESC = "WakeUp_Mon_KMDF Service"`

There is one more suspicious thing, when I remove the driver, it keeps failing. I can see that there are newly created my driver's device in Device manager, but can't delete it manually even. I have noticed that new virtual HID device is also created in Device manager, when I uninstall it, then "uninstall" option is available to the driver device too, but when I remove it, BSOD occurs.

I have checked if there are any misconfigured options in inf file regarding VHF usage, and searched alternative solutions, but didn't find. My expectation is driver should simulate keyboard input 'A' when timer is triggered, and also be able to be uninstalled and installed flexibly.

I'm still in starting point to learn KMDF programming, please help me.

New contributor

Lucifer is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật