如何在Surface 4 Pro中禁用WPF Tablet支持?

我inheritance了一个针对Net 3.5的WPF应用程序,我必须在Surface Pro 4(I5)中安装它。 该应用程序悬挂在不同的点上,我观察到动画有时永远不会触发已完成的事件(可能它们最终会在某个时刻结束,但不会在Duration属性中表示)。

作为一个转机,我尝试为WPF应用程序禁用RealTimeStylus,但经过几次试验后,我注意到尽管执行了DisableWPFTabletSupport方法并完成了(我在DisableWPFTabletSupport方法中添加了日志代码,并且Surface Pro 4中删除了四个设备),可能是WPF平板电脑支持在我的应用程序中仍处于活动状态,因为应用程序会不时地继续挂起并继续捕获屏幕触摸。

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

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

注意。 尽管在禁用和启用触摸屏驱动程序时有所说,但禁用“符合HID标准的触摸屏设备”是不够的:直到“英特尔(R)精确触摸设备”未被禁用,触摸屏仍保持激活且大多数WPF应用程序将失败。

我有同样的问题,并能够找到使用reflection的解决方法。

该问题是由WPF在发送窗口消息WM_TABLET_ADDED,WM_TABLET_REMOVED或WM_DEVICECHANGED时更新其内部平板设备处理引起的( 请参阅.net referencesource )。 由于这些消息可能会或可能不会生成,具体取决于所使用的硬件,原始的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上处理这些消息(这很容易)在这里没有用。

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