Получить текст и каретку из текстового поля в другом приложении - PullRequest
0 голосов
/ 01 октября 2018

Справочная информация

Я работаю в приложении для преобразования речи в текст.

Когда речь будет распознана, текст должен быть вставлен в текстовое полекоторый в настоящее время имеет фокус клавиатуры (Think Word / Firefox / другие приложения).Для вставки текста я сейчас использую InputSimulatorPlus .

При вставке текста важно, чтобы распознанный текст был отформатирован так, чтобы соответствовать окружающему тексту, например:

  • заглавная буква после пунктуации
  • заглавная первая буква приэто НЕ после пунктуации
  • заглавная первая буква на новой строке
  • пробел после пунктуации

И так далее

Проблема

Для возможности форматирования текста мне нужно текст и позиция каретки .

В настоящее время я использую UI Automation Пакет NuGet с Текстовый шаблон .Это хорошо работает для всех текстовых полей, которые поддерживают Text Pattern, но многие программы не поддерживают Text Pattern.

Стратегический вопрос: Должен ли я использовать другой подход, чем UI Automation?

Я заметил, что многие приложения, которые я хочу поддерживать, не поддерживают текстовый шаблон, но поддерживают шаблон значений или устаревший шаблон IAccessible ( Microsoft Active Accessibility ).

Я изучал использование шаблона значений и могу получить текст текстового поля, но не положение каретки.

using System.Windows.Automation;
using System.Windows.Automation.Text;
...

AutomationElement automationElement = AutomationElement.FocusedElement;
var elements = automationElement.FindAll(TreeScope.Element,
    new AndCondition(
        new PropertyCondition(AutomationElement.HasKeyboardFocusProperty, true),
        new PropertyCondition(AutomationElement.IsValuePatternAvailableProperty, true)));
foreach (AutomationElement element in elements)
{
    if (element.GetCurrentPattern(ValuePattern.Pattern) is ValuePattern valuePattern)
    {
        var text = valuePattern.Current.Value;
        var caret = ? //How to get caret position?
        Console.WriteLine($"Caret: {caret}, Text: {text}");
        return (text,caret);
    }
}

Я также изучал использование Legacy IAccessible Pattern и могу получить текст текстового поля, но не положение каретки.

using System.Windows.Automation;
using System.Windows.Automation.Text;
...

AutomationElement automationElement = AutomationElement.FocusedElement;
var elements = automationElement.FindAll(TreeScope.Element,
    new AndCondition(
        new PropertyCondition(AutomationElement.HasKeyboardFocusProperty, true),
        new PropertyCondition(AutomationElement.IsLegacyIAccessiblePatternAvailableProperty, true)));
foreach (AutomationElement element in elements)
{
    if (element.GetCurrentPattern(LegacyIAccessiblePattern.Pattern) is LegacyIAccessiblePattern legazyAccessiblePattern)
    {
        var text = legazyAccessiblePattern.Current.Value;
        var caret = ? //How to get caret position?
        Console.WriteLine($"Caret: {caret}, Text: {text}");
        return (text,caret);
    }
}

Основной вопрос: Как получить позицию каретки для данного текстового поля с помощью UI Automation?

PS Я знаю, что это никогда не будет работать для всех приложений, но если этоможет работать для большинства обычных приложений, это было бы здорово.

1 Ответ

0 голосов
/ 02 октября 2018

Вам не нужен ValuePattern, вам нужен IUIAutomationTextPattern2, который поддерживает GetCaretRange .

Если вам нужно вернуться к MSAA, это очень многохакер (и вы не можете получить текст вокруг каретки вообще).MSAA просто не имеет ничего общего с текстовой поддержкой UIAutomation.Если вы ДЕЙСТВИТЕЛЬНО ДЕЙСТВИТЕЛЬНО не можете использовать UIAutomation, тогда единственный реальный вариант - использовать Text Services Framework , который плохо документирован и поэтому не имеет широкого применения.Если вам нужна Text Services Framework, вы можете посмотреть мой блог , который давно не обновлялся.

Чтобы узнать о MSAA, начните с вызова GetGUIThreadInfo.Это возвращает данные о hwndCaret, который является окном, которое в настоящее время содержит каретку.(Или вы можете попробовать использовать hwndFocus, если hwndCaret НЕДЕЙСТВИТЕЛЕН.) Используя одно из этих окон, вы можете сделать что-то вроде:

IAccessible *pAccCaret = NULL;

VARIANT varCaret;
varCaret.vt = VT_I4;
varCaret.lVal = CHILDID_SELF;

if (SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_CARET, IID_IAccessible, (void **)&pAccCaret)))
{
    hr = pAccCaret->accLocation( &rcCaret.left, &rcCaret.top, &rcCaret.right, &rcCaret.bottom, varCaret);

    pAccCaret->Release();
}
...