在使用UI Automation对Winform和WPF的程序测试中发现有一些不同的地方,而这些不同来自于Winform与WPF的处理机制不同。下面我们通过一个简单的实例来加以说明:
实例描述
我们使用InvokePattern来点击按钮弹出一个对话框,然后点击对话框中的“确定”按钮关闭对话框。
两种方式对比
首先我们使用如下代码来针对Winfom和WPF分别进行测试:
Code
1public static void ClickButtonById(int processId, string buttonId)
2{
3 AutomationElement element = FindElementById(processId, buttonId);
4 if (element == null)
5 {
6 throw new NullReferenceException(string.Format("Element with AutomationId '{0}' can not be find.", element.Current.Name));
7 }
8 InvokePattern currentPattern = GetInvokePattern(element);
9 currentPattern.Invoke();
10}
上面的代码主要是用来点击按钮,我们的目的是点击按钮弹出MessageBox,然后点击MessageBox中的“OK”按钮关闭此对话框。
通过测试结果发现,上面的代码在WPF程序中完全可以通过,但是在Winform程序中,点击按钮弹出对话框之后发生阻塞现象,导致程序无法向下执行,所以我们通过如上代码视图点击MessageBox中的按钮来关闭此MessageBox将不可能实现,原因就在于Winform中的MessageBox弹出后就会出现阻塞现象,而WPF中使用了另一种处理方式(对此笔者解释的不够深刻,欢迎广大高手帮忙指正, 另外,此问题在Windows 7操作系统上面不会呈现,也可能与操作系统中API对UI Automation的支持有关)。
解决方案
那么我们通过什么方式来解决此问题呢?很多人会想到多线程,但是我们也可以通过发送异步消息来达到相应的效果。下面我们就通过多线程和发送异步消息的方式来方式来点击Winform中MessageBox中的“OK”按钮,此方法同样可以点击WPF中MessageBox中的按钮达到关闭对话框的效果。
1. 多线程的方式
Code
1public static void ClickButtonById(int processId, string buttonId)
2{
3 AutomationElement element = FindElementById(processId, buttonId);
4
5 if (element == null)
6 {
7 throw new NullReferenceException(string.Format("Element with AutomationId '{0}' can not be find.", element.Current.AutomationId));
8 }
9
10 ThreadStart start2 = null;
11
12 if (start2 == null)
13 {
14 start2 = delegate
15 {
16 object obj2;
17 if (!element.TryGetCurrentPattern(InvokePattern.Pattern, out obj2))
18 {
19 throw new Exception(string.Format("Button element with AutomationId '{0}' does not support the InvokePattern.", element.Current.AutomationId));
20 }
21 (obj2 as InvokePattern).Invoke();
22 };
23 }
24 ThreadStart start = start2;
25 new Thread(start).Start();
26 Thread.Sleep(0xbb8);
27}
1. 发送异步消息的方式
Code
1[DllImport("user32.dll", CharSet = CharSet.Auto)]
2private static extern IntPtr PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
3[DllImport("user32.dll", CharSet = CharSet.Auto)]
4private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
5
6public static void ClickButtonById(this Tester tester, int processId, string buttonId)
7{
8 AutomationElement element = FindElementById(processId, buttonId);
9 if (element == null)
10 {
11 throw new NullReferenceException(string.Format("Element with AutomationId '{0}' can not be find.", element.Current.AutomationId));
12 }
13
14 IntPtr nativeWindowHandle = (IntPtr)element.Current.NativeWindowHandle;
15 if (nativeWindowHandle != IntPtr.Zero)
16 {
17 HandleRef hWnd = new HandleRef(nativeWindowHandle, nativeWindowHandle);
18 PostMessage(hWnd, 0xf5, IntPtr.Zero, IntPtr.Zero);
19 }
20 }
21}
相关FindWindow和FindElement代码:
Code
1/**//// <summary>
2/// Get the automation elemention of current form.
3/// </summary>
4/// <param name="processId">Process Id</param>
5/// <returns>Target element</returns>
6public static AutomationElement FindWindowByProcessId(int processId)
7{
8 AutomationElement targetWindow = null;
9 int count = 0;
10 try
11 {
12 Process p = Process.GetProcessById(processId);
13 targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
14 return targetWindow;
15 }
16 catch (Exception ex)
17 {
18 count++;
19 StringBuilder sb = new StringBuilder();
20 string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
21 if (count > 5)
22 {
23 throw new InvalidProgramException(message, ex);
24 }
25 else
26 {
27 return FindWindowByProcessId(processId);
28 }
29 }
30}
31
32
33/**//// <summary>
34/// Get the automation element by automation Id.
35/// </summary>
36/// <param name="windowName">Window name</param>
37/// <param name="automationId">Control automation Id</param>
38/// <returns>Automatin element searched by automation Id</returns>
39public static AutomationElement FindElementById(int processId, string automationId)
40{
41 AutomationElement aeForm = FindWindowByProcessId(processId);
42 AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
43 new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
44 return tarFindElement;
45}
本节主要针对在非Window 7操作系统上面出现的有关WPF和Winform中的MessageBox关闭问题作了简单的探讨。