UIA 抛出COMException的解决方案
Introducation
在基于UI Automation平台的软件UI 自动化测试中,找控件的过程中有时候会出现如下异常:
System.Runtime.InteropServices.COMException (0x80042002): Exception from HRESULT: 0x80042002 at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo) at MS.Internal.Automation.UiaCoreApi.CheckError(Int32 hr) at MS.Internal.Automation.UiaCoreApi.UiaFind(SafeNodeHandle hnode, UiaFindParams findParams, Condition findCondition, UiaCacheRequest request) at System.Windows.Automation.AutomationElement.Find(TreeScope scope, Condition condition, UiaCacheRequest request, Boolean findFirst, BackgroundWorker worker) at System.Windows.Automation.AutomationElement.FindFirst(TreeScope scope, Condition condition) --- End of inner exception stack trace --- |
通过在网上查找相关的资料,得知抛出COMException的原因是使用了非托管资源,在程序运行中GC无法自动回收相应的垃圾,进而导致此异常出现。
Solution
注:测试如下方法需要引入如下命名空间 using System.Windows.Automation; using System.Runtime.InteropServices; |
如下代码为通过window name来查找window窗体的AutomationElement对象:
/// <summary> /// Find target window by window name. /// </summary> /// <param name="windowName">Window name</param> /// <returns>Return target window element</returns> public static AutomationElement FindWindowByWindowName(string windowName) { AutomationElement targetWindow = null; int count = 0; try { targetWindow = AutomationElement.FromHandle(WindowPtr(windowName)); return targetWindow; } catch (Exception) { Console.WriteLine("Try #" + count + 1 + " to find the target element"); count++; if (count > 10) { throw new COMException ("Target window is not existing ,please run the program and try it again"); } GC.Collect(); Thread.Sleep(3000); return FindWindowByWindowName(windowName); } } #region FindWindow Handing /// <summary> /// Find window with win32 API /// </summary> /// <param name="lpClassName">Class name</param> /// <param name="lpWindowName">WindowName</param> /// <returns></returns> [DllImport("USER32.DLL")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); /// <summary> /// Find window IntPtr /// </summary> /// <param name="windowName">Application WindowName</param> private static IntPtr WindowPtr(string windowName) { IntPtr intPtr = FindWindow(null, windowName); return intPtr; } #endregion |
在上面的方法中,我们通过USER32.DLL中的FindWindow函数还获取对应窗体的IntPtr类型的对象,在FindWindowByWindowName方法中通过捕获COMException来循环调用此方法,一旦捕获到此异常,使用GC.Collect();来强制进行垃圾回收,是非托管资源被释放,这样在循环执行此方法时将不会应为内存错误而导致无法继续执行。经过测试发现,此种解决方案虽然不是最好的方法,但确实可以解燃眉之急。
Reference
http://msdn.microsoft.com/zh-cn/library/s5zscb2d(VS.80).aspx