UIAutomation spettacoli API barra dei menu quando viene chiamato da un app, ma non un altro?
-
25-10-2019 - |
Domanda
Ho una domanda che sto cercando di scrivere un test dell'interfaccia utente automatizzato per. Il è un nativo C ++ applicazione ATL, che ha un paio di controlli e una barra dei menu. Un'applicazione client di automazione scritto in C # può vedere la barra dei menu, ma per nessun motivo apparente, un programma equivalente scritto in IronRuby non possono.
Il mio C # console applicazione può enumerare i bambini della finestra principale, e vede la MenuBar ... Ecco il codice
var desktop = AutomationElement.RootElement;
var walker = TreeWalker.RawViewWalker;
var mainWindow = desktop.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "TheWindowName"));
var child = walker.GetFirstChild(mainWindow);
do
{
Console.WriteLine(child.Inspect());
child = walker.GetNextSibling(child);
}
while (child != null);
---- Output ----
<Static Name="view:" AutomationId="4337">
<Static Name="[ALL]" AutomationId="4341">
<Button Name="View" AutomationId="4322">
<AtlAxWinLic100 Name="" AutomationId="1101">
<ATL:msctls_statusbar32 Name="" AutomationId="StatusBar">
< Name="TheWindowName" AutomationId="TitleBar">
< Name="Application" AutomationId="MenuBar">
Tuttavia, quando scrivo il codice equivalente utilizzando IronRuby (v1.1.3), la barra del titolo ei controlli MenuBar non sono elencati!
desktop = AutomationElement.RootElement;
walker = TreeWalker.RawViewWalker;
mainWindow = desktop.FindFirst(TreeScope.Children, PropertyCondition.new(AutomationElement.NameProperty, "TheWindowName".to_clr_string));
child = walker.GetFirstChild(mainWindow);
until child.nil? do
Console.WriteLine(Inspect(child));
child = walker.GetNextSibling(child);
end
---- Output ----
<Static Name="view:" AutomationId="4337">
<Static Name="[ALL]" AutomationId="4341">
<Button Name="View" AutomationId="4322">
<AtlAxWinLic100 Name="" AutomationId="1101">
<ATL:msctls_statusbar32 Name="" AutomationId="6872212">
Come si può vedere, gli elementi con una proprietà ClassName di stringa vuota, non sono sempre visibili (e anche notare l'AutomationId sulla barra di stato è diversa) ... ma perché ????
L'unico codice non mostrato qui è la roba spazio dei nomi utilizzando ...
Tutte le idee cosa potrebbe causare questo? Sia il mio C # app e IronRuby hanno un unico filo conduttore che è STA, e nemmeno chiamare CoInitializeSecurity per quanto posso dire.
PS: La solita risposta a queste domande è quello di installare l'aggiornamento Microsoft UI Automation 3.0 per Windows XP, Vista, Server2003, ecc Sono in esecuzione su Windows 7, e per quanto ne so, non ci sono aggiornamenti UIA per Windows 7
PPS: Ecco il codice per il metodo Inspect, che è lo stesso (abbastanza vicino) sia per ruby ??e C #
public static string Inspect(this AutomationElement element)
{
var className = element.GetCurrentPropertyValue(AutomationElement.ClassNameProperty);
var name = element.GetCurrentPropertyValue(AutomationElement.NameProperty);
var id = element.GetCurrentPropertyValue(AutomationElement.AutomationIdProperty);
return String.Format("<{0} Name=\"{1}\" AutomationId=\"{2}\">", className, name, id);
}
Soluzione
Dopo un presentimento, ho attivato i registri di fusione, e ho notato che il mio C # applicazione stava caricando UIAutomationClientSideProviders.dll
, ma la mia domanda IronRuby non lo ero. spettacoli riflettore che questa DLL contiene un intero gruppo di fornitori per i componenti di Windows, quindi questo sembrava sospetto.
Il passo successivo è stato quello di caricare esplicitamente che dll da IronRuby, che non ha fatto nulla.
Ho poi alzò gli occhi come clientside fornitori di lavoro - è necessario chiamare ClientSettings.RegisterClientSideProviderAssembly
registrare assemblee contenenti fornitori. Quando ho tentato di fare questo, ho ottenuto la seguente eccezione:
UIAutomationClient:0:in `RegisterProxyAssembly': 'UIAutomationClientsideProviders' assembly not found.
(System::Windows::Automation::ProxyAssemblyNotLoadedException)
from UIAutomationClient:0:in `LoadDefaultProxies'
from UIAutomationClient:0:in `RegisterWindowHandlers'
from UIAutomationClient:0:in `RegisterClientSideProviders'
Di nuovo alla riflettore a guardare il codice per LoadDefaultProxies
. Non voglio copiare / incollare il codice, ma ha fondamentalmente questo:
- Usa riflessione di guardare a tutte le assemblee referenziati per l'eseguibile corrente
- Utilizzare questi riferimenti per individuare il
UIAutomationClient
assemblaggio. - carico
UIAutomationClientsideProviders
da stringa-nome, utilizzando la versione, la cultura e la chiave pubblica gettoni copiato dalUIAutomationClient
assemblaggio
Questo spiega perché funziona:
La mia console app ha un esplicito riferimento alla UIAutomationClient
, e così ottiene la piena token di chiave pubblica, ecc, e può caricare i ClientSideProviders DLL dal GAC
IronRuby ha non ha riferimenti espliciti come è tutto dinamica, in modo che solo i carichi utilizzando la stringa di nome senza token di chiave pubblica, ecc, e quindi non può utilizzare il GAC.
Una volta ho scoperto questo, ho copiato UIAutomationClientsideProviders.dll
nella mia directory bin IronRuby (quindi sarebbe nel percorso di carico), e abbastanza sicuro, ha funzionato.
Non c'è bisogno di qualcosa di esplicitamente carico, IronRuby ha bisogno semplicemente di essere in grado di caricare UIAutomationClientsideProviders.dll
senza una chiave informazioni simbolico o versione pubblica, e tutto va bene.
Per evitare di dover copiare le cose intorno, il codice seguente utilizza l'evento AssemblyResolve
di restituire il corretto montaggio
require 'UIAutomationClientSideProviders, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL'
System::AppDomain.current_domain.assembly_resolve do |sender, args|
args.name == "UIAutomationClientsideProviders" ?
UIAutomationClientsideProviders::UIAutomationClientSideProviders.to_clr_type.assembly :
nil
end