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://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/13004a05-8fa8-4b9c-b1f5-eff469f32235

http://msdn.microsoft.com/zh-cn/library/s5zscb2d(VS.80).aspx