2015-07-20 8 views
16

Supponiamo che sto implementando alcuni pattern UIA nel mio controllo personalizzato. Dì, TablePattern. Le implementazioni esistenti restituiscono null se qualcosa è andato storto. Ma non è molto comodo eseguire il debug. Potrei avere più di un contesto nel peer di automazione. Ad esempio, per GetItem(int row, int column) potrei dire che gli argomenti forniti sono fuori limite anziché solo restituire null.Come restituire gli errori dal provider del pattern di automazione dell'interfaccia utente?

Se lancio un'eccezione dall'automazione peer - sul lato client UIA ottengo TargetInvocationException dall'oggetto IUIAutomationPatternInstance senza alcun dettaglio (la proprietà InnerException è null).

C'è un modo per far sì che l'UIA trasmetta l'errore con alcune informazioni aggiuntive dal lato UIA-server al lato client UIA?


UPD: Dopo alcune indagini e confronto con l'esempio @SimonMourier fornite nei commenti ho scoperto che TargetInvocationException stata colpa mia. Risolto il problema here.

Ora sto ricevendo il tipo di eccezione corretto, ma solo un messaggio di eccezione standard. Per IndexOutBoundsException è "L'indice era fuori dai limiti dell'array." indipendentemente da ciò che ho cercato di mettere in eccezione sul lato server UIA.

La differenza è che sto provando a chiamare il metodo UIA non tramite UIAutomationClient gestito standard, ma con il mio codice fino alla chiamata COM (la libreria gestita standard non supporta i modelli UIA personalizzati che vorrei usare). La libreria standard trasmette messaggi di eccezione in modo corretto. Ho cercato di tenere traccia di quale sia la differenza e trovato il seguente:

  • standard gestito biblioteca rende chiamata a P/Invoke attraverso InternallCall here tramite metodo definito come private static extern int RawGridPattern_GetItem(SafePatternHandle hobj, int row, int column, out SafeNodeHandle pResult);. Restituisce HRESULT, che viene gestito dal metodo CheckError tramite chiamata allo Marshal.ThrowExceptionForHR(hr);. A questo punto appare un'eccezione con il messaggio corretto così come è stato lanciato sul lato server UIA.
  • UIAComWrapper che utilizzo sembra identico a COM call definito in c:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include\UIAutomationClient.idl come HRESULT GetItem ([in] int row, [in] int column, [out, retval] IUIAutomationElement ** element);. Per quanto riguarda l'interoperabilità COM, il meccanismo del valore di ritorno della riscrittura controlla automaticamente HRESULT, genera un'eccezione se necessario e restituisce l'argomento out result altrimenti. Lo fa davvero eccetto che il messaggio di eccezione non viene tradotto per qualche motivo.

Per riprodurre il problema, provare this project. I file nella cartella lib sono stati creati da this repository. Se ConsoleApplication1 fa riferimento alla libreria UIAComWrapper, l'eccezione viene fornita con il messaggio predefinito. Se si modifica il riferimento per utilizzare lo standard UIAutomationClient, esso ne riceve uno personalizzato.

+0

Avete controllato l'evento SystemAlert (UIA_SystemAlertEventId/20023)? https://msdn.microsoft.com/en-us/library/windows/desktop/ee671223(v=vs.85).aspx (solo per Windows 8+, non è supportato dalle DLL standard di UIAutomation .NET ma dal UIAComWrapper che sembra sapere :-) –

+0

@SimonMourier eventi sono possibili, ma significherebbe che qualcuno dovrebbe essersi iscritto prima lì. E dovrebbe essere fatto prima di ogni chiamata per ottenere tali informazioni di errore. In modo simile, si potrebbe dichiarare la proprietà UIA standalone che restituisce i dettagli dell'ultimo errore - sarà qualcosa come "GetLastError". Soluzione non molto allettante (ma possibile da implementare ovviamente). –

+0

Bene, non c'è altro da UIA in ciò che è in UIAutomationCore.idl e UIAutomationClient.idl dall'SDK di windows 8. Le interfacce non sono interfacce IDispatch, quindi non sono progettate per trasportare informazioni di eccezione extra (struttura EXCEPINFO che piace a .NET). Penso che dovrai escogitare un modo convenzionale per definire quali sono gli errori (dal punto di vista dell'interfaccia utente) e come si verificano. Si potrebbe anche usare IAnnotationProvider o IObjectModelProvider che sono piuttosto generica –

risposta

3

L'impostazione predefinita TLB importer - o operazioni equivalenti di Visual Studio UI - che crea il gruppo Interop.UIAutomationClient utilizza il "[out, retval]" layout di firma invece di utilizzare Preservesig attributo (più su questo qui http://blogs.msdn.com/b/adam_nathan/archive/2003/04/30/56646.aspx).

Così, per esempio, qui si dichiara IUIAutomationGridPattern come questo (versione semplificata):

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

invece di questo:

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

Anche se entrambi sono validi, il secondo è migliore se si vuole gestire con cura le eccezioni. Il primo fa un po 'di magia che purtroppo trasforma ciò che è interessante qui in qualcosa di meno interessante. Quindi, se si utilizza la versione PreserveSig, è possibile sostituire il codice in GridItem.cs come questo:

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

E ora si dovrebbe vedere eccezioni originali.

Quindi per correggere il codice, è necessario ridefinire tutte le interfacce coinvolte, manualmente (oppure è disponibile qui http://clrinterop.codeplex.com/releases/view/17579 un nuovo tlbimp in grado di creare firme con PreserveSig - non testato). Dovrai cambiare anche il codice UIAComWrapper. Un sacco di lavoro da fare.

+0

Quindi, non c'è modo di convincere marshaller a fare questo lavoro per me. Speravo in qualche attributo magico come è spesso il caso :) Ma grazie, se PreserveSig è l'unico modo in cui farò un tentativo. Come potete vedere sto facendo un po 'di originale riscrittura risultato tlbimp già, quindi l'aggiunta di nuovo attributo potrebbe non essere così doloroso come sembra in un primo momento ... –

+0

C'è una certa ironia nel fatto che per risolvere i modelli personalizzati è stato sufficiente di cambiare tre righe di codice https://github.com/ivan-danilov/uia-custom-pattern-managed/commit/69226a819a9e316f6f608457891a4641ef5f32e6 ma per rendere i modelli comuni funzionano allo stesso modo sarebbe necessario significativa riscrittura del codice molto ... Grazie , Funziona. Contrassegnare come risposta corretta. –