UIAutomation API显示菜单栏时,从一个应用程序调用,而不是另一个



我有一个应用程序,我试图写一个自动化的UI测试。这是一个本地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是不同的)…但是为什么? ?

这里唯一没有显示的代码是using namespace的东西…

你知道是什么原因导致的吗?据我所知,我的c#应用程序和IronRuby都有一个STA线程,并且都没有调用CoInitializeSecurity。

PS:对于这些问题,通常的回答是为windows XP、Vista、server2003等安装MS UI自动化3.0更新。我在windows 7上运行,据我所知,windows 7没有UIA更新

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

直觉之后,我启用了融合日志,并注意到我的c#应用程序正在加载UIAutomationClientSideProviders.dll,但我的IronRuby应用程序没有。Reflector显示这个DLL包含了一大堆Windows组件的提供程序,所以这看起来很可疑。

我的下一步是显式地从IronRuby加载dll,它什么也没做。

然后我查看了客户端提供程序如何工作-您需要调用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的显式引用,因此获得完整的公钥令牌等,并且可以从GAC

加载ClientSideProviders dll

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

相关内容

  • 没有找到相关文章