UIAutomation API показывает строку меню при вызове из одного приложения, но не из другого? - PullRequest
1 голос
/ 28 апреля 2011

У меня есть приложение, для которого я пытаюсь написать автоматический тест пользовательского интерфейса.Это родное C ++ ATL-приложение, которое имеет несколько элементов управления и панель меню.Клиентское приложение автоматизации, написанное на C #, может видеть строку меню, но без видимой причины, эквивалентное приложение, написанное на IronRuby, не может.

Моё консольное приложение C # может перечислять дочерние элементы главного окна, и оно видит MenuBar... Вот код

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">

Однако, когда я пишу эквивалентный код с использованием IronRuby (v1.1.3), элементы управления TitleBar и MenuBar не отображаются!

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">

Как вы можете видеть, элементы со свойством ClassName пустой строки не отображаются (а также обратите внимание, что AutomationId в строке состояния отличается) ... но почему ????

Единственный код, который не показанздесь используется материал пространства имен ...

Есть идеи, что могло бы вызвать это?И мое приложение на C #, и IronRuby имеют единственный поток, который является STA, и ни один из них не вызывает CoInitializeSecurity, насколько я могу судить.

PS: Обычный ответ на эти вопросы - установить обновление автоматизации MS UI 3.0 для Windows.XP, Vista, server2003 и т. Д. Я работаю на Windows 7, и, насколько я знаю, нет обновлений UIA для Windows 7

PPS: Вот код для метода Inspect, который являетсяодинаково (достаточно близко) как для рубина, так и для 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);
}

1 Ответ

1 голос
/ 29 апреля 2011

После догадки я включил журналы Fusion и заметил, что мое приложение C # загружало UIAutomationClientSideProviders.dll, но мое приложение IronRuby не было.Reflector показывает, что эта DLL содержит целую кучу провайдеров для компонентов Windows, поэтому это выглядело подозрительно.

Мой следующий шаг - явная загрузка этой dll из IronRuby, которая ничего не делала.

Я тогдапосмотрел, как работают провайдеры ClientSide - вам нужно позвонить ClientSettings.RegisterClientSideProviderAssembly, чтобы зарегистрировать сборки, содержащие провайдеров.Когда я попытался сделать это, я получил следующее исключение:

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'

Назад к отражателю, чтобы посмотреть код для LoadDefaultProxies.Я не буду копировать / вставлять код, но он в основном делает это:

  1. Используйте отражение, чтобы просмотреть все Ссылочные сборки для текущего исполняемого файла
  2. Используйте эти ссылки, чтобы найтиUIAutomationClient сборка.
  3. загрузка UIAutomationClientsideProviders по имени строки с использованием токенов версии, культуры и открытого ключа, скопированных из UIAutomationClient сборки

Это объясняет, почему это работает:

В моем консольном приложении есть явная ссылка на UIAutomationClient, и поэтому он получает полный токен открытого ключа и т. Д. И может загрузить DLL-файл ClientSideProviders из GAC

У IronRuby нет явногоссылки, поскольку все они динамические, поэтому он загружается только с использованием строкового имени без маркера открытого ключа и т. д. и поэтому не может использовать GAC.

Как только я обнаружил это, я скопировал UIAutomationClientsideProviders.dll в свойКаталог bin IronRuby (поэтому он будет в пути загрузки), и, конечно же, он работал.

Вам не нужно ничего явно загружать, IronRuby просто должен иметь возможность загружать UIAutomationClientsideProviders.dll безтокен открытого ключа oИнформация о версии r, и все в порядке.


Чтобы избежать необходимости копировать объекты, следующий код использует событие AssemblyResolve для возврата правильной сборки

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
...