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

原文地址:https://www.cnblogs.com/Ming8006/p/3972788.html