Hi all,
I wrote about this in another thread some time ago.
A few months back, one of my clients has asked me to find a way to block all touch input in Windows 7 and 8+ and then redirect that input to a custom API that lives in user mode (implemented in C#/.NET). Mouse/Keyboard input, as well as the way it makes the system react, should not be affected at all.
The client is in the business of creating touch-enabled multi-screen systems that offer human-machine interaction in physical environments (like smart homes). This HM interaction spans the screens and uses its own interaction paradigm; therefore, he wants to ensure that Windows’ own touch related stuff stays out of the way as much as possible. Ideally, we would want to be able to install something that will block touch input from ANY touch sensor, including screens, touch-pads, etc., and not just specific touch input devices.
When I was first assigned the project I thought of developing an upper hidclass filter driver that would somehow find out which IRPs come from touch devices and which not and block/redirect only touch.
I quickly found out that this is not possible. I developed a simple class filter driver (code below) based on a WDK sample and was able to monitor IRPs (using Dbgview) as they were passing through when the filter installed as, say, an upper class filter to a class like “Image”, e.g. there would be a continuous stream of IRPs being listed in Dbgview by my KdPrint() statement when a camera is in use. However, when used for the “hidclass” in the same way, although the driver would install and load fine, it would only list a few IRPs at device start-up but then as touch operations were in progress nothing would show up.
After consulting other posts on the subject on this list as well as answers I received on my original post I understand that a hidclass upper filter driver could not work the way I want because HID communication through the driver stack is not fully done with IRPs.
Since then I have tried doing the mouse/touch input discrimination I need at the windows message processing level. Although I was successful in injecting all processes with a DLL that subclasses all Windows and then hooks into their message flow, the way this is implemented does not allow effective discrimination between touch and mouse for ALL messages. For instance although Windows has included some unofficial headers to help with discrimination of touch messages (e.g. #define MOUSEEVENTF_FROMTOUCH 0xFF515700 if ((GetMessageExtraInfo() & MOUSEEVENTF_FROMTOUCH) == MOUSEEVENTF_FROMTOUCH) { // Skip message}) this won’t work well with WM_NCACTIVATE and WM_NCHITTEST type of messages. This makes it impossible if not very hard to have zero impact on the way mouse works when blocking touch.
SO, I was forced to look into the filter driver approach once more. Since the filter won’t do what I want when installed as an upper class filter I tried to install it as a filter directly on the device (added an UpperFilters entry in the registry key for the usb enumerated device of my touch screen). This will load the filter driver on device restart but nothing shows and the devicemanager icon locks up when I do it.
So, 2 questions I guess:
-
Do I need to change something in the following code to make it work as a device filter instead of a class filter?
-
Is there some other place in the driver stack that I can install this filter in that will give me access to touch related IRPs that I can block/redirect?
Many thanks,
Alex Fotios
#include “filter.h”
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, FilterEvtDeviceAdd)
#endif
/////////////////////////////////////////////////////////////////////////////
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
WDFDRIVER hDriver;
UINT i;
//////////////////////////////////////////////////////////////////////////////////////////////////////////
KdPrint((“WDF Filter Driver\n”));
KdPrint((“Built %s %s\n”, DATE, TIME));
WDF_DRIVER_CONFIG_INIT(
&config,
FilterEvtDeviceAdd
);
status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
&hDriver);
if (!NT_SUCCESS(status)) {
KdPrint( (“WdfDriverCreate failed with status 0x%x\n”, status));
}
return status;
}
NTSTATUS
FilterEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
{
WDF_OBJECT_ATTRIBUTES deviceAttributes;
PFILTER_EXTENSION filterExt;
NTSTATUS status;
NTSTATUS status2;
WDFDEVICE device;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
PDEVICE_OBJECT pdo = NULL;
PAGED_CODE ();
UNREFERENCED_PARAMETER(Driver);
WdfFdoInitSetFilter(DeviceInit);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, FILTER_EXTENSION);
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status)) {
KdPrint( (“WdfDeviceCreate failed with status code 0x%x\n”, status));
return status;
}
filterExt = FilterGetData(device);
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
WdfIoQueueDispatchParallel);
ioQueueConfig.EvtIoDeviceControl = FilterEvtIoDeviceControl;
status = WdfIoQueueCreate(device,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
WDF_NO_HANDLE // pointer to default queue
);
if (!NT_SUCCESS(status)) {
KdPrint( (“WdfIoQueueCreate failed 0x%x\n”, status));
return status;
}
return status;
}
VOID
FilterEvtIoDeviceControl(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)
{
PFILTER_EXTENSION filterExt;
WDFDEVICE device;
PIRP pIRP;
PIO_STACK_LOCATION pSIRP;
/////////////////////////////////////////////////////////////////////////////////////////
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(InputBufferLength);
KdPrint((“Entered FilterEvtIoDeviceControl\n”));
device = WdfIoQueueGetDevice(Queue);
filterExt = FilterGetData(device);
/////////////////////////////////////////////////////////////////////////////////////////////////////
pIRP = WdfRequestWdmGetIrp(Request);
pSIRP = IoGetCurrentIrpStackLocation(pIRP);
/////////////////////////////////////////////////////////////////////////////////////////////////////
switch (IoControlCode)
{
default: KdPrint((“IOCTL: %lx\n”, IoControlCode));
}
__try
{
if (KeGetCurrentIrql() == PASSIVE_LEVEL)
{
switch (pSIRP->MajorFunction)
{
case IRP_MJ_CREATE: KdPrint((“IRP: IRP_MJ_CREATE\n”));break;
case IRP_MJ_READ: KdPrint((“IRP: IRP_MJ_READ\n”));break;
case IRP_MJ_WRITE: KdPrint((“IRP: IRP_MJ_WRITE\n”));break;
case IRP_MJ_DEVICE_CONTROL: KdPrint((“IRP: IRP_MJ_DEVICE_CONTROL\n”));break;
case IRP_MJ_PNP: KdPrint((“IRP_MJ_PNP\n”));break;
default: KdPrint((“pSIRP->MajorFunction: %x\n”, pSIRP->MajorFunction));
}
//Extra logic like blocking/diverting requests goes here
}
else
KdPrint((“IRQL is not PASSIVE_LEVEL!\n”));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
//Something went wrong!
status2 = GetExceptionCode();
KdPrint((“Exception: %lx\n”, status2));
}
KdPrint((“-------------------------------------------------------------------------\n”));
#if FORWARD_REQUEST_WITH_COMPLETION
FilterForwardRequestWithCompletionRoutine(Request,
WdfDeviceGetIoTarget(device));
#else
FilterForwardRequest(Request, WdfDeviceGetIoTarget(device));
#endif
return;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID
FilterForwardRequest(
IN WDFREQUEST Request,
IN WDFIOTARGET Target
)
{
WDF_REQUEST_SEND_OPTIONS options;
BOOLEAN ret;
NTSTATUS status;
WDF_REQUEST_SEND_OPTIONS_INIT(&options,
WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET);
ret = WdfRequestSend(Request, Target, &options);
if (ret == FALSE) {
status = WdfRequestGetStatus (Request);
KdPrint( (“WdfRequestSend failed: 0x%x\n”, status));
WdfRequestComplete(Request, status);
}
return;
}
#if FORWARD_REQUEST_WITH_COMPLETION
VOID
FilterForwardRequestWithCompletionRoutine(
IN WDFREQUEST Request,
IN WDFIOTARGET Target
)
{
BOOLEAN ret;
NTSTATUS status;
WdfRequestFormatRequestUsingCurrentType(Request);
WdfRequestSetCompletionRoutine(Request,
FilterRequestCompletionRoutine,
WDF_NO_CONTEXT);
ret = WdfRequestSend(Request,
Target,
WDF_NO_SEND_OPTIONS);
if (ret == FALSE) {
status = WdfRequestGetStatus (Request);
KdPrint( (“WdfRequestSend failed: 0x%x\n”, status));
WdfRequestComplete(Request, status);
}
return;
}
VOID
FilterRequestCompletionRoutine(
IN WDFREQUEST Request,
IN WDFIOTARGET Target,
PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,
IN WDFCONTEXT Context
)
{
UNREFERENCED_PARAMETER(Target);
UNREFERENCED_PARAMETER(Context);
WdfRequestComplete(Request, CompletionParams->IoStatus.Status);
return;
}
#endif //FORWARD_REQUEST_WITH_COMPLETION