Bagaimana cara menonaktifkan Dukungan Tablet WPF di Surface 4 Pro?

Saya mewarisi aplikasi WPF yang menargetkan Net 3.5, dan saya harus menginstalnya di Surface Pro 4 (I5). Aplikasi ini terhenti pada titik yang berbeda, dan saya telah mengamati bahwa animasi terkadang tidak pernah menampilkan peristiwa yang telah selesai (mungkin berakhir pada suatu saat, tetapi tidak pada waktu yang dinyatakan dalam properti Durasi).

Sebagai solusinya, saya mencoba Menonaktifkan RealTimeStylus untuk Aplikasi WPF tetapi setelahnya beberapa percobaan, saya perhatikan bahwa meskipun metode DisableWPFTabletSupport dijalankan dan selesai (saya menambahkan kode log dalam metode DisableWPFTabletSupport dan empat perangkat dihapus di Surface Pro 4), mungkin Dukungan Tablet WPF masih aktif di aplikasi saya, karena aplikasi terus hang dari waktu ke waktu dan terus menangkap sentuhan layar.

Jadi, satu-satunya cara yang saya temukan agar berhasil menjalankan aplikasi WPF yang menargetkan Net 3.5 di Surface 4 Pro adalah menggunakan Windows Device Manager untuk menonaktifkan semua perangkat terkait layar sentuh di antarmuka manusia.

Adakah yang tahu cara menonaktifkan Dukungan Tablet WPF di Surface 4 Pro?

Catatan. Terlepas dari apa yang dikatakan di menonaktifkan dan mengaktifkan driver layar sentuh, menonaktifkan "perangkat layar sentuh yang sesuai dengan HID" saja tidak cukup: hingga "Perangkat sentuh Intel(R) Precise" tidak dinonaktifkan, layar sentuh tetap aktif dan sebagian besar aplikasi WPF akan gagal.


person SERWare    schedule 28.07.2016    source sumber


Jawaban (1)


Saya memiliki masalah yang sama dan dapat menemukan solusi menggunakan refleksi.

Masalah ini disebabkan oleh WPF memperbarui penanganan perangkat tablet internalnya ketika pesan jendela WM_TABLET_ADDED, WM_TABLET_REMOVED atau WM_DEVICECHANGED dikirim (lihat sumber referensi .net). Karena pesan-pesan ini mungkin dihasilkan atau tidak tergantung pada perangkat keras yang digunakan, metode DisableWPFTabletSupport asli mungkin cukup atau tidak.

Solusi saya adalah menangani dan menyembunyikan ketiga pesan jendela tersebut dari WPF selain kode asli:

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
}

}

Beberapa catatan tentang penerapan dan batasan ini:

WPF tidak mendaftarkan pesan-pesan ini pada aplikasi MainWindow, tetapi melalui jendela tersembunyi bernama "SystemResources..." yang dibuat untuk setiap contoh aplikasi. Jadi menangani pesan-pesan itu di MainWindow (yang mudah) tidak membantu di sini.

Solusi saya juga menggunakan beberapa refleksi dan panggilan ke kelas internal dan properti internal. Ini berfungsi untuk .net 4.6.2 dan belum mengujinya pada versi sebelumnya. Selain itu, saat mendalami kode sumber .net, saya juga melihat dua jalur potensial lainnya yang memperbarui penanganan tablet, yang tidak ditangani dalam solusi ini: Konstruktor TabletCollection dan HwndStylusInputProvider.

person tseifried    schedule 09.09.2016
comment
Sudahkah Anda mengujinya di Surface 4 pro? Terima kasih telah membagikan solusi Anda - person SERWare; 09.09.2016
comment
@SERWare: Ya, ini berfungsi di SP4. Saya belum mengujinya secara pribadi, tetapi berfungsi pada SP4 salah satu pelanggan kami. Yang saya belum tahu, apakah ini juga berfungsi di Surface Book, tapi saya yakin. - person tseifried; 15.09.2016
comment
Saya hampir mengalami kasus yang sama. Saya mendeteksi kesalahan pada SP4 pelanggan dan saya harus menunggu sampai mereka mengembalikannya untuk menguji solusi Anda. Terima kasih lagi. - person SERWare; 16.09.2016
comment
Saya mencoba solusi Anda dalam proyek yang menargetkan 3.5 (saya menggunakan pembaruan VS 2015 4) dan kode Anda berhasil mengaitkan delegasi dan mencoba memfilter pesan. Untuk saat ini saya mengujinya di pc tanpa layar sentuh, tetapi yang penting tidak gagal jika tidak ada layar sentuh untuk menghindari pemeliharaan dua versi. - person SERWare; 19.09.2016
comment
Saya mencoba tes aplikasi WPF sederhana yang menggunakan kode Anda di SP4 dan input sentuh tidak dinonaktifkan. Delegasi hanya menangkap satu pesan saat aplikasi dimulai tetapi tidak difilter. - person SERWare; 20.09.2016
comment
@tseifried Ini solusi terbaik untuk mencegat dan memblokir WM_TABLET_ADDED, WM_TABLET_DELETED dan WM_DEVICECHANGE di tingkat aplikasi. Solusi ini juga berfungsi dengan baik untuk aplikasi .Net 4.6.2 berbasis Win 10 WPF. Ini telah membantu saya mencegah aplikasi WPF membeku ketika perangkat sentuh monitor sekunder dinonaktifkan dan diaktifkan. Terima kasih. - person Diwakar Padmaraja; 03.07.2019
comment
Di mana saya meletakkan kode ini? Bagaimana cara membuat instance objek kelas ini dan cara menggunakannya? - person sam; 23.03.2020