如何 return 来自 UI 自动化模式提供程序的错误?

How to return errors from UI Automation pattern provider?

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

如果我从自动化对等端抛出异常 - 在 UIA 客户端,我从 IUIAutomationPatternInstance 对象中得到 TargetInvocationException,没有任何细节(InnerException 属性 为空)。

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


UPD: 经过一些调查并与评论中提供的示例@SimonMourier 进行比较后,我发现 TargetInvocationException 是我的错。已修复 here.

现在我得到了正确的异常类型,但只有标准的异常消息。对于 IndexOutBoundsException 它是 "Index was outside the bounds of the array." 不管我一直试图在 UIA 服务器端放入异常。

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

要重现该问题,您可以尝试 this project. Files in lib folder were built from this repository。如果 ConsoleApplication1 引用 UIAComWrapper 库 - 默认消息附带异常。如果您更改参考以改用标准 UIAutomationClient - 它会收到自定义的。

默认TLB importer - or equivalent Visual Studio UI operations - that creates the Interop.UIAutomationClient assembly uses the "[out, retval]" signature layout instead of using Preservesig attribute (more on this here 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 可以使用 PreserveSig 创建签名的更新的 tlbimp - 未测试)。您还必须更改 UIAComWrapper 代码。前面还有很多工作要做。