Как я могу получить позицию каретки из текстового поля в другом приложении? (Не координаты, а фактический индекс внутри текстового поля) - PullRequest
0 голосов
/ 22 апреля 2020

Мне нужно получить индекс каретки внутри текстового поля в сфокусированном окне, возможно, с помощью UI Automation или, возможно, функции Win32 API, если есть какая-либо функция, которая делает это. И я подчеркиваю, я имею в виду не координаты x, y, а индекс каретки внутри текста текстового поля. Как я могу это сделать? Также см. этот аналогичный вопрос.

1 Ответ

1 голос
/ 22 апреля 2020

Для этого вы можете использовать UI Automation, особенно интерфейс IUIAutomationTextPattern2 , который имеет метод GetCaretRange .

Вот пример кода Consoleapp C ++, который работает непрерывно и отображает позицию каретки для текущего элемента под мышью:

C ++

int main()
{
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    {
        CComPtr<IUIAutomation> automation;

        // make sure you use CLSID_CUIAutomation8, *not* CLSID_CUIAutomation
        automation.CoCreateInstance(CLSID_CUIAutomation8);
        do
        {
            POINT pt;
            if (GetCursorPos(&pt))
            {
                CComPtr<IUIAutomationElement> element;
                automation->ElementFromPoint(pt, &element);
                if (element)
                {
                    CComBSTR name;
                    element->get_CurrentName(&name);
                    wprintf(L"Watched element %s\n", name);

                    CComPtr<IUIAutomationTextPattern2> text;
                    element->GetCurrentPatternAs(UIA_TextPattern2Id, IID_PPV_ARGS(&text));
                    if (text)
                    {
                        // get document range
                        CComPtr<IUIAutomationTextRange> documentRange;
                        text->get_DocumentRange(&documentRange);

                        // get caret range
                        BOOL active = FALSE;
                        CComPtr<IUIAutomationTextRange> range;
                        text->GetCaretRange(&active, &range);
                        if (range)
                        {
                            // compare caret start with document start
                            int caretPos = 0;
                            range->CompareEndpoints(TextPatternRangeEndpoint_Start, documentRange, TextPatternRangeEndpoint_Start, &caretPos);
                            wprintf(L" caret is at %i\n", caretPos);
                        }
                    }
                }
            }
            Sleep(500);
        } while (TRUE);
    }
    CoUninitialize();
    return 0;
}

C#

static void Main(string[] args)
{
    // needs 'using UIAutomationClient;'
    // to reference UIA, don't use the .NET assembly
    // but instead, reference the UIAutomationClient dll as a COM object
    // and set Embed Interop Types to False for the UIAutomationClient reference in the C# project
    var automation = new CUIAutomation8();
    do
    {
        var cursor = System.Windows.Forms.Cursor.Position;
        var element = automation.ElementFromPoint(new tagPOINT { x = cursor.X, y = cursor.Y });
        if (element != null)
        {
            Console.WriteLine("Watched element " + element.CurrentName);
            var guid = typeof(IUIAutomationTextPattern2).GUID;
            var ptr = element.GetCurrentPatternAs(UIA_PatternIds.UIA_TextPattern2Id, ref guid);
            if (ptr != IntPtr.Zero)
            {
                var pattern = (IUIAutomationTextPattern2)Marshal.GetObjectForIUnknown(ptr);
                if (pattern != null)
                {
                    var documentRange = pattern.DocumentRange;
                    var caretRange = pattern.GetCaretRange(out _);
                    if (caretRange != null)
                    {
                        var caretPos = caretRange.CompareEndpoints(
                            TextPatternRangeEndpoint.TextPatternRangeEndpoint_Start,
                            documentRange,
                            TextPatternRangeEndpoint.TextPatternRangeEndpoint_Start);
                        Console.WriteLine(" caret is at " + caretPos);
                    }
                }
            }
        }
        Thread.Sleep(500);
    }
    while (true);
}

Хитрость заключается в использовании IUIAutomationTextRange :: CompareEndpoints метод, который позволяет сравнивать диапазон каретки с другим диапазоном, например, со всем диапазоном документа.

Обратите внимание, что существуют недостатки:

  • некоторые приложения не не поддерживает самоанализ MSAA / UIA или не поддерживает текстовый шаблон. Для них просто не существует решения (даже с использованием Windows API, я думаю)
  • некоторые приложения сообщают о каретке неправильно, особенно когда вы выбираете текст (перемещение каретки). Например, с помощью блокнота, перемещение ВЛЕВО при нажатии SHIFT приведет к обратному выбору, но по какой-то причине UIA не обновляет поз. Я думаю, что это проблема с блокнотом, потому что он имеет проблему с IME (редактор метода ввода, такой как редактор Emoji, который вы можете вызывать, используя Win + ; комбинацию клавиатуры). Нет проблем с WordPad, например.
...