zoukankan      html  css  js  c++  java
  • Server-Side UI Automation Provider

    Server-Side UI Automation Provider - WinForm Sample

    2014-09-14

    源代码 

    目录

    引用程序集
    提供程序接口
    公开服务器端 UI 自动化提供程序
    从 UI 自动化提供程序返回属性
    从 UI 自动化提供程序中引发事件
    在 UI 自动化提供程序中支持控件模式
    WinForm Sample
    参考

    引用程序集[1]


     返回

    UI 自动化提供程序项目必须引用以下程序集:

    • UIAutomationProviders.dll
    • UIAutomationTypes.dll 
    • WindowsBase.dll

    提供程序接口[1]


     返回

    每个 UI 自动化提供程序必须实现下列接口之一。

    接口

    说明

    IRawElementProviderSimple

    提供窗口中承载的简单控件的功能,包括对控件模式和属性的支持。

    IRawElementProviderFragment

    继承自 IRawElementProviderSimple  为复杂控件中的元素添加功能,包括在片段中导航、设置焦点和返回元素的边框。 

    IRawElementProviderFragmentRoot

    继承自 IRawElementProviderFragment  为复杂控件中的根元素添加功能,包括将子元素定位于指定坐标以及设置整个控件的焦点状态。

    IRawElementProviderSimple的metadata见图1

    图1 metadata - IRawElementProviderSimple 

    公开服务器端 UI 自动化提供程序[2]


     返回

    重写窗口过程以捕获 WM_GETOBJECT,以响应客户端应用程序发送到控件窗口的 WM_GETOBJECT 消息时,返回实现 IRawElementProviderSimple(或派生接口)的对象。

     1         /// <summary>
     2         /// Handles WM_GETOBJECT message; others are passed to base handler.
     3         /// </summary>
     4         /// <param name="m">Windows message.</param>
     5         /// <remarks>
     6         /// This method enables UI Automation to find the control.
     7         /// </remarks>
     8         [PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
     9         protected override void WndProc(ref Message m)
    10         {
    11             const int WM_GETOBJECT = 0x003D;
    12 
    13             if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() == AutomationInteropProvider.RootObjectId))
    14             {
    15                 m.Result = AutomationInteropProvider.ReturnRawElementProvider(
    16                     Handle, m.WParam, m.LParam, (IRawElementProviderSimple)this);
    17                 return;
    18             }
    19             base.WndProc(ref m);
    20         }
    View Code

    从 UI 自动化提供程序返回属性[3]


     返回

    实现接口IRawElementProviderSimple方法GetPropertyValue,使得UI 自动化提供程序将元素的属性返回到客户端应用程序。

    对于不显式支持的任意属性,提供程序必须返回 null。这样可以确保 UI 自动化尝试从其他源(如宿主窗口提供程序)获取属性。

     1         /// <summary>
     2         /// Returns property values.
     3         /// </summary>
     4         /// <param name="propId">Property identifier.</param>
     5         /// <returns>Property value.</returns>
     6         object IRawElementProviderSimple.GetPropertyValue(int propId)
     7         {
     8             if (propId == AutomationElementIdentifiers.ClassNameProperty.Id)
     9             {
    10                 return "CustomButtonControlClass";
    11             }
    12             else if (propId == AutomationElementIdentifiers.ControlTypeProperty.Id)
    13             {
    14                 return ControlType.Button.Id;
    15             }
    16             if (propId == AutomationElementIdentifiers.HelpTextProperty.Id)
    17             {
    18                 return "Change the button color and pattern.";
    19             }
    20             if (propId == AutomationElementIdentifiers.IsEnabledProperty.Id)
    21             {
    22                 return true;
    23             }
    24             else
    25             {
    26                 return null;
    27             }
    28         }
    View Code

    从 UI 自动化提供程序中引发事件[4]


     返回

    下面的代码在自定义按钮控件的实现中引发了UI自动化事件。该实现使UI自动化客户端应用程序能够模拟按钮单击。

    为了避免不必要的处理,示例将检查 ClientsAreListening 以确定是否应该引发事件。

     1 /// <summary>
     2 /// Responds to a button click, regardless of whether it was caused by a mouse or
     3 /// keyboard click or by InvokePattern.Invoke. 
     4 /// </summary>
     5 private void OnCustomButtonClicked()
     6 {
     7     // TODO  Perform program actions invoked by the control.
     8 
     9     // Raise an event.
    10     if (AutomationInteropProvider.ClientsAreListening)
    11     {
    12         AutomationEventArgs args = new AutomationEventArgs(InvokePatternIdentifiers.InvokedEvent);
    13         AutomationInteropProvider.RaiseAutomationEvent(InvokePatternIdentifiers.InvokedEvent, this, args);
    14     }
    15 }
    View Code

    在 UI 自动化提供程序中支持控件模式[5]


     返回

    支持控件模式

    1.为该元素支持的控件模式实现相应的接口,例如,为 InvokePattern 实现 IInvokeProvider。 

     1         /// <summary>
     2         /// Responds to an InvokePattern.Invoke by simulating a MouseDown event.
     3         /// </summary>
     4         void IInvokeProvider.Invoke()
     5         {
     6             // If the control is not enabled, we're responsible for letting UI Automation know.
     7             // It catches the exception and then throws it to the client.
     8             IRawElementProviderSimple provider = this as IRawElementProviderSimple;
     9             if (false == (bool)provider.GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id))
    10             {
    11                 throw new ElementNotEnabledException();
    12             }
    13 
    14             // Create arguments for the click event. The parameters aren't used.
    15             MouseEventArgs mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0);
    16 
    17             // Simulate a mouse click. We cannot call RespondToClick directly, 
    18             // because it is illegal to update the UI from a different thread.
    19             MouseEventHandler handler = CustomButton_MouseDown;
    20             BeginInvoke(handler, new object[] { this, mouseArgs });
    21         }
    View Code

    若invoke实现如下,则用客户端模拟点击操作,只会弹出对话框。

    void IInvokeProvider.Invoke(){       MessageBox.Show("invoke Pattern.");        }

    我们可以用UISpy模拟客户端操作,引发invoke事件:

    1. 选中CustomControl
    2. 菜单‘View'->'Control Pattern,选择'Call Method'

    见下图2,只弹出了MessageBox,customControl的图形并没有改变

     

    图2 UISpy模拟客户端操作,引发invoke事件

    2.返回一个对象,其中包含 IRawElementProviderSimple.GetPatternProvider 实现中的每个控件接口的实现。

     1         /// <summary>
     2         /// Returns the object that supports the specified pattern.
     3         /// </summary>
     4         /// <param name="patternId">ID of the pattern.</param>
     5         /// <returns>Object that implements IInvokeProvider.</returns>
     6         object IRawElementProviderSimple.GetPatternProvider(int patternId)
     7         {
     8             if (patternId == InvokePatternIdentifiers.Pattern.Id)
     9             {
    10                 return this;
    11             }
    12             else
    13             {
    14                 return null;
    15             }
    16         }
    View Code

    WinForm Sample[6]


     返回

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Text;
      4 using System.Windows.Automation.Provider;
      5 using System.Windows.Automation;
      6 using System.Drawing;
      7 using System.Windows.Forms;
      8 using System.Diagnostics;
      9 using System.Security.Permissions;
     10 
     11 
     12 namespace ElementProvider
     13 {
     14     class CustomButton : Control, IRawElementProviderSimple, IInvokeProvider
     15     {
     16         bool buttonState = false;
     17         IntPtr myHandle;
     18 
     19         /// <summary>
     20         /// Constructor.
     21         /// </summary>
     22         /// <param name="rect">Position and size of control.</param>
     23         public CustomButton()
     24         {
     25             myHandle = Handle;
     26 
     27             // Add event handlers.
     28             MouseDown += new System.Windows.Forms.MouseEventHandler(this.CustomButton_MouseDown);
     29             this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.CustomButton_KeyPress);
     30             this.GotFocus += new EventHandler(CustomButton_ChangeFocus);
     31             this.LostFocus += new EventHandler(CustomButton_ChangeFocus);
     32         }
     33 
     34         /// <summary>
     35         /// Handles WM_GETOBJECT message; others are passed to base handler.
     36         /// </summary>
     37         /// <param name="m">Windows message.</param>
     38         /// <remarks>
     39         /// This method enables UI Automation to find the control.
     40         /// </remarks>
     41         [PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
     42         protected override void WndProc(ref Message m)
     43         {
     44             const int WM_GETOBJECT = 0x003D;
     45 
     46             if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() == AutomationInteropProvider.RootObjectId))
     47             {
     48                 m.Result = AutomationInteropProvider.ReturnRawElementProvider(
     49                     Handle, m.WParam, m.LParam, (IRawElementProviderSimple)this);
     50                 return;
     51             }
     52             base.WndProc(ref m);
     53         }
     54 
     55         /// <summary>
     56         /// Ensure that the focus rectangle is drawn or erased when focus changes.
     57         /// </summary>
     58         /// <param name="sender"></param>
     59         /// <param name="e"></param>
     60         void CustomButton_ChangeFocus(object sender, EventArgs e)
     61         {
     62             Refresh();
     63         }
     64 
     65 
     66         /// <summary>
     67         /// Handles Paint event.
     68         /// </summary>
     69         /// <param name="e">Event arguments.</param>
     70         protected override void OnPaint(PaintEventArgs e)
     71         {
     72             Rectangle buttonRect = new Rectangle(ClientRectangle.Left + 2,
     73                 ClientRectangle.Top + 2,
     74                 ClientRectangle.Width - 4,
     75                 ClientRectangle.Height - 4);
     76             System.Drawing.Drawing2D.HatchBrush brush;
     77             if (buttonState)
     78             {
     79                 brush = new System.Drawing.Drawing2D.HatchBrush(
     80                     System.Drawing.Drawing2D.HatchStyle.DarkHorizontal, Color.Red, Color.White);
     81             }
     82             else
     83             {
     84                 brush = new System.Drawing.Drawing2D.HatchBrush(
     85                     System.Drawing.Drawing2D.HatchStyle.DarkVertical, Color.Green, Color.White);
     86             }
     87 
     88             e.Graphics.FillRectangle(brush, buttonRect);
     89             if (Focused)
     90             {
     91                 ControlPaint.DrawFocusRectangle(e.Graphics, ClientRectangle);
     92             }
     93         }
     94 
     95         /// <summary>
     96         /// Responds to a button click, regardless of whether it was caused by a mouse or
     97         /// keyboard click or by InvokePattern.Invoke. 
     98         /// </summary>
     99         private void RespondToClick()
    100         {
    101             buttonState = !buttonState;
    102             this.Focus();
    103             this.Refresh();
    104 
    105             // Raise an event.
    106             if (AutomationInteropProvider.ClientsAreListening)
    107             {
    108                 AutomationEventArgs args = new AutomationEventArgs(InvokePatternIdentifiers.InvokedEvent);
    109                 AutomationInteropProvider.RaiseAutomationEvent(InvokePatternIdentifiers.InvokedEvent, this, args);
    110             }
    111         }
    112 
    113         /// <summary>
    114         /// Handles MouseDown event.
    115         /// </summary>
    116         /// <param name="sender">Object that raised the event.</param>
    117         /// <param name="e">Event arguments.</param>
    118         public void CustomButton_MouseDown(object sender, MouseEventArgs e)
    119         {
    120             RespondToClick();
    121         }
    122 
    123         /// <summary>
    124         /// Handles Keypress event.
    125         /// </summary>
    126         /// <param name="sender">Object that raised the event.</param>
    127         /// <param name="e">Event arguments.</param>
    128         public void CustomButton_KeyPress(object sender, KeyPressEventArgs e)
    129         {
    130             if (e.KeyChar == (char)Keys.Space)
    131             {
    132                 RespondToClick();
    133             }
    134         }
    135 
    136         #region IRawElementProviderSimple
    137 
    138         /// <summary>
    139         /// Returns the object that supports the specified pattern.
    140         /// </summary>
    141         /// <param name="patternId">ID of the pattern.</param>
    142         /// <returns>Object that implements IInvokeProvider.</returns>
    143         object IRawElementProviderSimple.GetPatternProvider(int patternId)
    144         {
    145             if (patternId == InvokePatternIdentifiers.Pattern.Id)
    146             {
    147                 return this;
    148             }
    149             else
    150             {
    151                 return null;
    152             }
    153         }
    154 
    155         /// <summary>
    156         /// Returns property values.
    157         /// </summary>
    158         /// <param name="propId">Property identifier.</param>
    159         /// <returns>Property value.</returns>
    160         object IRawElementProviderSimple.GetPropertyValue(int propId)
    161         {
    162             if (propId == AutomationElementIdentifiers.ClassNameProperty.Id)
    163             {
    164                 return "CustomButtonControlClass";
    165             }
    166             else if (propId == AutomationElementIdentifiers.ControlTypeProperty.Id)
    167             {
    168                 return ControlType.Button.Id;
    169             }
    170             if (propId == AutomationElementIdentifiers.HelpTextProperty.Id)
    171             {
    172                 return "Change the button color and pattern.";
    173             }
    174             if (propId == AutomationElementIdentifiers.IsEnabledProperty.Id)
    175             {
    176                 return true;
    177             }
    178             else
    179             {
    180                 return null;
    181             }
    182         }
    183 
    184 
    185         /// <summary>
    186         /// Tells UI Automation that this control is hosted in an HWND, which has its own
    187         /// provider.
    188         /// </summary>
    189         IRawElementProviderSimple IRawElementProviderSimple.HostRawElementProvider
    190         {
    191             get
    192             {
    193                 return AutomationInteropProvider.HostProviderFromHandle(myHandle);
    194             }
    195         }
    196 
    197         /// <summary>
    198         /// Retrieves provider options.
    199         /// </summary>
    200         ProviderOptions IRawElementProviderSimple.ProviderOptions
    201         {
    202             get
    203             {
    204                 return ProviderOptions.ServerSideProvider;
    205             }
    206         }
    207         #endregion IRawElementProviderSimple
    208 
    209         #region IInvokeProvider
    210 
    211         /// <summary>
    212         /// Responds to an InvokePattern.Invoke by simulating a MouseDown event.
    213         /// </summary>
    214         void IInvokeProvider.Invoke()
    215         {
    216             // If the control is not enabled, we're responsible for letting UI Automation know.
    217             // It catches the exception and then throws it to the client.
    218             IRawElementProviderSimple provider = this as IRawElementProviderSimple;
    219             if (false == (bool)provider.GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id))
    220             {
    221                 throw new ElementNotEnabledException();
    222             }
    223 
    224             // Create arguments for the click event. The parameters aren't used.
    225             MouseEventArgs mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0);
    226 
    227             // Simulate a mouse click. We cannot call RespondToClick directly, 
    228             // because it is illegal to update the UI from a different thread.
    229             MouseEventHandler handler = CustomButton_MouseDown;
    230             BeginInvoke(handler, new object[] { this, mouseArgs });
    231         }
    232 
    233         #endregion InvokeProvider
    234 
    235 
    236     }  // CustomButton class.
    237 } // Namespace.
    View Code

    图3 UISpy Co年trol view

    参考

    [1] 服务器端 UI 自动化提供程序的实现

    [2] 公开服务器端 UI 自动化提供程序

    [3] 从 UI 自动化提供程序返回属性

    [4] 从 UI 自动化提供程序中引发事件

    [5] 在 UI 自动化提供程序中支持控件模式

    [6] Simple Provider Sample

  • 相关阅读:
    SQL Server分页查询的万能存储过程
    前端相关及一些关键单词
    java shiro面试题
    java集合面试题
    芬芬购物系统基础配置2
    芬芬购物系统基础配置
    多线程安全
    多线程入门
    JAVA多线程
    java多线程
  • 原文地址:https://www.cnblogs.com/Ming8006/p/3972788.html
Copyright © 2011-2022 走看看