2015-07-20 5 views
16

Angenommen, ich implementiere ein UIA-Muster in meinem benutzerdefinierten Steuerelement. Sag, TablePattern. Vorhandene Implementierungen geben NULL zurück, wenn etwas schief gelaufen ist. Aber es ist nicht sehr bequem zu debuggen. Ich könnte mehr Kontext im Automatisierungspeer haben. Zum Beispiel könnte ich für GetItem(int row, int column) sagen, dass die bereitgestellten Argumente außerhalb der Grenzen liegen und nicht nur null zurückgeben.Wie können Fehler vom UI Automation-Musteranbieter zurückgegeben werden?

Wenn ich eine Ausnahme von Automatisierung Peer werfen - auf der UIA-Client-Seite bekomme ich TargetInvocationException von IUIAutomationPatternInstance Objekt ohne Details (InnerException-Eigenschaft ist null).

Gibt es eine Möglichkeit, UIA Fehler mit einigen zusätzlichen Informationen von UIA-Server-Seite zu UIA-Client-Seite übergeben?


UPD: Nach einigen Untersuchungen und Vergleich mit Beispiel @SimonMourier in Kommentaren versehen Ich fand, dass TargetInvocationException meine Schuld war. Es wurde behoben here.

Jetzt bekomme ich korrekte Ausnahme-Typ, aber nur eine Standard-Ausnahme-Nachricht. Für IndexOutBoundsException ist es "Index war außerhalb der Grenzen des Arrays." unabhängig davon, was ich versucht habe, eine Ausnahme auf der UIA-Serverseite zu machen.

Der Unterschied ist, dass ich versuche, UIA-Methode nicht über Standard verwaltet UIAutomationClient, sondern mit meinem eigenen Code bis hin zu COM-Aufruf (Standard verwaltete Bibliothek unterstützt benutzerdefinierte UIA-Muster, die ich möchte benutzen). Die Standardbibliothek übergibt Exceptions-Nachrichten einfach. Ich habe versucht, zu verfolgen, was der Unterschied ist, und fanden die folgende:

  • Standard-verwalteten Bibliothek Aufruf P macht/Invoke durch InternallCall here über Verfahren wie private static extern int RawGridPattern_GetItem(SafePatternHandle hobj, int row, int column, out SafeNodeHandle pResult); definiert. Es gibt HRESULT zurück, das von CheckError Methode über Aufruf an Marshal.ThrowExceptionForHR(hr); behandelt wird. An dieser Stelle erscheint die Ausnahme mit der korrekten Nachricht, die auf der Seite des UIA-Servers geworfen wurde.
  • UIAComWrapper, die ich benutze scheinbar gleich definiert in c:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\UIAutomationClient.idl als HRESULT GetItem ([in] int row, [in] int column, [out, retval] IUIAutomationElement ** element);. Nach meinem Verständnis von COM Interop überprüft der Mechanismus zum Umschreiben des Rückgabewerts automatisch HRESULT, löst bei Bedarf eine Ausnahme aus und gibt ansonsten das Argument out result zurück. Es tut wirklich, außer dass Ausnahmemeldung aus irgendeinem Grund nicht übersetzt wird.

Um das Problem zu reproduzieren, können Sie versuchen this project. Dateien im lib-Ordner wurden von this repository erstellt. Wenn ConsoleApplication1 auf die UIAComWrapper-Bibliothek verweist, wird die Standardnachricht ausgegeben. Wenn Sie stattdessen den Verweis auf den Standard-UIAutomationClient ändern, wird dieser benutzerdefiniert.

+0

Haben Sie das SystemAlert-Ereignis (UIA_SystemAlertEventId/20023) überprüft? https://msdn.microsoft.com/en-us/library/windows/desktop/ee671223(v=vs.85).aspx (nur für Windows 8+, wird nicht von den Standard-.NET UIAutomation dlls unterstützt, sondern von der UIAComWrapper, das Sie zu kennen scheinen: –

+0

@SimonMourier Ereignisse sind möglich, aber es würde bedeuten, dass jemand zuerst dort abonniert haben sollte. Und es sollte vor jedem Anruf gemacht werden, um solche Fehlerinformationen zu erhalten. In ähnlicher Weise könnte man eine eigenständige UIA-Eigenschaft deklarieren, die Details des letzten Fehlers zurückgibt - etwa "GetLastError". Nicht sehr ansprechende Lösung (aber natürlich umsetzbar). –

+0

Nun, es gibt nichts mehr zu UIA, was in UIAutomationCore.idl und UIAutomationClient.idl vom Windows 8 SDK ist. Da Schnittstellen keine IDispatch-Schnittstellen sind, sind sie nicht dafür ausgelegt, zusätzliche Ausnahmeinformationen (EXCEPINFO-Struktur, die .NET mag) zu tragen. Ich denke, dass Sie eine konventionelle Methode finden müssen, um Fehler zu definieren (aus einer UI-Perspektive) und wie sie passieren. Sie könnten auch IAnnotationProvider oder IObjectModelProvider verwenden, die ziemlich generisch sind –

Antwort

3

Der Standard TLB importer - oder gleichwertige Visual Studio UI-Operationen - das ist die Interop.UIAutomationClient Montag schafft verwendet das „[out, retval]“ Signatur Layout anstelle von Preservesig Attribute (mehr dazu hier http://blogs.msdn.com/b/adam_nathan/archive/2003/04/30/56646.aspx).

So zum Beispiel, hier erklärt es IUIAutomationGridPattern wie folgt aus (vereinfachte Version):

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

statt dessen:

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

Obwohl beide gültig sind, letztere ist besser, wenn Sie wollen Ausnahmen sorgfältig behandeln. Der erste macht eine Magie, die leider das Interessante in etwas weniger Interessantes verwandelt. Also, wenn Sie die PreserveSig Version verwenden, können Sie den Code in GridItem.cs wie folgt ersetzen:

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; } 
     } 
    } 

Und Sie sollten jetzt ursprünglichen Ausnahmen sehen.

Um den Code zu beheben, müssen Sie alle beteiligten Interfaces manuell neu definieren (oder es gibt hier http://clrinterop.codeplex.com/releases/view/17579 einen neueren tlbimp, der Signaturen mit PreserveSig erstellen kann - nicht getestet). Sie müssen den UIAComWrapper-Code auch ändern. Ziemlich viel Arbeit voraus.

+0

So gibt es keine Möglichkeit, Marshaller zu überzeugen, diese Arbeit für mich zu tun. Ich hatte auf magische Attribute gehofft als es ist oft der Fall :) Aber danke, wenn PreserveSig der einzige Weg ist, werde ich es versuchen. Wie Sie sehen können, mache ich schon ein bisschen das ursprüngliche tlbimp result writing, so dass das Hinzufügen eines neuen Attributs nicht so schmerzhaft ist, wie es zuerst aussieht ... –

+0

Es gibt etwas Ironie in der Tatsache, dass das Reparieren von benutzerdefinierten Mustern genug war Ändern Sie drei Zeilen Code https://github.com/ivan-danilov/uia-custom-pattern-managed/commit/69226a819a9e316f6f608457891a4641ef5f32e6, aber um gemeinsame Muster funktionieren das gleiche würde es erhebliche Neuschreiben von viel Code erfordern ... Danke , Es klappt. Kennzeichnung als richtige Antwort. –

Verwandte Themen