以编程方式获取Win32传统控件的工具提示文本

时间:2013-04-04 10:22:09

标签: c# winapi automation tooltip

我想获取win32旧版控件的工具提示文本(不是本身支持UI Automation的WPF控件)。

Screenshot of the buttons

我做了什么:

  • 给定一个感兴趣的按钮,我得到了它的AutomationElement及其边界矩形
  • 我将鼠标移到此按钮上(代码中);
  • Thread.Sleep(1500)等待工具提示控件弹出;
  • 枚举桌面的所有子窗口,并获取类型为tooltipAutomationElement的子窗口"Tooltip";
  • tooltipAutomationElement获取此工具提示的名称属性,该属性对应于工具提示字符串。

这实际上有效,但惩罚是:我必须sleep(1500)并手动等待工具提示出现(5-20​​个按钮将被扫描工具提示字符串),这与性能要求不符。

预期结果(不确定是否可行)

  • 以编程方式获取按钮的工具提示字符串,而不需要显示工具提示
  • 无需逐个鼠标放在每个按钮上。

更新1 :对于TTN_NEEDTEXT,MSDN doc似乎不太清楚,我也不知道如何使用C#对其进行编程。可以找到与工具提示控制相关的低级结构/消息的相关链接之一here

更新2 :那些相信这可以通过...完成的人,我会说,说起来容易做起来难。我欢迎那些尝试过对此发表评论的人,如果你能提供一些证据来展示其适用性和功效,那么欢迎一些表面上可行的解决方案。

更新3 :如果我们尝试最小化TTM_SETDELAYTIME,以便N中的sleep(N)可以最小化,那么在经过一些实验后这不起作用。我们只能在工具提示窗口句柄存在后进行调整。 e.g。

SendMessage(_tooltipCtrl.Handle, TTM_SETDELAYTIME, _TTDT_INITIAL, 10); //10 ms

更新4 :使用TTM_GETTEXTA消息似乎是一个解决方案,但是,它类似于Update 3,我们需要tooltipCtrl的句柄,这是仅在创建工具提示后才可用,因为要创建此工具提示,我们别无选择,只能将鼠标光标悬停在工具上方,这似乎存在性能问题(Thread.Sleep),如上所述。

SendMessage(_tooltipCtrl.Handle, TTM_GETTEXTA, 0, ti);

更新5 :“如何获取工具提示文本”使用传统方法使用InterOp(PInvoke)或Automation UI(鼠标悬停在工具窗口上,找到Hwnd句柄,然后获取其文本。 ..)不是这篇文章的关注点。预期结果:我们是否可以提取控件的工具提示字符串(例如按钮)而无需悬停在控件上?如果有,怎么样?

更新6 :使用WM_MOUSEHOVER激活工具提示窗口似乎不起作用,我已经使用SendMessage(...)测试了正确的wparam和lparam填充,但是静脉。

3 个答案:

答案 0 :(得分:3)

只是一个想法,但尝试使用消息而不是利用实际的鼠标。

玩WM_HOVER,WM_MOUSEHOVER,WM_MOUSEENTER

 SendMessage(_buttonCtrl.Handle, WM_MOUSEHOVER, ..., ...)

您的屏幕截图看起来像一个自定义控件,所以这将是一个黑客问题,找出触发工具提示的内容。

您可以同时发送几个WM_MOUSEENTER或WM_MOUSEHOVER。这实际上取决于底层代码。

如果这导致了太长的延迟,(并且没有建议的解决方案有效),请考虑将工具提示测试拉入次要测试池,该测试池执行频率较低或仅在特定请求时执行。

另外......我确定你已经尝试过了......但如果没有,请查看UI Spy并查看它是否在实际生成之前报告有关工具提示的任何信息。

答案 1 :(得分:1)

从UI Automation的角度来看,我们可以做的最好的事情就是订阅ToolTip Opened Event并在事件处理程序中处理相同的事情。此链接包含示例http://msdn.microsoft.com/en-us/library/ms752286.aspx。可以在http://msdn.microsoft.com/en-us/library/ms748252.aspx找到UI自动化事件列表。有关AutomationElement.ToolTipOpenedEvent的详细信息,请访问http://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement.tooltipopenedevent.aspx

至少使用UI自动化,我们应该让事情发生,无论是点击按钮还是打开窗口或显示工具提示。因此,订阅ToolTip事件将在这里完成工作,其性能优于硬Sleep()延迟。否则,少数人提到的hacky方式(虽然不是端到端)是预先获取工具提示字符串的资源字符串ID,并在执行自动化测试期间验证资源字符串。

答案 2 :(得分:0)

将工具提示的父类子类化,即将按钮的windproc 替换为您的并过滤WM_NOTIFYTTN_GETDISPINFO 情况,可能像这样..

case TTN_GETDISPINFO:
{
 // do this first
 NMTTDISPINFO pttdi = (LPNMTTDISPINFO)lparam;

 // next let the system do the default, ie fill the 
 //   relevant structures with the 
 // text that will appear on the tooltip. 
 // so

 CallWindowProc(OldListViewProc, hwnd, message, wparam, lparam);
 
 // then the text you seek will be in the 
 // NMTTDISPINFO pttdi structure. You can even alter 
 // the text to suit your needs if you want.     
}

显然,Visual Studio 2008 的 MSDN 库显示,对于 _WIN32_WINNT >= 0x0600,即在Vista之后,这个结构HBITMAP hbmp有一个额外的成员,它有点隐藏,但代表“要在工具提示中显示的大预览位图的句柄”。