我是NordVPN的用户并使用它没有任何问题。现在,对于某些要求,我需要设置一些属性,如协议(复选框)和点击其他应用程序中的按钮。
但该应用程序的该区域看起来像一个自定义控件,UIAutomation无法向下钻取。
该自定义控件中的元素没有任何自动化ID。
所以我需要知道如何使用UIAutomation和White Framework遍历wpf应用程序中的用户控件,如应用程序窗口的其他部分。
到目前为止我尝试过的是
答案 0 :(得分:3)
编辑1 - 澄清: 对我来说,控件在自动化API中是不可见的 - 如果没有能力唯一地识别它们以自动化。识别因子可以是name,id,sibling或parent之类的任何内容。
正如所料,缺少自动化ID导致控件在UI自动化树API中不可见。
为了解决这个问题,并且知道它们在Snoop应用程序中可见 - 您可以使用底层逻辑(Snoop使用)来以编程方式自动化这些控件。
下载binaries for SnoopUI,并将其添加到您的项目中。确保将编译选项保持为'无'并被复制到输出目录。
下一步将添加一个辅助方法,在这种情况下,使用这些二进制文件将自动化逻辑注入到目标应用程序(即NordVPN)中。将dll注入目标进程后,ManagedInjector
也会调用作为参数发送的方法。
public class Helper
{
public static void Inject(IntPtr windowHandle, Assembly assembly, string className, string methodName)
{
var location = Assembly.GetEntryAssembly().Location;
var directory = Path.GetDirectoryName(location);
var file = Path.Combine(directory, "HelperDlls", "ManagedInjectorLauncher" + "64-4.0" + ".exe");
Debug.WriteLine(file + " " + windowHandle + " \"" + assembly.Location + "\" \"" + className + "\" \"" + methodName + "\"");
Process.Start(file, windowHandle + " \"" + assembly.Location + "\" \"" + className + "\" \"" + methodName + "\"");
}
}
在应用程序中注入自动化dll后,使用Visual Tree
和Dispatcher
访问PresentationSources
非常简单。
public class Setup
{
public static bool Start()
{
Dispatcher dispatcher;
if (Application.Current == null)
dispatcher = Dispatcher.CurrentDispatcher;
else
dispatcher = Application.Current.Dispatcher;
dispatcher.Invoke(AutomateApp);
return true;
}
public static void AutomateApp()
{
Window root = null;
foreach (PresentationSource presentationSource in PresentationSource.CurrentSources)
{
root = presentationSource.RootVisual as Window;
if (root == null)
continue;
if ("NordVPN ".Equals(root.Title))
break;
}
访问VisualTree
非常简单,但识别控件并不那么简单,因为没有可以唯一标识这些控件的自动化ID或名称。但幸运的是,当他们使用MVVM时,可以使用附加的绑定来识别它们。
public static T GetChildWithPath<T>(this DependencyObject depObj, DependencyProperty property = null, string pathName = null) where T : DependencyObject
{
T toReturn = null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
bool pathNameMatch = (child is T) && child.IsPathNameMatch<T>(property, pathName);
if (pathNameMatch)
{
toReturn = child as T;
break;
}
else
toReturn = GetChildWithPath<T>(child, property, pathName);
if (toReturn != null)
break;
}
return toReturn;
}
一旦您可以访问控件,现在可以直接操作其属性,或访问相应的自动化对等项,以及提供程序来自动执行这些控件。
var checkBoxNames = new[]
{
"CyberSec", "AutomaticUpdates", "AutoConnect",
"StartOnStartup", "KillSwitch", "ShowNotifications",
"StartMinimized", "ShowServerList", "ShowMap",
"UseCustomDns", "ObfuscatedServersOnly"
};
foreach(var path in checkBoxNames)
{
var chkBox = settingsView.GetChildWithPath<CheckBox>(CheckBox.IsCheckedProperty, path);
if(chkBox != null && chkBox.IsEnabled)
chkBox.SimulateClick();
}
已在Github repository上传完整的工作样本。