Touch-Mouse discrimination

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:

  1. Do I need to change something in the following code to make it work as a device filter instead of a class filter?

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

So the point is, this code works fine when run as a hidclass upper filter but when I try to run it as an upper filter to the HID device the device won’t even start properly and the filter driver does not seem to load/execute.

Can somebody provide a pointer as to what I am doing wrong and what I should look into?

Cheers,
Alex

Well, it turns out that I was not doing enough hooking.

I was only filtering IoDeviceControl but then I added the filters below:

ioQueueConfig.EvtIoRead = FilterEvtIoRead;
ioQueueConfig.EvtIoWrite = FilterEvtIoWrite;
ioQueueConfig.EvtIoInternalDeviceControl = FilterEvtIoInternalDeviceControl;

After I did that, installing my filter as a hidclass upper filter driver will give me a constant flow of events while the touch sensor is being used (specifically the IoRead hook is being called).

Not sure if I am getting all touch related info but I will know more about this once I delve into the contents of the read information.

This solves one of my two major problems. The second one is filtering only specific HID devices. I am guessing that I should be able to specify the hardware IDs I’m interested in in the inf file. Can someone confirm this is the case and whether it is also possible to programmatically check the for the hardware id of the involved physical device in my code.

Many thanks,
Alex Fotios

xxxxx@gmail.com wrote:

This solves one of my two major problems. The second one is filtering only specific HID devices. I am guessing that I should be able to specify the hardware IDs I’m interested in in the inf file. Can someone confirm this is the case and whether it is also possible to programmatically check the for the hardware id of the involved physical device in my code.

There are two ways. One is the mechanism you describe – have one INF
that claims all of the PnP IDs you are interested in. The other is to
install it as a class filter, and check the hardware ID in the AddDevice
callback. If you exit that callback without adding yourself to the
stack, you will not participate in that stack.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Many thanks Tim - your help is as always much appreciated. I will eventually try both methods and report back here.