C# 如何从UI自动化模式提供程序返回错误?
假设我正在自定义控件中实现一些UIA模式。比如说,C# 如何从UI自动化模式提供程序返回错误?,c#,.net,wpf,ui-automation,C#,.net,Wpf,Ui Automation,假设我正在自定义控件中实现一些UIA模式。比如说,TablePattern。如果出现任何错误,现有实现将返回null。但是调试起来不是很方便。我可能在自动化领域有更多的背景知识。例如,对于GetItem(int行,int列)我可能会说提供的参数超出了范围,而不仅仅是返回null 如果我从automation peer引发异常-在UIA客户端,我从IUIAutomationPatternInstance对象中获得targetingException,但没有任何详细信息(InnerException
TablePattern
。如果出现任何错误,现有实现将返回null。但是调试起来不是很方便。我可能在自动化领域有更多的背景知识。例如,对于GetItem(int行,int列)
我可能会说提供的参数超出了范围,而不仅仅是返回null
如果我从automation peer引发异常-在UIA客户端,我从IUIAutomationPatternInstance
对象中获得targetingException
,但没有任何详细信息(InnerException属性为null)
有没有一种方法可以让UIA通过一些附加信息将错误从UIA服务器端传递到UIA客户端
UPD:经过调查并与评论中提供的@SimonMourier示例进行比较后,我发现
targetingException
是我的错。修好了
现在我得到了正确的异常类型,但只有一条标准的异常消息。对于IndexOutBoundsException
来说,它是“索引超出了数组的边界”。不管我一直试图在UIA服务器端设置什么异常
不同之处在于,我尝试调用UIA方法不是通过标准托管UIAutomationClient,而是通过我自己的代码,一直到COM调用(标准托管库不支持我想要使用的自定义UIA模式)。标准库可以很好地传递异常消息。我试着追踪差异,发现了以下几点:
- 标准托管库通过定义为
private static extern int RawGridPattern_GetItem(SafePatternHandle hobj、int行、int列、out SafeNodeHandle pResult)的方法通过内部调用调用P/Invoke代码>。它返回HRESULT,由
方法通过调用CheckError
Marshal.throweExceptionForHR(hr)来处理代码>。此时,带有正确消息的异常显示为UIA服务器端抛出的异常
- 我使用的UIAComWrapper与
asc:\ProgramFiles(x86)\Microsoft SDK\Windows\v7.1A\Include\UIAutomationClient.idl
HRESULT GetItem([in]int行,[in]int列,[out,retval]IUIAutomationeElement**元素)中定义的似乎相同代码>。据我对COM互操作的理解,重写返回值机制会自动检查HRESULT,必要时抛出异常,否则返回
参数。它确实是这样的,只是由于某种原因,异常消息没有被翻译out结果
[out,retval]
”签名布局,而不是使用Preservesig
属性(这里有更多信息)
例如,这里它声明了IUIAutomationGridPattern
,如下所示(简化版):
与此相反:
[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; }
}
}
现在您应该看到原始的异常
因此,要修复代码,您必须手动重新定义所有涉及的接口(或者这里有一个更新的tlbimp,它可以使用PreserveSig创建签名-未测试)。您还必须更改UIAComWrapper代码。前面有很多工作。您检查过SystemAlert事件(UIA_SystemAlertEventId/20023)吗?(仅适用于Windows 8+,标准的.NET UIAutomation DLL不支持它,但您似乎知道的UIAComWrapper支持它:-)@SimonMourier事件是可能的,但这意味着应该先有人订阅。并且应该在每次调用之前完成,以获取此类错误信息。类似地,可以声明独立的UIA属性,返回上一个错误的详细信息—类似于
GetLastError
。这不是很吸引人的解决方案(当然可以实现)。好吧,除了windows 8 SDK中的UIAutomationCore.idl和UIAutomationClient.idl中的内容之外,UIA没有更多内容。这些接口没有IDispatch接口,因此设计时不会携带额外的异常信息(除了.NET喜欢的结构).我认为你必须想出一个常规的方法来定义什么是错误(从UI的角度)以及它们是如何发生的。你也可以使用IAnnotationProvider或IObjectModelProvider,它们都很漂亮generic@SimonMourier如果我理解正确,UIA客户端会在调用后查看HRESULT,如果存在适当的异常(例如ElementNotAvailableException
).但我不明白UIA服务器端如何在那里设置HRESULT-即使在自动化对等中我抛出ElementNotAvailableException
-客户端仍然看到TargetingException
。也许我做错了什么,但实际上不知道如何调试它。事实上,我无法重现您的问题。这是一个示例WPFUserControl引发了一个异常,该异常显示在示例控制台应用程序服务器中:因此,无法说服marshaller为我做这项工作。我希望得到一些神奇的特性,就像通常的情况一样:)但是谢谢,如果PreserveSig是唯一的方法,我会尝试一下。正如你所看到的,我已经在做一些原始的tlbimp结果重写了,所以添加新属性可能不像一开始看起来那么痛苦…有很多
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; }
}
}