จะปิดการใช้งานการสนับสนุนแท็บเล็ต WPF ใน Surface 4 Pro ได้อย่างไร

ฉันได้รับแอปพลิเคชัน WPF ที่กำหนดเป้าหมาย Net 3.5 และฉันต้องติดตั้งใน Surface Pro 4 (I5) แอปพลิเคชันค้างอยู่ในจุดต่างๆ และฉันสังเกตเห็นว่าบางครั้งแอนิเมชั่นไม่เคยเริ่มเหตุการณ์ที่เสร็จสมบูรณ์เลย (อาจจะจบลงที่จุดใดจุดหนึ่ง แต่ไม่ใช่ในเวลาที่แสดงในคุณสมบัติ Duration)

เพื่อเป็นการพลิกฟื้น ฉันลองปิดการใช้งาน RealTimeStylus สำหรับแอปพลิเคชัน WPF แต่หลังจากนั้น การทดลองหลายครั้ง ฉันสังเกตเห็นว่าถึงแม้วิธี DisableWPFTabletSupport จะดำเนินการและเสร็จสิ้น (ฉันเพิ่มรหัสบันทึกในวิธี DisableWPFTabletSupport และอุปกรณ์สี่เครื่องถูกลบออกใน Surface Pro 4) อาจเป็นไปได้ว่า WPF Tablet Support ยังคงใช้งานอยู่ในแอปพลิเคชันของฉัน เนื่องจากแอปพลิเคชันยังคงหยุดทำงาน เป็นครั้งคราวและยังคงจับการสัมผัสหน้าจอต่อไป

ดังนั้น วิธีเดียวที่ฉันพบว่าสามารถเรียกใช้แอปพลิเคชัน WPF ที่กำหนดเป้าหมาย Net 3.5 ใน Surface 4 Pro ได้สำเร็จคือใช้ Windows Device Manager เพื่อปิดใช้งานอุปกรณ์ที่เกี่ยวข้องกับหน้าจอสัมผัสทั้งหมดในส่วนต่อประสานกับมนุษย์

มีใครทราบบ้างว่าฉันสามารถปิดใช้งานการสนับสนุนแท็บเล็ต WPF ใน Surface 4 Pro ได้อย่างไร

บันทึก. แม้จะมีสิ่งที่กล่าวไว้ใน ปิดใช้งานและเปิดใช้งานไดรเวอร์หน้าจอสัมผัส การปิดใช้งาน "อุปกรณ์หน้าจอสัมผัสที่รองรับ HID" นั้นไม่เพียงพอ: จนกว่า "อุปกรณ์ระบบสัมผัส Intel(R) Precise" จะไม่ถูกปิดใช้งาน หน้าจอสัมผัสยังคงเปิดใช้งานอยู่ และแอปพลิเคชัน WPF ส่วนใหญ่จะล้มเหลว


person SERWare    schedule 28.07.2016    source แหล่งที่มา


คำตอบ (1)


ฉันมีปัญหาเดียวกันและสามารถหาวิธีแก้ไขได้โดยใช้การไตร่ตรอง

ปัญหานี้เกิดจากการที่ 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 (ซึ่งน่าจะง่าย) จึงไม่ช่วยอะไรที่นี่

โซลูชันของฉันยังใช้การไตร่ตรองและการเรียกคลาสภายในและคุณสมบัติภายในค่อนข้างมาก ใช้งานได้กับ .net 4.6.2 และยังไม่ได้ทดสอบกับเวอร์ชันก่อนหน้า ยิ่งไปกว่านั้น ในการเจาะลึกซอร์สโค้ด .net ฉันยังเห็นอีกสองเส้นทางที่เป็นไปได้ซึ่งมีการอัปเดตการจัดการแท็บเล็ต ซึ่งไม่ได้รับการจัดการในโซลูชันนี้: ตัวสร้างของ TabletCollection และของ HwndStylusInputProvider

person tseifried    schedule 09.09.2016
comment
คุณได้ทดสอบใน Surface 4 pro แล้วหรือยัง? ขอขอบคุณสำหรับการแบ่งปันวิธีการแก้ปัญหาของคุณ - person SERWare; 09.09.2016
comment
@SERWare: ใช่ มันใช้งานได้บน SP4 ฉันยังไม่ได้ทดสอบเป็นการส่วนตัว แต่ใช้งานได้กับ SP4 ของลูกค้ารายหนึ่งของเรา สิ่งที่ฉันยังไม่รู้ว่ามันใช้ได้กับ Surface Book หรือไม่ แต่ฉันมั่นใจ - person tseifried; 15.09.2016
comment
ฉันเกือบจะในกรณีเดียวกัน ฉันตรวจพบข้อผิดพลาดใน SP4 ของลูกค้า และต้องรอจนกว่าพวกเขาจะส่งคืนข้อผิดพลาดดังกล่าวจึงจะทดสอบโซลูชันของคุณได้ ขอบคุณอีกครั้ง. - person SERWare; 16.09.2016
comment
ฉันลองวิธีแก้ปัญหาของคุณในโครงการที่กำหนดเป้าหมายเป็น 3.5 (ฉันใช้ VS 2015 อัปเดต 4) และโค้ดของคุณเชื่อมโยงผู้รับมอบสิทธิ์ได้สำเร็จและพยายามกรองข้อความ ในตอนนี้ ฉันทดสอบมันในพีซีที่ไม่มีหน้าจอสัมผัส แต่สิ่งสำคัญคือมันจะต้องไม่ล้มเหลวเมื่อไม่มีหน้าจอสัมผัสเพื่อหลีกเลี่ยงการบำรุงรักษาสองเวอร์ชัน - person SERWare; 19.09.2016
comment
ฉันลองใช้แอปทดสอบ WPF ธรรมดาที่ใช้รหัสของคุณบน SP4 และไม่ได้ปิดใช้งานการป้อนข้อมูลแบบสัมผัส ผู้รับมอบสิทธิ์จะบันทึกข้อความเดียวเมื่อเริ่มต้นแอปพลิเคชัน แต่ไม่ได้ถูกกรอง - person SERWare; 20.09.2016
comment
@tseifried มันเป็นโซลูชั่นที่ยอดเยี่ยมในการสกัดกั้นและบล็อก WM_TABLET_ADDED, WM_TABLET_DELETED และ WM_DEVICECHANGE ในระดับแอปพลิเคชัน โซลูชันนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับแอปพลิเคชัน .Net 4.6.2 ที่ใช้ Win 10 WPF เช่นกัน ช่วยให้ฉันป้องกันไม่ให้แอปพลิเคชัน WPF หยุดทำงานเมื่ออุปกรณ์สัมผัสของจอภาพรองถูกปิดใช้งานและเปิดใช้งาน ขอบคุณ. - person Diwakar Padmaraja; 03.07.2019
comment
ฉันจะใส่รหัสนี้ที่ไหน? จะสร้างอินสแตนซ์ของอ็อบเจ็กต์ของคลาสนี้และใช้งานอย่างไร - person sam; 23.03.2020