c#的uiautomation中关于tooltip的元素的捕获?

需要做pc的自动化操作。

一个winform的软件,鼠标悬停到某个表格的单元格上,有个tooltip,我需要捕获那个tootip的文字信息。
如图“被引用文件中的问题”这样的字样。
https://learn.microsoft.com/zh-cn/dotnet/framework/ui-automation/ui-automation-support-for-the-tooltip-control-type
网上的资料很少,只有这一篇,也没好到解决方案。
谢谢!

img

引用new bing部分回答作答:
在 C# 的 UIAutomation 中,要获取 Tooltip 的文字信息,可以通过以下步骤:

1 首先使用 UIAutomation 查找到对应的表格单元格元素。可以使用 FindFirst() 或 FindAll() 方法,通过元素的 Name、AutomationId、ClassName、ControlType 等属性进行查找。例如:

var tableCell = window.FindFirst(TreeScope.Descendants,
    new PropertyCondition(AutomationElement.NameProperty, "单元格名称"));

2 一旦找到了表格单元格元素,可以使用 GetUpdatedCacheRequest() 方法来获取当前 UIAutomation 缓存中的元素信息。例如:

var cacheRequest = new CacheRequest();
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.Add(ValuePattern.ValueProperty);
cacheRequest.Add(ValuePattern.IsReadOnlyProperty);
cacheRequest.Add(ValuePattern.IsPasswordProperty);
cacheRequest.TreeScope = TreeScope.Element | TreeScope.Descendants;
cacheRequest.AutomationElementMode = AutomationElementMode.Full;
var cachedElement = Automation.Cached.GetUpdatedCacheRequest(cacheRequest).FindFirst(
    TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "单元格名称"));

3 如果表格单元格上有 Tooltip,可以使用 AutomationElement.ToolTipOpenedEvent 和 AutomationElement.ToolTipClosedEvent 事件来检测 Tooltip 的打开和关闭。例如:

var tooltip = Automation.AddAutomationEventHandler(
    AutomationElement.ToolTipOpenedEvent, tableCell, TreeScope.Element, (sender, e) =>
{
    // 在此处获取 Tooltip 的信息
});

4 在 tooltip 事件处理程序中,可以使用 e.SourceElement 属性来获取打开 Tooltip 的元素。可以使用 GetUpdatedCacheRequest() 方法来获取 Tooltip 元素的缓存信息。例如:

var tooltipElement = e.SourceElement.GetUpdatedCacheRequest(cacheRequest);
var tooltipText = tooltipElement.GetCurrentPropertyValue(ValuePattern.ValueProperty) as string;

为了确保能够正确捕获 Tooltip 的信息,需要在代码中加入适当的延时或等待操作,以确保 Tooltip 已经完全打开并加载了其内容。

您可以尝试使用鼠标钩子(Mouse Hook)和控件提供的 MouseHover 事件来实现获取 Tooltip 文字信息的功能。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace TooltipDemo
{
    public partial class Form1 : Form
    {
        private IntPtr _tooltipHandle = IntPtr.Zero;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 注册鼠标钩子
            MouseHook.Start();
            // 订阅 MouseHover 事件
            dataGridView1.MouseHover += DataGridView1_MouseHover;
        }

        private void DataGridView1_MouseHover(object sender, EventArgs e)
        {
            if (_tooltipHandle != IntPtr.Zero)
            {
                // 获取 Tooltip 文字信息
                string tooltipText = GetTooltipText(_tooltipHandle);
                MessageBox.Show(tooltipText);
                // 清空句柄
                _tooltipHandle = IntPtr.Zero;
            }
        }

        private void dataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
        {
            // 获取单元格句柄
            IntPtr cellHandle = GetCellHandle(e.ColumnIndex, e.RowIndex);
            if (cellHandle != IntPtr.Zero)
            {
                // 获取 Tooltip 句柄
                _tooltipHandle = GetTooltipHandle(cellHandle);
            }
        }

        private IntPtr GetCellHandle(int columnIndex, int rowIndex)
        {
            // 获取单元格句柄
            IntPtr handle = IntPtr.Zero;
            if (dataGridView1[columnIndex, rowIndex].Displayed)
            {
                handle = dataGridView1[columnIndex, rowIndex].DataGridView.Handle;
                handle = WinAPI.ChildWindowFromPointEx(handle, dataGridView1[columnIndex, rowIndex].ContentBounds.Left + 5,
                    dataGridView1[columnIndex, rowIndex].ContentBounds.Top + 5, WinAPI.CWP_SKIPINVISIBLE);
            }
            return handle;
        }

        private IntPtr GetTooltipHandle(IntPtr cellHandle)
        {
            // 获取 Tooltip 句柄
            return WinAPI.SendMessage(cellHandle, WinAPI.TTM_GETTOOLINFOA, IntPtr.Zero, ref _tooltipInfo);
        }

        private string GetTooltipText(IntPtr tooltipHandle)
        {
            // 获取 Tooltip 文字信息
            var buffer = new byte[256];
            var tt = new WinAPI.TOOLINFO();
            tt.cbSize = Marshal.SizeOf(tt);
            tt.hwnd = dataGridView1.Handle;

            var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(tt));
            Marshal.StructureToPtr(tt, ptr, true);
            try
            {
                var result = WinAPI.SendMessage(tooltipHandle, WinAPI.TTM_GETTEXTA, new IntPtr(255), ptr);
                if (result != IntPtr.Zero)
                {
                    tt = (WinAPI.TOOLINFO)Marshal.PtrToStructure(ptr, typeof(WinAPI.TOOLINFO));
                    Marshal.Copy(tt.lpszText, buffer, 0, 255);
                }
            }
            finally
            {
                Marshal.FreeHGlobal(ptr);
            }
            return System.Text.Encoding.Default.GetString(buffer).TrimEnd('\0');
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 卸载鼠标钩子
            MouseHook.Stop();
        }
    }

    public static class MouseHook
    {
        private static IntPtr _hookId = IntPtr.Zero;
        private static WinAPI.HookProc _hookProc = MouseHookProc;

        public static void Start()
        {
            // 安装鼠标钩子
            _hookId = WinAPI.SetWindowsHookEx(WinAPI.WH_MOUSE_LL, _hookProc, IntPtr.Zero, 0);
        }

        public static void Stop()
        {
            // 卸载鼠标钩子
            WinAPI.UnhookWindowsHookEx(_hookId);
        }

        private static IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WinAPI.WM_MOUSEMOVE)
            {
                // 获取鼠标所在位置的句柄
                var handle = WinAPI.WindowFromPoint(Cursor.Position);
                // 检查句柄是否属于要处理的表格控件
                if (handle != IntPtr.Zero && handle == MainForm.dataGridView1.Handle)
                {
                    // 查询 Tooltip 是否显示
                    var tti = new WinAPI.TTTOOLINFO();
                    tti.cbSize = Marshal.SizeOf(tti);
                    tti.hwnd = MainForm.dataGridView1.Handle;
                    tti.uFlags = WinAPI.TTF_IDISHWND | WinAPI.TTF_SUBCLASS;
                    tti.uId = handle;

                    var tooltipHandle = WinAPI.SendMessage(MainForm._tooltipHandle, WinAPI.TTM_GETTOOLINFOA, IntPtr.Zero, ref tti);
                    if (tooltipHandle != IntPtr.Zero)
                    {
                        // 发送 WM_MOUSELEAVE 消息以关闭 Tooltip
                        WinAPI.SendMessage(tooltipHandle, WinAPI.WM_MOUSELEAVE, IntPtr.Zero, IntPtr.Zero);
                    }
                }
            }
            return WinAPI.CallNextHookEx(_hookId, nCode, wParam, lParam);
        }
    }

    public static class WinAPI
    {
        public const int WM_MOUSEMOVE = 0x200;
        public const int WM_MOUSELEAVE = 0x2A3;
        public const int WH_MOUSE_LL = 14;
        public const int CWP_SKIPINVISIBLE = 0x0001;
        public const int TTM_GETTOOLINFOA = 0x0400 + 13;
        public const int TTM_GETTEXTA = 0x0400 + 45;
        public const int TTF_IDISHWND = 0x0001;
        public const int TTF_SUBCLASS = 0x0010;

        [StructLayout(LayoutKind.Sequential)]
        public struct TOOLINFO
        {
            public int cbSize;
            public int uFlags;
            public IntPtr hwnd;
            public IntPtr uId;
            public RECT rect;
            public IntPtr hinst;
            public IntPtr lpszText;
            public IntPtr lParam;
        }

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

        public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref TOOLINFO lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr ChildWindowFromPointEx(IntPtr hWndParent, int x, int y, int uFlags);

        [DllImport("user32.dll")]
        public static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll")]
        public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll")]
        public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern IntPtr WindowFromPoint(System.Drawing.Point p);
    }
}


在上述代码中,我们通过鼠标钩子(MouseHook)检测鼠标移动事件,并查询当前鼠标所在位置的句柄是否属于要处理的表格控件。如果 Tooltip 已经显示,则发送 WM_MOUSELEAVE 消息以关闭 Tooltip 并获取其文字信息。
注意,该代码仅提供基本思路,若有不懂的地方可以留言,看到了就会恢复哦

要自动化获取WinForm应用程序中的工具提示的文本信息,您可以使用UI自动化技术。

首先,您需要使用UI自动化API获取对表格单元格的UI元素的引用。然后,您可以使用UI元素的AutomationElement.ToolTipOpened事件来获取工具提示的UI元素。

一旦您有了工具提示的UI元素,您可以使用AutomationElement.Name属性获取工具提示中的文本信息。下面是一个示例代码片段,演示如何使用UI自动化获取工具提示的文本信息:

using System.Windows.Automation;

// 获取表格单元格的UI元素
AutomationElement cellElement = ...;

// 注册ToolTipOpened事件
Automation.AddAutomationEventHandler(
    ToolTipOpenedEvent,
    cellElement,
    TreeScope.Element,
    (sender, e) =>
    {
        // 获取工具提示的UI元素
        AutomationElement tooltipElement = (AutomationElement)e.AutomationElement;

        // 获取工具提示中的文本信息
        string tooltipText = tooltipElement.Current.Name;

        // 在控制台输出文本信息
        Console.WriteLine("Tooltip text: " + tooltipText);
    }
);

注意,您需要在UI自动化客户端应用程序中引用UI自动化API,以便使用Automation类和AutomationElement类。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
你可以使用 UI Automation API 的 Tooltip Control Type 来实现获取 tooltip 中的文本信息。以下是实现过程:

  1. 使用 UI Automation API 获取被悬停控件的 AutomationElement 对象。
  2. 通过 AutomationElement 对象获取该控件的 Tooltip Control Type.
  3. 获取 Tooltip Text 对应的 AutomationElement 对象。
  4. 获取该 AutomationElement 对象的 TextPattern 然后获取 Tooltip Text.

以下为示例代码:

using System.Windows.Automation;

// 获取被悬停控件的 AutomationElement 对象
AutomationElement element = AutomationElement.RootElement.FindFirst(
    TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "目标控件名称"));

if (element != null)
{
    // 获取该控件的 Tooltip Control Type.
    AutomationElement tooltip = element.FindFirst(
        TreeScope.Children,
        new AndCondition(
            new PropertyCondition(
                AutomationElement.IsControlElementProperty, true),
            new PropertyCondition(
                AutomationElement.ControlTypeProperty,
                ControlType.ToolTip)));

    if (tooltip != null)
    {
        // 获取 tooltip 中的 Text 对应的 AutomationElement 对象.
        AutomationElement text = tooltip.FindFirst(
            TreeScope.Children,
            new AndCondition(
                new PropertyCondition(
                    AutomationElement.IsControlElementProperty, true),
                new PropertyCondition(
                    AutomationElement.ControlTypeProperty,
                    ControlType.Text)));

        if (text != null)
        {
            // 获取 tooltip 中的文本信息.
            TextPattern pattern = text.GetCurrentPattern(TextPattern.Pattern) as TextPattern;
            string tooltipText = pattern.DocumentRange.GetText(-1);
            // 输出 tooltip 信息.
            Console.WriteLine(tooltipText);
        }
    }
}

注意:如果以上代码获取到的 tooltip 与目标控件之间存在深度结构,可能需要根据实际情况搭配 TreeScope 和 PropertyCondition 参数来搜索正确的 AutomationElement 对象。

希望以上信息对你有帮助!
如果我的回答解决了您的问题,请采纳!

内容来源与ChatGpt4及newbing和百度:


您可以使用UIAutomation来捕获tooltip元素的信息。以下是捕获tooltip元素信息的示例代码:

AutomationElement element = //获取表格单元格元素
AutomationElement tooltip = null;

//获取tooltip元素
Condition condition = new PropertyCondition(AutomationElement.ClassNameProperty, "tooltips_class32");
AutomationElementCollection tooltips = element.FindAll(TreeScope.Descendants, condition);
if (tooltips.Count > 0)
{
    tooltip = tooltips[0];
}

//获取tooltip文字信息
string tooltipText = tooltip.Current.Name;

首先,您需要获取表格单元格的AutomationElement对象。然后,使用ClassName为"tooltips_class32"的条件来查找tooltip元素。如果找到了tooltip元素,就可以获取它的Current.Name属性来获取文字信息。

请注意,这个示例代码只是一个参考,具体实现可能需要根据您的具体情况进行调整。


祝您问题迎刃而解