Search code examples
winapihidcitrix

How to determine if an HID keyboard is remote?


We're using custom HID devices for data input in our internal business application. We're now trying to get the working over Citrix, so people can take the keypads home and use them there.

We've needed to enable USB forwarding for the specific USB VID and PID in Citrix in order to get this working, and it does work.

However, we've noticed that if a keypad is left plugged into a user's main machine in the office, that's still detected and works too, even when the user is remote! This is a problem because it could lead to bad data being input. 'Just unplug the keypad in the office' might be your first reaction, but this isn't good enough because the possible consequences of bad data are very bad.

I've used both the Raw Input and HID win32 interfaces. They both detect the rogue remote device. I wondered whether there might be something in the HID device path, but both local and remote looked very similar.

What approach would you suggest to resolve this? I'm wondering whether I can walk up the device tree and see whether the device is connected to a Citrix USB bus, but I've no idea how I'd go about this


Solution

  • To detect Citrix session you can use this code:

    bool IsUnderCitrixConnection()
    {
        static bool isUnderCitrixConnection = []()
        {
            HMODULE wfapi64 = LoadLibrary("wfapi64.dll");
    
            if (!wfapi64)
            {
                return false;
            }
    
            // See wfapi.h in WinFrame Application Programming Interface (WFAPI) SDK
            // https://developer.cloud.com/archived-sdks/docs/archived-sdks
            typedef INT(WINAPI * WFGetActiveProtocolFn)(DWORD SessionId);
            WFGetActiveProtocolFn WFGetActiveProtocol = (WFGetActiveProtocolFn)GetProcAddress(wfapi64, "WFGetActiveProtocol");
            if (!WFGetActiveProtocol)
            {
                return false;
            }
    
            return WFGetActiveProtocol((DWORD)-1 /*WF_CURRENT_SESSION*/) == 1 /*WFProtocolICA*/;
        }();
    
        return isUnderCitrixConnection;
    }
    

    To detect if device is virtual keyboard/mouse created by Citrix you can check its device interface for CITRIXDEVICES string:

    static std::string GetRawInputDeviceInterface(HANDLE deviceHandle)
    {
        static WCHAR buffer[MAX_PATH];
        UINT size = MAX_PATH;
        UINT result = ::GetRawInputDeviceInfoW(deviceHandle, RIDI_DEVICENAME, buffer, &size);
    
        if (result == UINT_MAX)
        {
            return {};
        }
    
        return utf8::narrow(buffer);
    }
    
    static bool IsCitrixDevice(const std::string& deviceInterface)
    {
        return deviceInterface.find("CITRIXDEVICES") != std::string::npos;
    }
    
    ....
    //RAWINPUT.header.hDevice - contains raw input device handle.
    const std::string deviceInterface = GetRawInputDeviceInterface(hDevice);
    if (IsCitrixDevice(deviceInterface))
    {
    ....
    }
    

    enter image description here