2

런타임 중에 Win32, WPF 컨트롤을 찾을 수있는 자동화 요소 구현이 포함 된 자체 확장 메서드를 만들었습니다.UIA 자동화 요소 대상 응용 프로그램 프로세스의 GDI 누설

동적 특성과 부모 컨트롤을 입력으로 지정하고 UIA 개념을 사용하여 컨트롤을 찾고 컨트롤을 CodedUI 컨트롤 (WinControls/WpfControls)으로 다시 반환합니다.

UIA 개념을 구현 한 후 일부 사용자 지정 컨트롤이 CodedUI를 사용하여 식별되지 않았기 때문에이 개념을 구현했지만 자동화 코드의 안정성이 향상되었습니다.

스크립트를 Smoke/Regression으로 실행하는 동안 아무 문제가 없었지만 CycleTest (30 회 반복으로 Smoke 스크립트를 실행하는 것처럼 사용)에서 대상 응용 프로그램 프로세스에서 일관되게 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; 
    } 

답변

0

내가 과거에 이러한 경험을 한 이유는 VB6 응용 프로그램에서 가비지 수집 된 적이 없었기 때문입니다. 난 단지 응용 프로그램 수명주기의 전체에 살 것이다 어떤 컨트롤의 한 인스턴스를 얻는 것이 좋습니다 것입니다. 우리에게 이것은 리본 바가되었습니다. 새 자동화 요소가 생길 때마다 VB6 응용 프로그램에서 컨트롤을 지원하는 자동화 피어를 만들고 메모리와 GDI 개체가 누출됩니다.

blog은 UIA의 클라이언트 (UIA 테스트)와 서버 (테스트중인 응용 프로그램) 간의 통신 방식을 잘 보여줍니다.

관련 문제