#ironruby #ui-automation #menubar
#ironruby #пользовательский интерфейс-автоматизация #панель меню
Вопрос:
У меня есть приложение, для которого я пытаюсь написать автоматический тест пользовательского интерфейса. Это родное приложение на C ATL, в котором есть пара элементов управления и строка меню. Клиентское приложение автоматизации, написанное на C #, может видеть строку меню, но без видимой причины эквивалентное приложение, написанное на IronRuby, не может.
Мое консольное приложение C # может перечислять дочерние элементы главного окна, и оно видит строку меню… Вот код
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), строка заголовка и элементы управления меню отсутствуют в списке!
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 automation 3.0 для Windows XP, Vista, server2003 и т.д. Я работаю в Windows 7, и, насколько я знаю, обновлений UIA для Windows 7 нет
PPS: Вот код для метода Inspect, который одинаков (достаточно близок) как для ruby, так и для 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:
После догадки я включил журналы fusion logs и заметил, что мое приложение C # загружалось UIAutomationClientSideProviders.dll
, но мое приложение IronRuby — нет. Reflector показывает, что эта DLL содержит целую кучу поставщиков компонентов Windows, так что это выглядело подозрительно.
Моим следующим шагом было явно загрузить эту dll из IronRuby, что ничего не дало.
Затем я посмотрел, как работают поставщики на стороне клиента — вам нужно позвонить 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'
Вернемся к reflector, чтобы посмотреть код для LoadDefaultProxies
. Я не буду копировать / вставлять код, но в основном он делает это:
- Используйте отражение, чтобы просмотреть все сборки, на которые ссылаются, для текущего исполняемого файла
- Используйте эти ссылки, чтобы найти
UIAutomationClient
сборку. - загружайте
UIAutomationClientsideProviders
по имени строки, используя токены версии, культуры и открытого ключа, скопированные изUIAutomationClient
сборки
Это объясняет, почему это работает:
Мое консольное приложение имеет явную ссылку на UIAutomationClient
и поэтому получает полный токен открытого ключа и т.д. И может загружать ClientSideProviders dll из GAC
У IronRuby нет явных ссылок, поскольку все это динамично, поэтому он загружается только с использованием string-name без токена открытого ключа и т.д., И поэтому не может использовать GAC.
Как только я обнаружил это, я скопировал UIAutomationClientsideProviders.dll
в свой каталог IronRuby bin (чтобы он был в пути загрузки), и, конечно же, это сработало.
Вам не нужно ничего явно загружать, IronRuby просто должен иметь возможность загружаться UIAutomationClientsideProviders.dll
без токена открытого ключа или информации о версии, и все в порядке.
Чтобы избежать необходимости копировать что-либо, следующий код использует 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