【问题标题】:How to return errors from UI Automation pattern provider?如何从 UI 自动化模式提供程序返回错误?
【发布时间】:2015-07-20 13:43:42
【问题描述】:

假设我在自定义控件中实现了一些 UIA 模式。说,TablePattern。如果出现任何问题,现有实现将返回 null。但是调试起来不是很方便。我可能在自动化同行中有更多的上下文。例如,对于GetItem(int row, int column),我可能会说提供的参数超出范围,而不仅仅是返回 null。

如果我从自动化对等方抛出异常 - 在 UIA 客户端,我会从 IUIAutomationPatternInstance 对象获得 TargetInvocationException,但没有任何详细信息(InnerException 属性为空)。

有没有办法让 UIA 将带有一些附加信息的错误从 UIA 服务器端传递到 UIA 客户端?


UPD: 在与 cmets 中提供的示例 @SimonMourier 进行一些调查和比较后,我发现 TargetInvocationException 是我的错。修复它here

现在我得到了正确的异常类型,但只有一个标准的异常消息。对于IndexOutBoundsException,它是“索引超出了数组的范围。”不管我一直试图在 UIA 服务器端设置什么异常。

不同之处在于我不是通过标准托管 UIAutomationClient 调用 UIA 方法,而是使用我自己的代码一直到 COM 调用(标准托管库不支持我想要的自定义 UIA 模式采用)。标准库可以很好地传递异常消息。我试图追踪其中的区别并发现以下内容:

  • 标准托管库通过定义为 private static extern int RawGridPattern_GetItem(SafePatternHandle hobj, int row, int column, out SafeNodeHandle pResult); 的方法通过 InternallCall here 调用 P/Invoke。它返回 HRESULT,由CheckError 方法通过调用Marshal.ThrowExceptionForHR(hr); 处理。此时会出现带有正确消息的异常,正如在 UIA 服务器端引发的那样。
  • 我使用的 UIAComWrapper 看起来与 c:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\UIAutomationClient.idl 中定义的 COM callHRESULT GetItem ([in] int row, [in] int column, [out, retval] IUIAutomationElement ** element ); 相同。根据我对 COM 互操作的理解,重写返回值机制会自动检查 HRESULT,如有必要则抛出异常,否则返回 out result 参数。除了异常消息由于某种原因没有被翻译之外,它确实如此。

要重现该问题,您可以尝试this project。 lib 文件夹中的文件是从this repository 构建的。如果 ConsoleApplication1 引用 UIAComWrapper 库 - 异常带有默认消息。如果您将引用更改为使用标准 UIAutomationClient - 它会收到自定义的。

【问题讨论】:

  • 您是否检查过 SystemAlert 事件 (UIA_SystemAlertEventId / 20023)? msdn.microsoft.com/en-us/library/windows/desktop/…(仅适用于 Windows 8+,标准 .NET UIAutomation dll 不支持,但您似乎知道的 UIAComWrapper 不支持它:-)
  • @SimonMourier 事件是可能的,但这意味着应该先有人在那里订阅。并且应该在每次调用之前完成以获取此类错误信息。以类似的方式,可以声明返回最后一个错误详细信息的独立 UIA 属性 - 类似于 GetLastError。不是很吸引人的解决方案(但当然可以实施)。
  • 好吧,对于 UIA 来说,Windows 8 SDK 中的 UIAutomationCore.idl 和 UIAutomationClient.idl 中的内容并没有更多。那里的接口不是 IDispatch 接口,因此不是为了携带额外的异常信息(.NET 喜欢的 EXCEPINFO 结构)而设计的。我认为你必须想出一种传统的方法来定义什么是错误(从 UI 角度)以及它们是如何发生的。您还可以使用非常通用的 IAnnotationProvider 或 IObjectModelProvider
  • @SimonMourier 如果我理解正确,UIA 客户端会在调用后查看 HRESULT,如果有适当的异常(例如 ElementNotAvailableException),则将其转换为异常。但我不明白 UIA 服务器端如何在那里设置 HRESULT - 即使在自动化对等中我抛出ElementNotAvailableException - 客户端仍然看到TargetInvocationException。也许我做错了什么,但不知道如何实际调试它。
  • 事实上,我无法重现您的问题。这是一个示例 WPF UserControl,它引发了一个异常,该异常显示在示例控制台应用服务器中:pastebin.com/xH7QmHhe

标签: c# .net wpf ui-automation


【解决方案1】:

默认的 TLB importer - 或等效的 Visual Studio UI 操作 - 创建 Interop.UIAutomationClient 程序集使用“[out, retval]”签名布局,而不是使用 Preservesig 属性(更多关于这里http://blogs.msdn.com/b/adam_nathan/archive/2003/04/30/56646.aspx)。

例如,这里声明IUIAutomationGridPattern 像这样(简化版):

[Guid("414C3CDC-856B-4F5B-8538-3131C6302550"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUIAutomationGridPattern
{
    UIAutomationClient.IUIAutomationElement GetItem(int row, int column);
    ...
}

而不是这个:

[Guid("414C3CDC-856B-4F5B-8538-3131C6302550")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUIAutomationGridPattern
{
    [PreserveSig]
    int GetItem(int row, int column, out UIAutomationClient.IUIAutomationElement element);
    ...
}

虽然两者都有效,但如果你想小心处理异常,后一种更好。第一个做了一些魔法,不幸的是,它把这里有趣的东西变成了不那么有趣的东西。 所以,如果你使用PreserveSig版本,你可以像这样替换GridItem.cs中的代码:

    public AutomationElement GetItem(int row, int column)
    {
        try
        {
            UIAutomationClient.IUIAutomationElement element;
            int hr = _pattern.GetItem(row, column, out element);
            if (hr != 0)
                throw Marshal.GetExceptionForHR(hr); // note this uses COM's EXCEPINFO if any

            return AutomationElement.Wrap(element).GetUpdatedCache(CacheRequest.Current);
        }
        catch (System.Runtime.InteropServices.COMException e)
        {
            Exception newEx; if (Utility.ConvertException(e, out newEx)) { throw newEx; } else { throw; }
        }
    }

您现在应该会看到原始异常。

因此,要修复代码,您必须手动重新定义所有涉及的接口(或者这里有 http://clrinterop.codeplex.com/releases/view/17579 一个更新的 tlbimp,可以使用 PreserveSig 创建签名 - 未测试)。您还必须更改 UIAComWrapper 代码。前面还有很多工作要做。

【讨论】:

  • 所以,没有办法说服 marshaller 为我做这项工作。我希望有一些神奇的属性,因为它经常出现 :) 但是谢谢,如果 PreserveSig 是唯一的方法,我会试一试。正如你所看到的,我已经在做一些原始的 tlbimp 结果重写,所以添加新属性可能不像最初看起来那么痛苦......
  • 具有讽刺意味的是,要修复自定义模式,只需更改三行代码github.com/ivan-danilov/uia-custom-pattern-managed/commit/… 就足够了,但要使常见模式工作相同,则需要大量重写大量代码。 . 谢谢,它有效。标记为正确答案。
猜你喜欢
  • 2018-04-23
  • 2016-12-22
  • 1970-01-01
  • 2020-06-12
  • 1970-01-01
  • 2021-11-03
  • 2017-07-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多