How to disable WPF Tablet Support in Surface 4 Pro?

旧城冷巷雨未停 提交于 2019-12-10 17:25:41

问题


I've inherited an WPF application that targets Net 3.5, And I have to install it in a Surface Pro 4 (I5). The application is hanging in different points, and I have observed that animations sometimes never fire the completed event (maybe they end up at some point, but not at the time expressed in Duration property).

As a turnaround, I tried Disable the RealTimeStylus for WPF Applications but after several trials, I noticed that although the DisableWPFTabletSupport method is executed and finishes (I added log code in DisableWPFTabletSupport method and four devices are removed in the Surface Pro 4), probably WPF Tablet Support is still active in my application, because the application continues hanging from time to time and continues capturing screen touches.

So, the only way I've found to be able to successfully run an WPF application targeting Net 3.5 in Surface 4 Pro is use the Windows Device Manager to disable all touch screen related devices in human interfaces.

Anyone know how I can disable WPF Tablet Support in Surface 4 Pro?

Note. Despite what is said on disable and enable the touchscreen driver, it is not enough to disable "HID-compliant touch screen devices": until the "Intel(R) Precise touch devices" is not disabled, the touch screen remains activated and most WPF applications will fail.


回答1:


I had the same problem and was able to find a work-around using reflection.

The issue is caused by WPF updating its internal tablet device handling when the window messages WM_TABLET_ADDED, WM_TABLET_REMOVED or WM_DEVICECHANGED are sent (see .net referencesource). As these messages might or might not be generated depending on the hardware used, the original DisableWPFTabletSupport method might be enough or not.

My solution was to handle and hide those three window messages from WPF in addition to the original code:

class DisableWPFTouchAndStylus
{

private static void DisableWPFTabletSupport()
{
    // Get a collection of the tablet devices for this window.  
    var devices = Tablet.TabletDevices;

    if (devices.Count > 0)
    {
        // Get the Type of InputManager.
        var inputManagerType = typeof(InputManager);

        // Call the StylusLogic method on the InputManager.Current instance.
        var stylusLogic = inputManagerType.InvokeMember("StylusLogic",
                    BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                    null, InputManager.Current, null);

        if (stylusLogic != null)
        {
            //  Get the type of the stylusLogic returned from the call to StylusLogic.
            var stylusLogicType = stylusLogic.GetType();

            // Loop until there are no more devices to remove.
            while (devices.Count > 0)
            {
                // Remove the first tablet device in the devices collection.
                stylusLogicType.InvokeMember("OnTabletRemoved",
                        BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
                        null, stylusLogic, new object[] { (uint)0 });
            }
        }
    }

    // END OF ORIGINAL CODE

    // hook into internal class SystemResources to keep it from updating the TabletDevices on system events

    object hwndWrapper = GetSystemResourcesHwnd();
    if (hwndWrapper != null)
    {
        // invoke hwndWrapper.AddHook( .. our method ..)
        var internalHwndWrapperType = hwndWrapper.GetType();

        // if the delegate is already set, we have already added the hook.
        if (_handleAndHideMessageDelegate == null)
        {
            // create the internal delegate that will hook into the window messages
            // need to hold a reference to that one, because internally the delegate is stored through a WeakReference object

            var internalHwndWrapperHookDelegate = internalHwndWrapperType.Assembly.GetType("MS.Win32.HwndWrapperHook");
            var handleAndHideMessagesHandle = typeof(DisableWPFTouchAndStylus).GetMethod(nameof(HandleAndHideMessages), BindingFlags.Static | BindingFlags.NonPublic);
            _handleAndHideMessageDelegate = Delegate.CreateDelegate(internalHwndWrapperHookDelegate, handleAndHideMessagesHandle);


            // add a delegate that handles WM_TABLET_ADD
            internalHwndWrapperType.InvokeMember("AddHook",
                BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
                null, hwndWrapper, new object[] { _handleAndHideMessageDelegate });
        }
    }
}

private static Delegate _handleAndHideMessageDelegate = null;

private static object GetSystemResourcesHwnd()
{
    var internalSystemResourcesType = typeof(Application).Assembly.GetType("System.Windows.SystemResources");

    // get HwndWrapper from internal property SystemRessources.Hwnd;
    var hwndWrapper = internalSystemResourcesType.InvokeMember("Hwnd",
                BindingFlags.GetProperty | BindingFlags.Static | BindingFlags.NonPublic,
                null, null, null);
    return hwndWrapper;
}

private static IntPtr HandleAndHideMessages(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == (int)WindowMessage.WM_TABLET_ADDED ||
        msg == (int)WindowMessage.WM_TABLET_DELETED ||
        msg == (int)WindowMessage.WM_DEVICECHANGE)
    {
        handled = true;
    }
    return IntPtr.Zero;
}

enum WindowMessage : int
{
    WM_TABLET_DEFBASE = 0x02C0,
    WM_TABLET_ADDED = WM_TABLET_DEFBASE + 8,
    WM_TABLET_DELETED = WM_TABLET_DEFBASE + 9,
    WM_DEVICECHANGE = 0x0219
}

}

Some notes on this implementation and limitations:

WPF does not register to these messages on the applications MainWindow, but through a hidden windows named "SystemResources..." which is created for each application instance. So handling those messages on the MainWindow (which would be easy) does not help here.

My solution also uses quite some reflection and calls into internal classes and internal properties. It works for .net 4.6.2 and have not tested it on earlier versions. Moreover, in my deep dive into the .net source code I also saw two other potential paths in which the tablet handling is updated, which are not handled in this solution: The constructors of TabletCollection and of HwndStylusInputProvider.



来源:https://stackoverflow.com/questions/38642479/how-to-disable-wpf-tablet-support-in-surface-4-pro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!