如何从另一个应用程序的文本框中获取插入符的位置? (不是坐标,而是文本框内的实际索引) [英] How can I get the caret position from a textbox in another application? (Not the coordinates, but the actual index inside of the textbox)

查看:288
本文介绍了如何从另一个应用程序的文本框中获取插入符的位置? (不是坐标,而是文本框内的实际索引)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在焦点窗口的文本框中检索插入符号的索引,如果有的话,可能需要使用UI Automation或Win32 API函数. 我要强调的是,我不是指x,y坐标,而是指文本框文本内的插入符号的索引.我怎样才能做到这一点? 另请参见类似问题. >

I need to retrieve the index of the caret inside a textbox in the focused window, maybe using UI Automation, or maybe a Win32 API function, if there's any function that cat do that. And I emphasize, I don't mean the x,y coordinates, but the index of the caret inside the text of the textbox. How can I do that? Also see this similar question.

推荐答案

您可以为此使用UI自动化,尤其是

You can use UI Automation for that, and especially the IUIAutomationTextPattern2 interface that has a GetCaretRange method.

以下是示例Consoleapp C ++代码,该代码连续运行并在鼠标下显示当前元素的插入符位置:

Here is a sample Consoleapp C++ code that runs continuously and displays the caret position for the current element under the mouse:

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 方法,使您可以将插入符号范围与另一个范围(例如整个文档范围)进行比较.

The trick is to use the IUIAutomationTextRange::CompareEndpoints method that allows you to compare the caret range with another range, for example the whole document range.

请注意有缺点:

  • 某些应用程序根本不支持任何MSAA/UIA自省,或不支持文本模式.对于这些,根本没有解决方案(我认为即使使用Windows API)
  • 某些应用程序错误地报告了插入符号,尤其是当您选择文本(移动插入符号)时.例如,使用记事本,在按下 SHIFT 的同时移动 LEFT 会向后选择,但由于某些原因,UIA不会更新插入符号pos.我认为这是记事本的问题,因为它与IME有关(输入法编辑器,如Emoji编辑器,您可以使用 Win + ; 键盘组合进行召唤)有插入符号问题.例如,写字板没有问题.
  • some apps don't support any MSAA/UIA introspection at all, or don't support the text pattern. For these, there are simply no solution (even using Windows API I think)
  • some apps report the caret incorrectly, especially when you select text (moving caret). For example with notepad, moving LEFT while pressing SHIFT will select backwards but for some reason UIA doesn't update caret pos. I think it's a problem with notepad because it has caret issue with IME (Input Method Editor, like the Emoji editor you can summon using Win+; keyboard combination). There's no problem with wordpad for example.

这篇关于如何从另一个应用程序的文本框中获取插入符的位置? (不是坐标,而是文本框内的实际索引)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆