UIAutomation API 在从一个应用程序调用时显示菜单栏,但在另一个应用程序中不显示?
我有一个应用程序,我正在尝试为其编写自动化 UI 测试。 这是一个本机 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 不同)...但是为什么???
这里唯一没有显示的代码是 using 命名空间的东西...
有什么想法会导致这个吗?我的 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);
}
I have an application I'm trying to write an automated UI test for.
The is a native C++ ATL application, which has a couple of controls, and a MenuBar. An automation client application written in C# can see the menu bar, but for no apparent reason, an equivalent application written in IronRuby cannot.
My C# console app can enumerate children of the main window, and it sees the MenuBar... Here's the code
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">
However, when I write the equivalent code using IronRuby (v1.1.3), the TitleBar and the MenuBar controls are not listed!
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">
As you can see, items with a ClassName property of empty string aren't getting shown (and also note the AutomationId on the statusbar is different)... but why????
The only code not shown here is the the using namespace stuff...
Any ideas what would cause this? Both my C# app and IronRuby have a single thread which is STA, and neither call CoInitializeSecurity as far as I can tell.
PS: The usual response to these questions is to install the MS UI automation 3.0 update for windows XP, Vista, server2003, etc. I'm running on windows 7, and as far as I know, there are no UIA updates for windows 7
PPS: Here's the code for the Inspect method, which is the same (close enough) for both ruby and 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);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
预感之后,我启用了融合日志,并注意到我的 C# 应用程序正在加载
UIAutomationClientSideProviders.dll
,但我的 IronRuby 应用程序却没有。 Reflector 显示该 DLL 包含大量 Windows 组件的提供程序,因此这看起来很可疑。我的下一步是从 IronRuby 显式加载该 dll,但它什么也没做。
然后,我查看了 ClientSide 提供程序的工作原理 - 您需要调用 ClientSettings.RegisterClientSideProviderAssembly 来注册包含提供程序的程序集。当我尝试执行此操作时,出现以下异常:
返回反射器查看
LoadDefaultProxies
的代码。我不会复制/粘贴代码,但它基本上是这样做的:UIAutomationClient
程序集中复制的版本、区域性和公钥令牌,按字符串名称加载UIAutomationClientsideProviders
这解释了它的工作原理:
我的控制台应用程序有对
UIAutomationClientsideProviders
的显式引用code>UIAutomationClient,因此获取完整的公钥令牌等,并且可以从 GAC 加载 ClientSideProviders dll。IronRuby 没有显式引用,因为它都是动态的,因此它只能使用string-name 没有公钥令牌等,因此无法使用 GAC。
发现这一点后,我将 UIAutomationClientsideProviders.dll 复制到我的 IronRuby bin 目录中(因此它将位于加载路径中),果然,它起作用了。
您不需要显式加载任何内容,IronRuby 只需能够加载
UIAutomationClientsideProviders.dll
而无需公钥令牌或版本信息,一切都很好。为了避免复制内容,以下代码使用
AssemblyResolve
事件返回正确的程序集After a hunch, I enabled fusion logs, and noticed that my C# application was loading
UIAutomationClientSideProviders.dll
, but my IronRuby application was not. Reflector shows that this DLL contains a whole bunch of providers for Windows components, so this looked suspicious.My next step was to explicitly load that dll from IronRuby, which did nothing.
I then looked up how ClientSide providers work - you need to call
ClientSettings.RegisterClientSideProviderAssembly
to register assemblies containing providers. When I attempted to do this, I got the following exception:Back to reflector to look at the code for
LoadDefaultProxies
. I won't copy/paste the code, but it basically does this:UIAutomationClient
assembly.UIAutomationClientsideProviders
by string-name, using the version, culture and public key tokens copied from theUIAutomationClient
assemblyThis explains why it works:
My console app has an explicit reference to
UIAutomationClient
, and so gets the full public key token, etc, and can load the ClientSideProviders dll from the GACIronRuby has has no explicit references as it's all dynamic, so it only loads using the string-name with no public key token, etc, and so can't use the GAC.
Once I discovered this, I copied
UIAutomationClientsideProviders.dll
into my IronRuby bin directory (so it would be in the load path), and sure enough, it worked.You don't need to explicitly load anything, IronRuby simply needs to be able to load
UIAutomationClientsideProviders.dll
without a public key token or version info, and all is well.To avoid having to copy things around, the following code uses the
AssemblyResolve
event to return the correct assembly