Для этого вы можете использовать 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, например.