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

时间:2011-04-28 04:36:01

标签: ironruby ui-automation menubar

我有一个应用程序我正在尝试编写自动UI测试。 这是一个本机C ++ ATL应用程序,它有几个控件和一个MenuBar。用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不同)...但为什么????

此处未显示的唯一代码是using namespace stuff ...

任何想法会导致什么?我的C#app和IronRuby都有一个单独的线程,它是STA,就我所知,它都没有调用CoInitializeSecurity。

PS:对这些问题的通常回答是为Windows XP,Vista,server2003等安装MS UI自动化3.0更新。我在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 个答案:

答案 0 :(得分:1)

预感后,我启用了Fusion日志,并注意到我的C#应用​​程序正在加载UIAutomationClientSideProviders.dll,但我的IronRuby应用程序却没有。反射器显示此DLL包含一大堆Windows组件提供程序,因此看起来很可疑。

我的下一步是从IronRuby显式加载该DLL,它没有做任何事情。

然后我查看了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. 使用反射查看当前可执行文件的所有Referenced程序集
  2. 使用这些引用找到UIAutomationClient程序集。
  3. 使用从UIAutomationClientsideProviders程序集
  4. 复制的版本,区域性和公钥标记,按字符串名称加载UIAutomationClient

    这解释了它的工作原理:

    我的控制台应用程序明确引用UIAutomationClient,因此获取完整的公钥令牌等,并可以从GAC加载ClientSideProviders dll

    IronRuby没有明确的引用,因为它都是动态的,因此它只使用没有公钥令牌等的字符串名称加载,因此无法使用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