【问题标题】:UI Automation - ElementFromHandle doesn't find elementUI 自动化 - ElementFromHandle 找不到元素
【发布时间】:2014-04-09 16:02:04
【问题描述】:

我正在尝试使用 UIAutomation 访问/控制 Chrome 浏览器(使用基于其他人使用的方法 - 例如获取当前 URL)。

出于练习的目的,我试图在 Delphi 中复制这个问题 - Retrieve current URL from C# windows forms application。我已经导入了 TLB 好的。但是,我对 ElementFromHandle 的调用从未找到元素。

ElementFromHandle 方法的签名是:

function ElementFromHandle(hwnd: Pointer; out element: IUIAutomationElement): HResult; stdcall;

我的测试很简单:

procedure TForm3.Button1Click(Sender: TObject);
var
  UIAuto: IUIAutomation;
  element: IUIAutomationElement;
  value: WideString;
  h: PInteger;
begin
  new(h);
  h^ := $1094E;
  SetForegroundWindow(h^);
  ShowWindow(h^, SW_SHOW);
  UIAuto := CoCUIAutomation.Create;
  UIAuto.ElementFromHandle(h, element);
  if Assigned(element) then
  begin
    element.Get_CurrentName(value);
    showmessage('found -' + value);
  end
  else
    showMessage('not found');
end;

调用 SetForegroundWindow 和 ShowWindow 只是为了以防它需要焦点(但我怀疑它会有所作为并且不会)。我可以确认我传入的句柄($1094E)是“正确的”,因为 Spy++ 显示了我尝试访问的 Chrome 选项卡的值。 Chrome 中的活动标签始终报告该句柄。

我上面的实现是否正确?使用 UIAutomation 是否比我上面实现的更多?我以前从未探索过。

谢谢

编辑

我发现如果我使用 ElementFromPoint 并传入一个(硬编码)值,我知道我的 Tab 位于 X、Y 的位置 - 它确实有效。即:

  UIAuto := CoCUIAutomation.Create;
  p.x := 2916;
  p.y := 129;
  UIAuto.ElementFromPoint(p, element);
  if Assigned(element) then

如果放置在上面的 OnClick 事件中,上面的 sn-p 确实会返回一个元素实例,并且我也期待这个实例(这是一个奖励)。所以也许我在 ElementFromHandle 中为 Hwnd 传递了一个不正确的值?即,我正在使用我的 MS Spy++ 中找到的 Chrome 的“顶级”句柄:

它直接位于 Spy++ 中的 (Desktop) 下方。

【问题讨论】:

  • 摆脱对 New 的调用。将 h 声明为 HWND 类型。它不是整数。下一步是检查错误。完成后,请告诉我们会发生什么。
  • 问题是该方法需要一个指针而不是第一个参数的 HWnd
  • 你的问题。让我把它写下来作为答案。
  • 我已将它包含在问题的顶部。元素来自句柄。这是由 TypeLibrary 导入器自动生成的。在 _TLB 文件中无论如何我都必须更改一些东西,所以如果将其更改为 HWnd 来修复它,那就太好了。不过 12 小时左右都没有机会测试。

标签: delphi ui-automation delphi-xe3


【解决方案1】:

您的错误在于您将窗口句柄传递给ElementFromHandle 的方式。你应该通过HWND。相反,您传递HWND 的地址。

函数真的应该是:

function ElementFromHandle(hwnd: HWND; 
    out element: IUIAutomationElement): HResult; stdcall;

您应该删除对New 的调用,改为:

var
  window: HWND;
....
window := HWND($1094E);

然后像这样调用函数:

if Succeeded(UIAuto.ElementFromHandle(window, element)) then
  ....

也许您最大的根本问题是完全没有错误检查。我认为您需要调整心态,以意识到这些 API 调用不会引发异常。他们通过返回值报告失败。您必须检查每个 API 调用是否失败。

一种常见的方法是将HRESULT 值转换为异常,以防调用OleCheck 失败。例如:

var
  UIAuto: IUIAutomation;
  element: IUIAutomationElement;
  value: WideString;
  window: HWND;
....
window := HWND($1094E);
SetForegroundWindow(window);
ShowWindow(window, SW_SHOW);
UIAuto := CoCUIAutomation.Create;
OleCheck(UIAuto.ElementFromHandle(window, element));
OleCheck(element.Get_CurrentName(value));
ShowMessage('found -' + value);

【讨论】:

  • 绝对是导入类型库的方式。更改为 HWnd 而不是 Pointer 修复了它。干杯
  • 只是出于好奇,你怎么知道ElementFromHandle 声明是错误的?
  • @Wodzu 它是这样记录的。但是,我确信我只是查看了代码,虽然奇怪的是用户正在传递 HWND 变量的地址。当被调用者向调用者返回一个窗口时,这将是有意义的。但在这种情况下,它是一个 IN 参数,因此您会期望 HWND 按值传递。
猜你喜欢
  • 1970-01-01
  • 2012-01-19
  • 1970-01-01
  • 2019-02-26
  • 2012-07-09
  • 1970-01-01
  • 2020-11-13
  • 2016-11-03
  • 2017-12-05
相关资源
最近更新 更多