如何强制 UI 自动化树刷新

How to force UI automation tree refresh

我目前正在尝试使用 System.Windows.Automation 来自动化一个 chrome 实例,但在某个时刻 AutomationElement.FindAll(TreeScope.Children, Condition.TrueCondition); returns 总是有 0 个子节点,但我可以看到它们在 inspect.exe。当我在 inspect.exe 中点击刷新时,元素也会出现在我的应用程序中。

在寻找解决方案时,我发现 this SO post,OP 或多或少有同样的问题。 建议使用的答案:

SystemParametersInfo( SPI_SETSCREENREADER, TRUE, NULL, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
PostMessage( HWND_BROADCAST, WM_WININICHANGE, SPI_SETSCREENREADER, 0);

我将其实现为:

[DllImport("user32.dll", SetLastError = true)]
static extern bool SystemParametersInfo(int uiAction, int uiParam, IntPtr pvParam, int fWinIni);

[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

....

const int SPI_SETSCREENREADER = 0x0047;

IntPtr inptrnull = IntPtr.Zero;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDCHANGE = 0x02;


IntPtr HWND_BROADCAST = new IntPtr(0xffff);
const int WM_WININICHANGE = 0x001A;

SystemParametersInfo(SPI_SETSCREENREADER, 1, inptrnull, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
PostMessage(HWND_BROADCAST, WM_WININICHANGE, SPI_SETSCREENREADER, 0);

但是没有效果

我错过了什么? 有 other/better 方法吗?

看起来您使用的是过时的托管 UIA 实现 (System.Windows.Automation)。 这是一个已知的案例,它可能 return 并非所有 children,这可能是原因。

根据 documentation:

For Windows 7, the API has been rewritten in the Component Object Model (COM). Although the library functions introduced in the earlier version of UI Automation are still documented, they should not be used in new applications.

您可以尝试使用 UIA COM interface(Inspect 使用相同的方法):

  1. 将对 COM 的引用添加到您的项目 -> UIAutomationClient。这将为您生成一个包装器,因此可以在代码中以通常的方式访问 COM 接口 OO-style.

  2. 创建一个 UI 自动化 object 并使用 UIA 元素做你的事情:

    IUIAutomation automation = new CUIAutomation();
    // Start with the root element; you could also get an element from the point or current focus
    IUIAutomationElement element = automation.GetRootElement();
    IUIAutomationElementArray foundElements = element.FindAll(TreeScope.TreeScope_Children, automation.CreateTrueCondition());
    for (int i = 0; i < foundElements.Length; i++)
    {
        IUIAutomationElement currentElement = foundElements.GetElement(i);
        // do your stuff with the element: get its properties, etc
    }
    
  3. 如果上述方法没有帮助,您可以尝试使用 RawTreeWalker 而不是调用 FindAll() 来遍历 UI 树:

    IUIAutomationTreeWalker walker = automation.RawViewWalker;
    IUIAutomationElement child = walker.GetFirstChildElement(element);
    var sibling = walker.GetNextSiblingElement(child);
    while (sibling != null)
    {
        // do your stuff with sibling elements (get their child elements, etc)
        sibling = walker.GetNextSiblingElement(child);
    }
    
  4. 我之前使用 UIA 自动化了 Chrome,据我所知,它可能会响应 UIA 调用,这有点奇怪:例如有时它 return 是一个容器元素而不是实际的元素(按钮、编辑等)。我通过多次尝试解决了这个问题,直到我最终找回了预期的结果。如果您也多次调用 FindAll() 怎么办?