其他分享
首页 > 其他分享> > 禁用Surface 4 Pro中的WPF平板电脑支持?

禁用Surface 4 Pro中的WPF平板电脑支持?

作者:互联网

我继承了针对Net 3.5的WPF应用程序,并且必须将其安装在Surface Pro 4(I5)中.该应用程序挂在不同的位置,我观察到有时动画有时不会触发已完成的事件(也许它们会在某个时刻结束,但不会在Duration属性中表示的时间结束).

解决方法是,我尝试使用Disable the RealTimeStylus for WPF Applications,但经过几次试验,我注意到尽管DisableWPFTabletSupport方法已执行并完成(我在DisableWPFTabletSupport方法中添加了日志代码,并且在Surface Pro 4中删除了四个设备),但可能仍在激活WPF Tablet支持在我的应用程序中,因为该应用程序会不时地挂起并继续捕获屏幕触摸.

因此,我发现能够在Surface 4 Pro中成功运行针对Net 3.5的WPF应用程序的唯一方法是使用Windows设备管理器禁用人机界面中所有与触摸屏相关的设备.

有人知道如何在Surface 4 Pro中禁用WPF平板电脑支持吗?

注意.尽管在disable and enable the touchscreen driver上进行了说明,但是禁用“符合HID的触摸屏设备”是不够的:在未禁用“ Intel(R)精确触摸设备”之前,触摸屏将保持激活状态,并且大多数WPF应用程序将失败.

解决方法:

我遇到了同样的问题,并且能够使用反射找到解决方法.

该问题是由发送窗口消息WM_TABLET_ADDED,WM_TABLET_REMOVED或WM_DEVICECHANGED(see .net referencesource)时WPF更新其内部平板设备处理引起的.由于可能会生成或可能不会生成这些消息,具体取决于所使用的硬件,因此原始的DisableWPFTabletSupport方法可能就足够了.

我的解决方案是除了原始代码外,还要从WPF处理和隐藏这三个窗口消息:

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
}

}

有关此实现和限制的一些注意事项:

WPF不会在应用程序MainWindow上注册这些消息,而是通过为每个应用程序实例创建的名为“ SystemResources …”的隐藏窗口进行注册.因此,在MainWindow上处理这些消息(这很容易)在这里没有帮助.

我的解决方案还使用了一些反射,并调用了内部类和内部属性.它适用于.net 4.6.2,并且尚未在较早版本上进行过测试.此外,在深入研究.net源代码时,我还看到了更新平板电脑处理的其他两条可能的路径,在此解决方案中未处理这些路径:TabletCollection和HwndStylusInputProvider的构造函数.

标签:multi-touch,pixelsense,wpf,c,net
来源: https://codeday.me/bug/20191118/2027535.html