WM_TOUCH event not received anymore after screen disconnection in C# app

Gael Charpentier 1 Reputation point
2021-09-24T08:08:49.537+00:00

I have a C# application handling touch by hooking to the Windows API message loop and listening to WM_TOUCH event.

It work perfectly except when the screen is disconnected or switch off. When the screen is reconected, Windows OS still receive touch but my application don't receive touch event anymore.

Here the code used. On application start, I use RegisterTouchWindow to register for WM_TOUCH events. I use RegisterDeviceNotification to watch devices changes. On devices changes, IsTouchEnabled check the state of touch with GetSystemMetrics(SM_DIGITIZER). if touch is not available and it was registered previously with RegisterTouchWindow, I unregister it with UnregisterTouchWindow.

if it is available and has not been registered or has been unregistered, it register with RegisterTouchWindow. This is were it fail. RegisterTouchWindow return true but the WndProc will not received any WM_TOUCH event.

I tried to not Unregister touch when device is lost but it is the same result.

I checked the messages with Spy++. after the touch has been disconnected then recconected, there is no WM_TOUCH message appearing in spy++.

Here you can download the source code of a WPF test application to reproduce the probleme https://drive.google.com/file/d/1E-RdiHAH0mfIEFj1-hNaBzVCfhn1iaNp/view?usp=sharing

   public static bool IsTouchEnabled() {  
    var value = (SM_DIGITIZER_FLAG) GetSystemMetrics(SM_DIGITIZER);  
    return value.HasFlag(SM_DIGITIZER_FLAG.NID_EXTERNAL_TOUCH) ||  
     value.HasFlag(SM_DIGITIZER_FLAG.NID_INTEGRATED_TOUCH) ||  
     value.HasFlag(SM_DIGITIZER_FLAG.NID_EXTERNAL_PEN) ||  
     value.HasFlag(SM_DIGITIZER_FLAG.NID_INTEGRATED_PEN);  
   }  
     
   public static void RegisterUsbDeviceNotification(IntPtr windowHandle) {  
    DevBroadcastDeviceinterface dbi = new DevBroadcastDeviceinterface {  
     DeviceType = DbtDevtypDeviceinterface,  
      Reserved = 0,  
      ClassGuid = GuidDevinterfaceUSBDevice,  
      Name = 0  
    };  
     
    dbi.Size = Marshal.SizeOf(dbi);  
    IntPtr buffer = Marshal.AllocHGlobal(dbi.Size);  
    Marshal.StructureToPtr(dbi, buffer, true);  
     
    notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);  
   }  
     
   protected void OnSourceInitialized(object o, EventArgs e) {  
    var source = PresentationSource.FromVisual(win) as HwndSource;  
     
    RegisterUsbDeviceNotification(source.Handle);  
     
    RegisterTouchEvent(source.Handle);  
     
    //setup windows hook here  
    source.AddHook(WndProc);  
   }  
     
   private bool RegisterTouchEvent(IntPtr handle) {  
    if (!IsTouchEnabled()) {  
     logger.Warn("no Touch device available");  
     if (_touchRegistered) {  
      if (!UnregisterTouchWindow(handle))  
       logger.Info("problem during touch unregistering");  
      _touchRegistered = false;  
      logger.Info("unregister touch event");  
     }  
     OnTouchNotAvailable();  
     return false;  
    }  
    if (_touchRegistered)  
     return true;  
     
    if (!RegisterTouchWindow(handle, RegisterTouchFlags.TWF_WANTPALM)) {  
     var err = Marshal.GetLastWin32Error();  
     logger.Error("cant register touch window. error " + err);  
     return false;  
    } else  
     logger.Info("Win Touch source initialysed");  
    _touchRegistered = true;  
     
    return true;  
   }  
     
   private IntPtr WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, ref Boolean handled) {  
    if (msg == WmDevicechange) {  
     
     WmDevicechangeParam p = (WmDevicechangeParam) wParam.ToInt32();  
     logger.Info("Device changed -> " + p.ToString());  
     var source = PresentationSource.FromVisual(win) as HwndSource;  
     if (source != null)  
      RegisterTouchEvent(source.Handle);  
     return new IntPtr(1);  
    }  
     
    if (msg == WM_TOUCH) {  
     handled = HandleTouch(wParam, lParam);  
     return new IntPtr(1);  
    }  
     
    return IntPtr.Zero;  
   }  

Here the log I get when I switch off the touch screen and switch it on again

   Win Touch source initialysed  
   //Screen switched off ----------------------  
   Device changed -> DBT_DEVICEREMOVECOMPLETE  
   no Touch device available  
   unregister touch event  
   Device changed -> DBT_DEVNODES_CHANGED  
   no Touch device available  
   Device changed -> DBT_DEVNODES_CHANGED  
   no Touch device available  
   Device changed -> DBT_DEVNODES_CHANGED  
   no Touch device available  
   //Screen switched on ----------------------  
   Device changed -> DBT_DEVNODES_CHANGED  
   no Touch device available  
   Device changed -> DBT_DEVNODES_CHANGED  
   no Touch device available  
   Device changed -> DBT_DEVNODES_CHANGED  
   no Touch device available  
   Device changed -> DBT_DEVNODES_CHANGED  
   no Touch device available  
   Device changed -> DBT_DEVICEARRIVAL  
   no Touch device available  
   Device changed -> DBT_DEVNODES_CHANGED  
   Win Touch source initialysed  
   Device changed -> DBT_DEVNODES_CHANGED  

Here all the PInvoke function and WinApi enum used

   private static readonly Int32 WM_TOUCH = 0x0240;  
     
   public enum WmDevicechangeParam {  
    DBT_CONFIGCHANGECANCELED = 0x0019,  
     DBT_CONFIGCHANGED = 0x0018,  
     DBT_CUSTOMEVENT = 0x8006,  
     DBT_DEVICEARRIVAL = 0x8000,  
     DBT_DEVICEQUERYREMOVE = 0x8001,  
     DBT_DEVICEQUERYREMOVEFAILED = 0x8002,  
     DBT_DEVICEREMOVECOMPLETE = 0x8004,  
     DBT_DEVICEREMOVEPENDING = 0x8003,  
     DBT_DEVICETYPESPECIFIC = 0x8005,  
     DBT_DEVNODES_CHANGED = 0x0007,  
     DBT_QUERYCHANGECONFIG = 0x0017,  
     DBT_USERDEFINED = 0xFFFF,  
   }  
     
   // device is gone  
   public  
   const int WmDevicechange = 0x0219;  
     
   // device change event  
   private  
   const int DbtDevtypDeviceinterface = 5;  
     
   private  
   const int MAXTOUCHES_INDEX = 0x95;  
   private  
   const int SM_DIGITIZER = 94;  
   private  
   const uint WM_DISPLAYCHANGE = 0x007e;  
   private static readonly Guid GuidDevinterfaceUSBDevice = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED");  
   private static readonly Int32 touchInputSize = Marshal.SizeOf(new TOUCHINPUT());  
     
   [DllImport("kernel32.dll")]  
   public static extern uint GetLastError();  
     
   [System.Runtime.InteropServices.DllImport("user32.dll")]  
   public static extern int GetSystemMetrics(int nIndex);  
     
   [DllImport("user32.dll", SetLastError = true)]  
   public static extern bool RegisterTouchWindow(IntPtr hwnd,  
    [MarshalAs(UnmanagedType.U4)] RegisterTouchFlags flags);  
     
   [DllImport("user32.dll", SetLastError = true)]  
   static extern bool UnregisterTouchWindow(IntPtr hWnd);  
     
   [DllImport("user32")]  
   [  
    return :MarshalAs(UnmanagedType.Bool)  
   ]  
   private static extern void CloseTouchInputHandle(IntPtr lParam);  
     
   [DllImport("user32")]  
   [  
    return :MarshalAs(UnmanagedType.Bool)  
   ]  
   private static extern Boolean GetTouchInputInfo(IntPtr hTouchInput, Int32 cInputs, [In, Out] TOUCHINPUT[] pInputs, Int32 cbSize);  
     
   [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
   private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);  
     
   // USB devices  
   [DllImport("user32.dll")]  
   private static extern bool UnregisterDeviceNotification(IntPtr handle);  
     
   [StructLayout(LayoutKind.Sequential)]  
   private struct DevBroadcastDeviceinterface {  
    internal int Size;  
    internal int DeviceType;  
    internal int Reserved;  
    internal Guid ClassGuid;  
    internal short Name;  
   }  
     
   [StructLayout(LayoutKind.Sequential)]  
   private struct TOUCHINPUT {  
    public Int32 x;  
    public Int32 y;  
    public IntPtr hSource;  
    public Int32 dwID;  
    public Int32 dwFlags;  
    public Int32 dwMask;  
    public Int32 dwTime;  
    public IntPtr dwExtraInfo;  
    public Int32 cxContact;  
    public Int32 cyContact;  
   }  
     
   public enum DWFlags {  
    TOUCHEVENTF_MOVE = 0x0001,  
     
     TOUCHEVENTF_DOWN = 0x0002,  
     
     TOUCHEVENTF_UP = 0x0004,  
   }  
     
   [Flags, Serializable]  
   public enum RegisterTouchFlags {  
    TWF_NONE = 0x00000000,  
     
     TWF_FINETOUCH = 0x00000001,  
     
     TWF_WANTPALM = 0x00000002  
   }  
     
   [Flags]  
   public enum SM_DIGITIZER_FLAG {  
    TABLET_CONFIG_NONE = 0x00000000, //    The input digitizer does not have touch capabilities.  
     NID_INTEGRATED_TOUCH = 0x00000001, // An integrated touch digitizer is used for input.  
     NID_EXTERNAL_TOUCH = 0x00000002, //   An external touch digitizer is used for input.  
     NID_INTEGRATED_PEN = 0x00000004, //   An integrated pen digitizer is used for input.  
     NID_EXTERNAL_PEN = 0x00000008, // An external pen digitizer is used for input.  
     NID_MULTI_INPUT = 0x00000040, //  An input digitizer with support for multiple inputs is used for input.  
     NID_READY = 0x00000080, //The input digitizer is ready for input. If this value is unset, it may mean that the tablet service is stopped, the digitizer is not supported, or digitizer drivers have not been installed.  
   }  
     
   private struct TouchMessage  
   {  
       public int Count  
       { get; private set; }  
     
       public TOUCHINPUT[] inputs  
       { get; private set; }  
     
       public TouchMessage(TOUCHINPUT[] inputs, int count)  
           : this()  
       {  
          this.inputs = inputs;  
          this.Count = count;  
       }  
   }  
     
   private static Int32 LoWord(Int32 number)  
   {  
       return number & 0xffff;  
   }  
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,670 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,418 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,228 questions
{count} votes