如何从UI自动化模式提供程序返回错误
本文关键字:程序 返回 错误 模式 UI 自动化 | 更新日期: 2023-09-27 18:03:40
假设我在自定义控件中实现了一些UIA模式。比如TablePattern
。如果出现任何问题,现有的实现将返回null。但是调试起来不是很方便。我可能在自动化对等体中有更多的上下文。例如,对于GetItem(int row, int column)
,我可能会说提供的参数越界,而不是仅仅返回null。
如果我从自动化对等抛出一个异常-在UIA客户端我得到TargetInvocationException
从IUIAutomationPatternInstance
对象没有任何细节(InnerException属性为null)。
是否有一种方法使UIA通过错误与一些额外的信息从UIA服务器端到UIA客户端?
UPD:经过一些调查和对比@SimonMourier在评论中提供的例子,我发现TargetInvocationException
是我的错。
现在我得到了正确的异常类型,但只有一个标准的异常消息。对于IndexOutBoundsException
,它是"索引在数组的边界之外",无论我一直试图在UIA服务器端放入异常。
不同之处在于,我试图调用UIA方法不是通过标准管理的UIAutomationClient,而是用我自己的代码一直到COM调用(标准管理的库不支持我想使用的自定义UIA模式)。标准库很好地传递异常消息。我试图跟踪差异是什么,发现如下:
- 标准托管库在这里通过定义为
private static extern int RawGridPattern_GetItem(SafePatternHandle hobj, int row, int column, out SafeNodeHandle pResult);
的方法通过InternallCall调用P/Invoke。它返回HRESULT,由CheckError
方法通过调用Marshal.ThrowExceptionForHR(hr);
来处理。在这一点上,错误的消息出现在UIA服务器端抛出。 - 我使用的UIAComWrapper在
c:'Program Files (x86)'Microsoft SDKs'Windows'v7.1A'Include'UIAutomationClient.idl
中定义了与HRESULT GetItem ([in] int row, [in] int column, [out, retval] IUIAutomationElement ** element );
相同的COM调用。根据我对COM互操作的理解,重写返回值机制会自动检查HRESULT,必要时抛出异常,否则返回out result
参数。它确实可以,除了异常消息由于某种原因没有被翻译。
要重现该问题,您可以尝试此项目。lib文件夹中的文件是从这个存储库构建的。如果ConsoleApplication1引用了UIAComWrapper库-异常伴随着默认消息。如果你改变引用使用标准的UIAutomationClient代替-它接收自定义的
创建Interop.UIAutomationClient
程序集的默认TLB导入器-或等效的Visual Studio UI操作-使用" [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代码。还有很多工作要做