2

我创建了自己的扩展方法,其中包含自动化元素实现,用于在运行时查找任何 Win32、WPF 控件。

我已将动态属性和父控件作为输入,并使用 UIA 概念找到控件,并再次将控件作为 CodedUI 控件 (WinControls/WpfControls) 返回。

我已经实现了这个概念,因为使用 CodedUI 没有识别一些自定义控件,在实现这个 UIA 概念之后,自动化代码的稳定性得到了提高。

在将脚本作为 Smoke/Regression 执行时,没有遇到任何问题,但是在使用 CycleTest 执行相同操作时(就像我们曾经重复运行 Smoke 脚本 30 次一样)在 Target 应用程序进程中始终出现 GDI 内存泄漏,而我没有面临UIMaps 的这类问题。

我尝试了很多方法来释放 GDI 对象,但没有一个对我有帮助。已将我的包装方法放在这里。

    public static T DetectControl<T>(this Object parentControl, Object attributes,
        [Optional]bool shouldUseAEToFind, [Optional] PropertyExpressionOperator propertyExpression,
        bool isLogMandatory = true, bool shouldScrollIntoView = false) where T : UITestControl, new()
    {           

        #region Local Variables

        AE.AutomationElement outputAutomationElement = null;

        AE.AutomationElement inputAutomationElement = null;

        List<AE.Condition> list_conditions = new List<AE.Condition>();

        string technologyName = string.Empty;
        string controlType = string.Empty;
        T returnControl = new T();
        if (!returnControl.GetType().Name.ToLower().Equals("uitestcontrol"))
        {
            technologyName = returnControl.TechnologyName;
            if (!returnControl.GetType().Name.ToString().EndsWith("Control"))
            {
                controlType = returnControl.ControlType.Name;
            }
        }

        StringBuilder allAttributesValue = new StringBuilder();

        string localizedControlType = string.Empty;

        bool flag = false;

        ICollection<KeyValuePair<string, object>> IColl_properties = null;

        List<T> list_matchingControls = null;

        IntPtr intPtr = IntPtr.Zero;

        #endregion



        try
        {
            LogWriter.WriteDayLog(messageType.information,
                "DetectControl method successfully called to find control with the given properties",
                logStatus.DONE);

            if (!shouldUseAEToFind)
                returnControl.Container = (UITestControl)parentControl;

            if (attributes.GetType().Name.Contains("Expando"))
            {
                IColl_properties = (ICollection<KeyValuePair<string, object>>)attributes;
            }
            else
            {
                IColl_properties = attributes.GetType().GetProperties().ToDictionary(x => x.Name.ToString(), x => x.GetValue(attributes));
            }

            foreach (KeyValuePair<string, object> keyValyePair in IColl_properties)
            {
                if (shouldUseAEToFind == false)
                {
                    if (propertyExpression == PropertyExpressionOperator.EqualTo)
                    {
                        returnControl.SearchProperties.Add(keyValyePair.Key, keyValyePair.Value.ToString());
                    }
                    else
                    {
                        returnControl.SearchProperties.Add(keyValyePair.Key, keyValyePair.Value.ToString(), PropertyExpressionOperator.Contains);
                    }
                }
                else
                {
                    if (keyValyePair.Key == "LocalizedControlType")
                    {
                        localizedControlType = keyValyePair.Value.ToString();
                    }

                    if (propertyExpression == PropertyExpressionOperator.EqualTo)
                    {
                        list_conditions.Add(new AE.PropertyCondition(GetAutomationProperty(keyValyePair.Key), (object)keyValyePair.Value));
                    }
                    else
                    {
                        if (keyValyePair.Key == "LocalizedControlType")
                        {
                            list_conditions.Add(new AE.PropertyCondition(GetAutomationProperty(keyValyePair.Key), (object)keyValyePair.Value));
                        }
                    }
                }

                if (IColl_properties.Last().Key != keyValyePair.Key)
                {
                    allAttributesValue.Append(keyValyePair.Key + " : " + keyValyePair.Value + " | ");
                }
                else
                {
                    allAttributesValue.Append(keyValyePair.Key + " : " + keyValyePair.Value);
                }
            }

            if (localizedControlType == string.Empty && shouldUseAEToFind && propertyExpression == PropertyExpressionOperator.Contains)
            {
                throw new InvalidDataException("Please provide a LocalizedControlType to proceed further.");
            }

            LogWriter.WriteDayLog(messageType.trace, "Started to find the control with the given properties --> "
                + allAttributesValue.ToString(), logStatus.DONE);

            if (shouldUseAEToFind)
            {
                LogWriter.WriteDayLog(messageType.information, "Finding the control based on the Automation Element", logStatus.DONE);

                inputAutomationElement = parentControl.GetInputAutomationElement(isLogMandatory);

                if (propertyExpression == PropertyExpressionOperator.EqualTo)
                {
                    try
                    {
                        outputAutomationElement = inputAutomationElement.FindFirst(AE.TreeScope.Children
                            | AE.TreeScope.Descendants | AE.TreeScope.Element,
                            new AE.AndCondition(list_conditions.ToArray()));
                    }
                    catch (Exception ex)
                    {
                        if (isLogMandatory)
                        {
                            LogWriter.WriteDayLog(messageType.exception, "Failed to do FindFirst, encountered exception", logStatus.FAIL);
                        }
                        else
                        {
                            LogWriter.WriteDayLog(messageType.warning, "Failed to do FindFirst, encountered exception", logStatus.WARNING);
                        }
                        throw ex;
                    }

                    if (outputAutomationElement != null)
                    {
                        LogWriter.WriteDayLog(messageType.information, "outputAutomationElement is found", logStatus.DONE);
                    }
                    else
                        throw new Exception("outputAutomationElement is null");

                    if (shouldScrollIntoView)
                    {
                        LogWriter.WriteDayLog(messageType.information, "shouldScrollIntoView is true",
                            logStatus.DONE);

                        outputAutomationElement.ScrollIntoView();
                    }

                    bool visibleFlag = outputAutomationElement.Current.BoundingRectangle.Location.X == 0 ? outputAutomationElement.Current.BoundingRectangle.Location.Y > 0 : true;

                    if (visibleFlag)
                    {
                        switch (technologyName)
                        {
                            case "UIA":
                                LogWriter.WriteDayLog(messageType.information, "Try to find the control using FromNativeElement, UIA",
                                      logStatus.DONE);
                                returnControl = (T)UITestControlFactory.FromNativeElement(outputAutomationElement, "UIA");
                                break;
                            default:
                                LogWriter.WriteDayLog(messageType.information, "technologyName is " + technologyName,
                                       logStatus.DONE);

                                if (string.IsNullOrWhiteSpace(technologyName) || technologyName.Equals("MSAA"))
                                {
                                    try
                                    {
                                        LogWriter.WriteDayLog(messageType.information, "Try to find the control using FromPoint",
                                             logStatus.DONE);

                                        if (localizedControlType.ToLower() != "window")
                                        {
                                            returnControl = (T)UITestControlFactory.FromPoint(outputAutomationElement.GetClickablePoint());
                                        }
                                        else
                                        {
                                            intPtr = new IntPtr(outputAutomationElement.Current.NativeWindowHandle);
                                            returnControl = (T)UITestControlFactory.FromWindowHandle(intPtr);
                                        }
                                    }
                                    catch (AE.NoClickablePointException)
                                    {
                                        LogWriter.WriteDayLog(messageType.information, "Unable to find the control using GetClickablePoint, try FromWindowHandle", logStatus.DONE);

                                        intPtr = new IntPtr(outputAutomationElement.Current.NativeWindowHandle);
                                        returnControl = (T)UITestControlFactory.FromWindowHandle(intPtr);
                                    }
                                    catch (Exception ex)
                                    {
                                        if (!ex.Message.Contains("Access is denied"))
                                        {
                                            LogWriter.WriteDayLog(messageType.exception, "Failed to find the control, encountered exception",
                                                logStatus.FAIL);

                                            throw new Exception(ex.Message);
                                        }
                                        else
                                        {
                                            intPtr = new IntPtr(outputAutomationElement.Current.NativeWindowHandle);
                                            returnControl = (T)UITestControlFactory.FromWindowHandle(intPtr);
                                        }
                                    }
                                }
                                else
                                {
                                    throw new Exception("Different Technology has been captured : " + technologyName.ToUpper());
                                }
                                break;
                        }
                    }
                    else
                    {
                        returnControl = null;
                    }
                }
                else
                {
                    list_matchingControls = inputAutomationElement.DetectIdenticalControls<T>(new { LocalizedControlType = localizedControlType }, true, isLogMandatory: isLogMandatory);

                    foreach (KeyValuePair<string, object> keyValyePair in IColl_properties)
                    {
                        switch (keyValyePair.Key.ToLower())
                        {
                            case "name":
                                returnControl = (T)list_matchingControls.First(x => x.GetInspectProperty(InspectProperty.NAME).ToLower().Contains(keyValyePair.Value.ToString().ToLower()));
                                break;
                            case "classname":
                                returnControl = (T)list_matchingControls.First(x => x.GetInspectProperty(InspectProperty.CLASSNAME).ToLower().Contains(keyValyePair.Value.ToString().ToLower()));
                                break;
                            case "automationid":
                                returnControl = (T)list_matchingControls.First(x => x.GetInspectProperty(InspectProperty.AUTOMATIONID).ToLower().Contains(keyValyePair.Value.ToString().ToLower()));
                                break;
                        }
                    }
                }
            }
            else
            {
                LogWriter.WriteDayLog(messageType.information, "Finding the control based on default CodedUI Search properties", logStatus.DONE);

                flag = returnControl.TryFind();
            }
        }
        catch (InvalidCastException ex)
        {
            throw new Exception(ex.Message);
        }
        catch (InvalidDataException idex)
        {
            throw new Exception(idex.Message);
        }
        catch (Exception e)
        {
            if (isLogMandatory)
            {
                LogWriter.WriteDayLog(messageType.exception, "Failed to find the control with the given properties --> " + allAttributesValue.ToString() + ", it throws >> " + e.ToString(), logStatus.FAIL);
            }
            else
            {
                LogWriter.WriteDayLog(messageType.warning, "Failed to find the control with the given properties --> " + allAttributesValue.ToString() + ", it throws >> " + e.ToString(), logStatus.WARNING);
            }
            returnControl = null;
        }
        finally
        {
            if (flag || returnControl != null)
            {
                LogWriter.WriteDayLog(messageType.trace, "Successfully found the control with the given properties --> " + allAttributesValue.ToString(), logStatus.DONE);
            }
            else
            {
                if (isLogMandatory)
                {
                    LogWriter.WriteDayLog(messageType.error, "Unable to find the control with the given properties --> " + allAttributesValue.ToString(), logStatus.FAIL);
                }
                else
                {
                    LogWriter.WriteDayLog(messageType.warning, "Unable to find the control with the given properties --> " + allAttributesValue.ToString(), logStatus.WARNING);
                }
                returnControl = null;
            }

            #region Variable Cleanup

            list_conditions = null;
            technologyName = null;
            controlType = null;
            allAttributesValue = null;
            localizedControlType = null;
            IColl_properties = null;
            list_matchingControls = null;
            parentControl = null;

            AEDispose(inputAutomationElement);
            AEDispose(outputAutomationElement);
            AEDispose(null, intPtr);

            #endregion
        }
        return returnControl;
    }

internal static void AEDispose(AE.AutomationElement automationElement, [Optional]IntPtr wHandle)
    {
        if (automationElement != null)
        {
            IntPtr windowHandle = IntPtr.Zero;
            if (wHandle == IntPtr.Zero)
                windowHandle = new IntPtr(automationElement.Current.NativeWindowHandle);
            else
                windowHandle = wHandle;
            var deviceContext = GetWindowDC(windowHandle);
            var compatibleDeviceContext = CreateCompatibleDC(deviceContext);
            DeleteDC(compatibleDeviceContext);
            ReleaseDC(windowHandle, deviceContext);
            DeleteObject(windowHandle);
        }
    }


    [DllImport("GDI32.dll")]
    private static extern bool DeleteDC(IntPtr hDC);

    [DllImport("GDI32.dll")]
    private static extern bool DeleteObject(IntPtr hObject);

    [DllImport("GDI32.dll")]
    private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

    [DllImport("User32.dll")]
    private static extern IntPtr GetWindowDC(IntPtr hWnd);

    [DllImport("User32.dll")]
    private static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("GDI32.dll")]
    private static extern IntPtr CreateCompatibleDC(IntPtr hDC);



    internal static AE.AutomationProperty GetAutomationProperty(string propertyName)
    {
        #region Local Variables

        AE.AutomationProperty property = null;

        #endregion

        try
        {
            switch (propertyName.ToLower())
            {
                case "localizedcontroltype":
                    property = AE.AutomationElement.LocalizedControlTypeProperty;
                    break;
                case "name":
                    property = AE.AutomationElement.NameProperty;
                    break;
                case "automationid":
                    property = AE.AutomationElement.AutomationIdProperty;
                    break;
                case "classname":
                    property = AE.AutomationElement.ClassNameProperty;
                    break;
            }
        }
        catch (Exception e)
        {
            LogWriter.WriteDayLog(messageType.exception, "Unable to get the automation property, it throws exception >> " + e.Message, logStatus.FAIL);
        }

        return property;
    }



internal static AE.AutomationElement GetInputAutomationElement(this Object parentControl, bool isLogMandatory = true)
    {            

        #region Local Variables

        UITestControl uiControl = null;

        AE.AutomationElement inputAutomationElement = null;

        #endregion

        try
        {
            if (parentControl != null)
            {
                if (!parentControl.GetType().Name.ToLower().Contains("automation"))
                {
                    uiControl = (UITestControl)parentControl;

                    try
                    {
                        inputAutomationElement = (AE.AutomationElement)(uiControl.ControlType.ToString() == "Window" ? AE.AutomationElement.FromHandle(uiControl.WindowHandle) : uiControl.NativeElement);

                        LogWriter.WriteDayLog(messageType.information, "inputAutomationElement is found, uiControl.ControlType is "
                                    + uiControl.ControlType.ToString(), logStatus.DONE);
                    }
                    catch (InvalidCastException ex)
                    {
                        if (ex.Message.Contains("Unable to cast object of type 'System.Object[]' to type 'System.Windows.Automation.AutomationElement"))
                        {
                            inputAutomationElement = AE.AutomationElement.FromHandle(uiControl.WindowHandle);
                        }
                    }
                }
                else
                {
                    inputAutomationElement = (AE.AutomationElement)parentControl;
                }
            }
            else
            {
                inputAutomationElement = AE.AutomationElement.RootElement;
            }

            LogWriter.WriteDayLog(messageType.information, "Successfully converted the given control into Automaiton Element", logStatus.PASS);
        }
        catch (Exception ex)
        {
            if (isLogMandatory)
            {
                LogWriter.WriteDayLog(messageType.exception, "Unable to convert given control to Automation Element, throws exception >> "
                    + ex.Message, logStatus.FAIL);
            }
            else
            {
                LogWriter.WriteDayLog(messageType.trace, "Unable to convert given control to Automation Element, throws exception >> "
                + ex.Message + " So assigning Desktop as Root AutomationElement", logStatus.DONE);
                inputAutomationElement = AE.AutomationElement.RootElement;
            }
        }

        #region Variable Cleanup

        uiControl = null;

        #endregion

        return inputAutomationElement;
    }
4

1 回答 1

0

我过去经历过这些的原因是因为我不断地得到一个在我们的 VB6 应用程序中从未被垃圾收集的控件。我建议只获取将在整个应用程序生命周期中存在的任何控件的一个实例。对我们来说,这恰好是一个丝带酒吧。每次我得到一个新的自动化元素时,它都会强制 VB6 应用程序创建一个支持控件的自动化对等体,并且会泄漏内存和 GDI 对象。

这个博客很好地说明了 UIA 中客户端(您的 UIA 测试)和服务器(被测应用程序)之间的通信是如何工作的。

于 2017-12-14T00:30:00.000 回答