c# - ¿Cómo devolver errores del proveedor de patrones de UI Automation?
.net wpf (1)
El importador de TLB predeterminado, o las operaciones equivalentes de Visual Studio UI, que crean el ensamblaje Interop.UIAutomationClient
usa el diseño de firma " [out, retval]
" en lugar de usar el atributo Preservesig
(más información aquí http://blogs.msdn.com/b/adam_nathan/archive/2003/04/30/56646.aspx ).
Así, por ejemplo, aquí declara IUIAutomationGridPattern
como esto (versión simplificada):
[Guid("414C3CDC-856B-4F5B-8538-3131C6302550"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUIAutomationGridPattern
{
UIAutomationClient.IUIAutomationElement GetItem(int row, int column);
...
}
en lugar de esto:
[Guid("414C3CDC-856B-4F5B-8538-3131C6302550")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IUIAutomationGridPattern
{
[PreserveSig]
int GetItem(int row, int column, out UIAutomationClient.IUIAutomationElement element);
...
}
Aunque ambos son válidos, este último es mejor si desea manejar con cuidado las excepciones. El primero hace algo de magia que desafortunadamente transforma lo que es interesante aquí en algo menos interesante. Entonces, si usa la versión PreserveSig
, puede reemplazar el código en GridItem.cs de esta manera:
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; }
}
}
Y ahora deberías ver excepciones originales.
Entonces, para corregir el código, tendrá que redefinir todas las interfaces involucradas, manualmente (o aquí hay http://clrinterop.codeplex.com/releases/view/17579 un tlbimp más nuevo que puede crear firmas con PreserveSig, no probado) . Tendrá que cambiar el código UIAComWrapper también. Mucho trabajo por delante.
Supongamos que estoy implementando algún patrón UIA en mi control personalizado. Diga, TablePattern
. Las implementaciones existentes devuelven nulo si algo sale mal. Pero no es muy conveniente depurar. Podría tener más de un contexto en el par de automatización. Por ejemplo, para GetItem(int row, int column)
puedo decir que los argumentos proporcionados están fuera de los límites en lugar de simplemente devolver el valor nulo.
Si lanzo una excepción desde el par de automatización: en el lado del cliente UIA, obtengo TargetInvocationException
del objeto IUIAutomationPatternInstance
sin ningún detalle (la propiedad InnerException es nula).
¿Hay alguna manera de hacer que UIA pase un error con información adicional del lado del servidor UIA al lado del cliente UIA?
UPD: Después de algunas investigaciones y comparaciones con el ejemplo que @SimonMourier proporcionó en los comentarios, descubrí que la TargetInvocationException
era mi culpa. Lo arreglé here .
Ahora recibo el tipo de excepción correcto, pero solo un mensaje de excepción estándar. Para IndexOutBoundsException
es "El índice estaba fuera de los límites de la matriz". independientemente de lo que he estado tratando de poner en excepción en el lado del servidor UIA.
La diferencia es que estoy tratando de llamar al método UIA no a través del UIAutomationClient administrado estándar, sino con mi propio código hasta la llamada COM (la biblioteca administrada estándar no admite patrones UIA personalizados que me gustaría usar). La biblioteca estándar pasa los mensajes de excepción muy bien. Intenté rastrear cuál es la diferencia y encontré lo siguiente:
- La biblioteca administrada estándar realiza llamadas a P / Invoke a través de InternallCall here través de un método definido como
private static extern int RawGridPattern_GetItem(SafePatternHandle hobj, int row, int column, out SafeNodeHandle pResult);
. Devuelve HRESULT, que se maneja mediante el métodoCheckError
mediante una llamada aMarshal.ThrowExceptionForHR(hr);
. En este punto, aparece una excepción con el mensaje correcto tal como se lanzó en el lado del servidor UIA. - UIAComWrapper que utilizo hace aparentemente la misma llamada COM definida en
c:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/Include/UIAutomationClient.idl
comoHRESULT GetItem ([in] int row, [in] int column, [out, retval] IUIAutomationElement ** element );
. A mi entender de la interoperabilidad COM, el mecanismo de valor de retorno de reescritura verifica automáticamente HRESULT, lanza una excepción si es necesario y devuelveout result
argumento delout result
contrario. Realmente lo hace, excepto que el mensaje de excepción no se traduce por alguna razón.
Para reproducir el problema puedes probar este proyecto . Los archivos en la carpeta lib se crearon desde este repositorio . Si ConsoleApplication1 hace referencia a la biblioteca UIAComWrapper, la excepción viene con el mensaje predeterminado. Si cambia la referencia para usar el UIAutomationClient estándar en su lugar, recibirá una personalizada.