在使用UI Automation对Winform和WPF的程序测试中发现有一些不同的地方,而这些不同来自于Winform与WPF的处理机制不同。下面我们通过一个简单的实例来加以说明:
实例描述
我们使用InvokePattern来点击按钮弹出一个对话框,然后点击对话框中的“确定”按钮关闭对话框。
两种方式对比
首先我们使用如下代码来针对Winfom和WPF分别进行测试:

Code
1
public 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
1
public 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)]
2
private static extern IntPtr PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
3
[DllImport("user32.dll", CharSet = CharSet.Auto)]
4
private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
5
6
public 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>
6
public 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>
39
public 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关闭问题作了简单的探讨。