C # WPF Windows 10 (1803) Ненадежный TouchKeyboard Проблема (Prism ClickOnce) - PullRequest
0 голосов
/ 31 мая 2018

Я пытаюсь решить проблему, которая возникла у нас с экранной клавиатурой в нашем приложении C # Prism WPF с таргетингом на .Net 4.5.2, развернутым с помощью ClickOnce и работающим в Windows 10 версии 1803.

Наше приложение имеет TimeOutфункция, которая запускает событие, и в этом случае я хотел бы либо переключить видимость клавиатуры, либо закрыть ее, если она открыта.Функция Close () в приведенном ниже классе иногда не работает, я вижу в журналах (не показано в приведенном ниже коде) приложение находит дескриптор (который> 0) и выдает команду close, но клавиатура не закрывается.

Этот вопрос относится к 2 вопросам, упомянутым и связанным ниже!Но я не могу комментировать, поэтому мне нужно задать новый вопрос.

Ранее в v1709 было какое-то рабочее решение, предложенное Определить, является ли сенсорная клавиатура Windows 10 видимой или скрытой

Теперь я пытаюсь реализовать последний пост в этом вопросе, чтобы иметь возможность переключать видимость клавиатуры при тайм-ауте или событии закрытия приложения, если оно обнаруживает, что клавиатура открыта, я пытаюсь объединить ее с классом Keyboard.вы видите ниже, но у меня проблемы с этим.

Итак, у нас есть класс сенсорной клавиатуры, объединенный из вышеприведенного поста, и Показать сенсорную клавиатуру (TabTip.exe) в выпуске Windows 10 Anniversary :

static class TouchKeyboard
{


    private static void StartTabTip()
    {
        var p = Process.Start(@"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
        int handle = 0;
        while ((handle = NativeMethods.FindWindow("IPTIP_Main_Window", "")) <= 0)
        {
            Thread.Sleep(100);
        }
    }

    public static void ToggleVisibility()
    {
        var type = Type.GetTypeFromCLSID(Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376"));
        var instance = (ITipInvocation)Activator.CreateInstance(type);
        instance.Toggle(NativeMethods.GetDesktopWindow());
        Marshal.ReleaseComObject(instance);
    }

    public static void Show()
    {
        int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
        if (handle <= 0) // nothing found
        {
            StartTabTip();                
            Thread.Sleep(100);                
        }
        // on some devices starting TabTip don't show keyboard, on some does  ¯\_(ツ)_/¯
        if (!IsOpen())
        {
            ToggleVisibility();
        }
    }

    public static void Hide()
    {
        if (IsOpen())
        {
            ToggleVisibility();
        }
    }        


    public static bool Close()
    {
        // find it
        int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
        bool active = handle > 0;
        if (active)
        {
            // don't check style - just close
            NativeMethods.SendMessage(handle, NativeMethods.WM_SYSCOMMAND, NativeMethods.SC_CLOSE, 0);
        }
        return active;
    }

   public static bool GetIsOpen()
   {
      return GetIsOpen1709() ?? GetIsOpenLegacy();
   }

private static bool? GetIsOpen1709()
{
    var parent = IntPtr.Zero;
    for (;;)
    {
        parent = FindWindowEx(IntPtr.Zero, parent, WindowParentClass1709);
        if (parent == IntPtr.Zero)
            return null; // no more windows, keyboard state is unknown

        // if it's a child of a WindowParentClass1709 window - the keyboard is open
        var wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709);
        if (wnd != IntPtr.Zero)
            return true;
    }
}

private static bool GetIsOpenLegacy()
{
    var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass);
    if (wnd == IntPtr.Zero)
        return false;

    var style = GetWindowStyle(wnd);
    return style.HasFlag(WindowStyle.Visible)
        && !style.HasFlag(WindowStyle.Disabled);
}

private const string WindowClass = "IPTip_Main_Window";
private const string WindowParentClass1709 = "ApplicationFrameWindow";
private const string WindowClass1709 = "Windows.UI.Core.CoreWindow";
private const string WindowCaption1709 = "Microsoft Text Input Application";

private enum WindowStyle : uint
{
    Disabled = 0x08000000,
    Visible = 0x10000000,
}

private static WindowStyle GetWindowStyle(IntPtr wnd)
{
    return (WindowStyle)GetWindowLong(wnd, -16);
}

[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string className, string title = null);

[DllImport("user32.dll", SetLastError = false)]
private static extern uint GetWindowLong(IntPtr wnd, int index);

Это сработалодостаточно прилично в 1709 году, но уже не в 1803 году, и новое требование потребителя закрыть клавиатуру по таймаутуИтак, теперь я добавил код из последнего поста этого вопроса stackoverflow в вышеприведенный класс как функцию (я не думаю, что это правильный путь):

   static class TouchKeyboard
   {
    ///Above code omitted

    public static bool GetIsOpen1803()
    {
        // do this once:
        var brokerClass = new ImmersiveShellBroker();
        var broker = (IImmersiveShellBroker)brokerClass;
        var ihm = broker.GetInputHostManagerBroker();
        Marshal.ReleaseComObject(broker);

        // now ihm reference can be cached and used later:
        Rect rect;
        DisplayMode mode;
        ihm.GetIhmLocation(out rect, out mode);

    }

    [ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
    class ImmersiveShellBroker
    {
    }

    [ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IImmersiveShellBroker
    {
        void Dummy();
        IInputHostManagerBroker GetInputHostManagerBroker();
    }

    [ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IInputHostManagerBroker
    {
        void GetIhmLocation(out Rect rect, out DisplayMode mode);
    }

    [StructLayout(LayoutKind.Sequential)]
    struct Rect
    {
        public int Left, Top, Right, Bottom;
    }

    enum DisplayMode
    {
        NotSupported = 0,
        Floating = 2,
        Docked = 3,
    }
    }

Я думаю, что реализую его неправильнотак как он не работает все время, когда я вызываю GetIsOpen1803 из вышеупомянутого события тайм-аута, он иногда возвращает прямоугольник как 0,0,0,0, пока я вижу, что клавиатура работает.В коде в комментариях написано «делайте это только один раз» и «можно кэшировать и использовать позже», но я не могу понять, как это сделать в классе Touchkeyboard, который у нас есть.

Пока что получение методов GetIsOpen для надежного возврата, если клавиатура находится на экране из любого места в приложении, и возможность ее закрытия закрывает глаза.Даже завершение всех процессов табуляции не всегда закрывает клавиатуру!Единственное, что работает, - это прекращение работы службы, но я не могу этого сделать из-за необходимых повышенных привилегий.

Так что, безусловно, мой день может найти надежный способ определения, открыта ли клавиатура для всех окон 10.версии и надежный способ его закрытия или переключения видимости (если есть!)

Любая помощь будет принята с благодарностью!

Ответы [ 3 ]

0 голосов
/ 27 июля 2018

Как заметил Тим Бийненс, когда он запускал этот поток, переключение клавиатуры (ITipInvocation.Toggle ()) делает не всегда, вызывая клавиатуру.

В Win10 Ver 1803, DesktopMode, нет надежного способа
включить / выключить «Сенсорную клавиатуру» [ITipInvocation.Toggle ()];
и вы не можете надежно обнаружить, что она «вверх» (на экране)
[IFrameworkInputPane.Location ()];обе процедуры не выполняются случайным образом .

Вместо этого убедитесь, что «TabTIP.EXE» и «.... InputApp.EXE»
работают только тогда, когда клавиатура «вверх» (на экране);см .:
https://stackoverflow.com/a/51376030/5732431

0 голосов
/ 10 сентября 2018

Мы НЕ могли заставить клавиатуру надежно работать с предоставленными решениями.Единственное (и простое) решение, которое сработало для нас, - это обновление до .Net Framework 4.7.2 и обновление всех пакетов Nuget в наших решениях с его помощью.Мы не могли использовать 4.7.2 пару месяцев назад, поскольку у ClickOnce не было необходимости исправлять встроенные ссылки, это было решено в недавнем обновлении.

После обновления до 4.7.2 мы удалили весь код, связанный с клавиатуройиз нашего приложения, и теперь все работает как положено (в режиме планшета, что мы и хотели), без какого-либо кода с нашей стороны, я мог бы добавить!Поэтому причина, по которой я не публикую ни одного кода, заключается просто потому, что его нет!

0 голосов
/ 24 июня 2018

Можете ли вы попробовать обновленный вариант моей библиотеки - https://github.com/AlexeiScherbakov/osklib

Вы можете использовать OnScreenKeyboardWatcher / DispatcherOnScreenKeyboardWatcher / WinFormsKeyboardWatcher для: 1. Обнаружения текущего состояния клавиатуры 2. Посмотрите, как долго клавиатура открыта (для тайм-аута)

Я тестировал этот код на старых версиях (1709, 1703 и 1607). На моем 1803 (сборка 17134) он также работает

...