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(或派生接口)的对象。

         /// <summary>
/// Handles WM_GETOBJECT message; others are passed to base handler.
/// </summary>
/// <param name="m">Windows message.</param>
/// <remarks>
/// This method enables UI Automation to find the control.
/// </remarks>
[PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
protected override void WndProc(ref Message m)
{
const int WM_GETOBJECT = 0x003D; if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() == AutomationInteropProvider.RootObjectId))
{
m.Result = AutomationInteropProvider.ReturnRawElementProvider(
Handle, m.WParam, m.LParam, (IRawElementProviderSimple)this);
return;
}
base.WndProc(ref m);
}

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


返回

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

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

         /// <summary>
/// Returns property values.
/// </summary>
/// <param name="propId">Property identifier.</param>
/// <returns>Property value.</returns>
object IRawElementProviderSimple.GetPropertyValue(int propId)
{
if (propId == AutomationElementIdentifiers.ClassNameProperty.Id)
{
return "CustomButtonControlClass";
}
else if (propId == AutomationElementIdentifiers.ControlTypeProperty.Id)
{
return ControlType.Button.Id;
}
if (propId == AutomationElementIdentifiers.HelpTextProperty.Id)
{
return "Change the button color and pattern.";
}
if (propId == AutomationElementIdentifiers.IsEnabledProperty.Id)
{
return true;
}
else
{
return null;
}
}

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


返回

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

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

 /// <summary>
/// Responds to a button click, regardless of whether it was caused by a mouse or
/// keyboard click or by InvokePattern.Invoke.
/// </summary>
private void OnCustomButtonClicked()
{
// TODO Perform program actions invoked by the control. // Raise an event.
if (AutomationInteropProvider.ClientsAreListening)
{
AutomationEventArgs args = new AutomationEventArgs(InvokePatternIdentifiers.InvokedEvent);
AutomationInteropProvider.RaiseAutomationEvent(InvokePatternIdentifiers.InvokedEvent, this, args);
}
}

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


返回

支持控件模式

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

         /// <summary>
/// Responds to an InvokePattern.Invoke by simulating a MouseDown event.
/// </summary>
void IInvokeProvider.Invoke()
{
// If the control is not enabled, we're responsible for letting UI Automation know.
// It catches the exception and then throws it to the client.
IRawElementProviderSimple provider = this as IRawElementProviderSimple;
if (false == (bool)provider.GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id))
{
throw new ElementNotEnabledException();
} // Create arguments for the click event. The parameters aren't used.
MouseEventArgs mouseArgs = new MouseEventArgs(MouseButtons.Left, , , , ); // Simulate a mouse click. We cannot call RespondToClick directly,
// because it is illegal to update the UI from a different thread.
MouseEventHandler handler = CustomButton_MouseDown;
BeginInvoke(handler, new object[] { this, mouseArgs });
}

若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 实现中的每个控件接口的实现。

         /// <summary>
/// Returns the object that supports the specified pattern.
/// </summary>
/// <param name="patternId">ID of the pattern.</param>
/// <returns>Object that implements IInvokeProvider.</returns>
object IRawElementProviderSimple.GetPatternProvider(int patternId)
{
if (patternId == InvokePatternIdentifiers.Pattern.Id)
{
return this;
}
else
{
return null;
}
}

WinForm Sample[6]


返回

 using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Automation.Provider;
using System.Windows.Automation;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.Security.Permissions; namespace ElementProvider
{
class CustomButton : Control, IRawElementProviderSimple, IInvokeProvider
{
bool buttonState = false;
IntPtr myHandle; /// <summary>
/// Constructor.
/// </summary>
/// <param name="rect">Position and size of control.</param>
public CustomButton()
{
myHandle = Handle; // Add event handlers.
MouseDown += new System.Windows.Forms.MouseEventHandler(this.CustomButton_MouseDown);
this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.CustomButton_KeyPress);
this.GotFocus += new EventHandler(CustomButton_ChangeFocus);
this.LostFocus += new EventHandler(CustomButton_ChangeFocus);
} /// <summary>
/// Handles WM_GETOBJECT message; others are passed to base handler.
/// </summary>
/// <param name="m">Windows message.</param>
/// <remarks>
/// This method enables UI Automation to find the control.
/// </remarks>
[PermissionSetAttribute(SecurityAction.Demand, Unrestricted = true)]
protected override void WndProc(ref Message m)
{
const int WM_GETOBJECT = 0x003D; if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() == AutomationInteropProvider.RootObjectId))
{
m.Result = AutomationInteropProvider.ReturnRawElementProvider(
Handle, m.WParam, m.LParam, (IRawElementProviderSimple)this);
return;
}
base.WndProc(ref m);
} /// <summary>
/// Ensure that the focus rectangle is drawn or erased when focus changes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void CustomButton_ChangeFocus(object sender, EventArgs e)
{
Refresh();
} /// <summary>
/// Handles Paint event.
/// </summary>
/// <param name="e">Event arguments.</param>
protected override void OnPaint(PaintEventArgs e)
{
Rectangle buttonRect = new Rectangle(ClientRectangle.Left + ,
ClientRectangle.Top + ,
ClientRectangle.Width - ,
ClientRectangle.Height - );
System.Drawing.Drawing2D.HatchBrush brush;
if (buttonState)
{
brush = new System.Drawing.Drawing2D.HatchBrush(
System.Drawing.Drawing2D.HatchStyle.DarkHorizontal, Color.Red, Color.White);
}
else
{
brush = new System.Drawing.Drawing2D.HatchBrush(
System.Drawing.Drawing2D.HatchStyle.DarkVertical, Color.Green, Color.White);
} e.Graphics.FillRectangle(brush, buttonRect);
if (Focused)
{
ControlPaint.DrawFocusRectangle(e.Graphics, ClientRectangle);
}
} /// <summary>
/// Responds to a button click, regardless of whether it was caused by a mouse or
/// keyboard click or by InvokePattern.Invoke.
/// </summary>
private void RespondToClick()
{
buttonState = !buttonState;
this.Focus();
this.Refresh(); // Raise an event.
if (AutomationInteropProvider.ClientsAreListening)
{
AutomationEventArgs args = new AutomationEventArgs(InvokePatternIdentifiers.InvokedEvent);
AutomationInteropProvider.RaiseAutomationEvent(InvokePatternIdentifiers.InvokedEvent, this, args);
}
} /// <summary>
/// Handles MouseDown event.
/// </summary>
/// <param name="sender">Object that raised the event.</param>
/// <param name="e">Event arguments.</param>
public void CustomButton_MouseDown(object sender, MouseEventArgs e)
{
RespondToClick();
} /// <summary>
/// Handles Keypress event.
/// </summary>
/// <param name="sender">Object that raised the event.</param>
/// <param name="e">Event arguments.</param>
public void CustomButton_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Space)
{
RespondToClick();
}
} #region IRawElementProviderSimple /// <summary>
/// Returns the object that supports the specified pattern.
/// </summary>
/// <param name="patternId">ID of the pattern.</param>
/// <returns>Object that implements IInvokeProvider.</returns>
object IRawElementProviderSimple.GetPatternProvider(int patternId)
{
if (patternId == InvokePatternIdentifiers.Pattern.Id)
{
return this;
}
else
{
return null;
}
} /// <summary>
/// Returns property values.
/// </summary>
/// <param name="propId">Property identifier.</param>
/// <returns>Property value.</returns>
object IRawElementProviderSimple.GetPropertyValue(int propId)
{
if (propId == AutomationElementIdentifiers.ClassNameProperty.Id)
{
return "CustomButtonControlClass";
}
else if (propId == AutomationElementIdentifiers.ControlTypeProperty.Id)
{
return ControlType.Button.Id;
}
if (propId == AutomationElementIdentifiers.HelpTextProperty.Id)
{
return "Change the button color and pattern.";
}
if (propId == AutomationElementIdentifiers.IsEnabledProperty.Id)
{
return true;
}
else
{
return null;
}
} /// <summary>
/// Tells UI Automation that this control is hosted in an HWND, which has its own
/// provider.
/// </summary>
IRawElementProviderSimple IRawElementProviderSimple.HostRawElementProvider
{
get
{
return AutomationInteropProvider.HostProviderFromHandle(myHandle);
}
} /// <summary>
/// Retrieves provider options.
/// </summary>
ProviderOptions IRawElementProviderSimple.ProviderOptions
{
get
{
return ProviderOptions.ServerSideProvider;
}
}
#endregion IRawElementProviderSimple #region IInvokeProvider /// <summary>
/// Responds to an InvokePattern.Invoke by simulating a MouseDown event.
/// </summary>
void IInvokeProvider.Invoke()
{
// If the control is not enabled, we're responsible for letting UI Automation know.
// It catches the exception and then throws it to the client.
IRawElementProviderSimple provider = this as IRawElementProviderSimple;
if (false == (bool)provider.GetPropertyValue(AutomationElementIdentifiers.IsEnabledProperty.Id))
{
throw new ElementNotEnabledException();
} // Create arguments for the click event. The parameters aren't used.
MouseEventArgs mouseArgs = new MouseEventArgs(MouseButtons.Left, , , , ); // Simulate a mouse click. We cannot call RespondToClick directly,
// because it is illegal to update the UI from a different thread.
MouseEventHandler handler = CustomButton_MouseDown;
BeginInvoke(handler, new object[] { this, mouseArgs });
} #endregion InvokeProvider } // CustomButton class.
} // Namespace.

图3 UISpy Co年trol view

参考

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

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

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

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

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

[6] Simple Provider Sample

Server-Side UI Automation Provider - WinForm Sample的更多相关文章

  1. Client-Side UI Automation Provider - WinForm Sample

    Client-Side UI Automation Provider -  WinForm Sample 2014-09-15 源代码 目录 引用程序集实现提供程序接口分发客户端提供程序注册和配置客户 ...

  2. Server-Side UI Automation Provider - WPF Sample

    Server-Side UI Automation Provider - WPF Sample 2014-09-14 引用程序集 自动化对等类 WPF Sample 参考 引用程序集 返回 UIAut ...

  3. MS UI Automation Introduction

    MS UI Automation Introduction 2014-09-17 MS UI Automation是什么 UIA架构 UI自动化模型 UI自动化树概述 UI自动化控件模式概述 UI 自 ...

  4. 开源自己用python封装的一个Windows GUI(UI Automation)自动化工具,支持MFC,Windows Forms,WPF,Metro,Qt

    首先,大家可以看下这个链接 Windows GUI自动化测试技术的比较和展望 . 这篇文章介绍了Windows中GUI自动化的三种技术:Windows API, MSAA - Microsoft Ac ...

  5. 使用UI Automation实现自动化测试--5-7

    使用UI Automation实现自动化测试--5 (Winfrom和WPF中弹出和关闭对话框的不同处理方式) 在使用UI Automation对Winform和WPF的程序测试中发现有一些不同的地方 ...

  6. 使用UI Automation实现自动化测试--1-4

    Introduction UI Automation是Microsoft .NET 3.0框架下提供的一种用于自动化测试的技术,是在MSAA基础上建立的,MSAA就是Microsoft Active ...

  7. UI Automation 简介

    转载,源地址: http://blog.csdn.net/ffeiffei/article/details/6637418 MS UI Automation(Microsoft User Interf ...

  8. MS UI Automation简介

    转自:http://blog.csdn.net/ffeiffei/article/details/6637418 MS UI Automation(Microsoft User Interface A ...

  9. UI Automation的两个成熟的框架(QTP 和Selenium)

    自己在google code中开源了自己一直以来做的两个自动化的框架,一个是针对QTP的一个是针对Selenium的,显而易见,一个是商业的UI automation工具,一个是开源的自动化工具. 只 ...

随机推荐

  1. nodeJs入门篇之认识nodejs

    摘要:将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎.chrome浏览器就基于V8,同时打开 ...

  2. Asp.net与Flex交互测试记录

    一.利用asp.net为Flex提供数据服务,flex为前端表现. 二.flex通过三种方式四种代码实现来取数据.分别为     HttpService. WebService. RemoteObje ...

  3. Visual Event插件----查看html元素绑定的事件与方法的利器

    WEB标准提倡结构.表现和行为相 分离,现在越来越多采用这种表现和行为的方式,但它也为我们开发调试带来一些问题,网页载入一堆JavaScript,,我们很难搞清楚最后在哪些元素的哪个动作绑定了事件,尤 ...

  4. 使用PHP_UML生成代码的UML图

    在读别人代码的时候, 在没有详细文档的时候, 如何快速的看清整个代码的结构(类结构), 就成为了一个现实的问题. 今天我就介绍一种, 自动生成UML图的方法. 假设, 我有一个项目文件夹:laruen ...

  5. MapReduce数据流向分析

    MR数据流向示意图 步骤 1 输入文件从HDFS流向Mapper节点.在一般情况下,map所需要的数据就存在本节点,这就是数据本地化计算的优势,但是往往集群中数据分布不均衡(1000台节点,数据冗余度 ...

  6. LCS最长公共子序列(最优线性时间O(n))

    这篇日志主要为了记录这几天的学习成果. 最长公共子序列根据要不要求子序列连续分两种情况. 只考虑两个串的情况,假设两个串长度均为n. 一,子序列不要求连续. (1)动态规划(O(n*n)) (转自:h ...

  7. SGU 107

    107. 987654321 problem time limit per test: 0.25 sec. memory limit per test: 4096 KB For given numbe ...

  8. android 启动adb

    1.命令行进入 sdk/platform-tools 2.执行命令 adb kill-server 3.执行命令 adb start-server

  9. Android ActionBar详解

    Android ActionBar详解 分类: Android2014-04-30 15:23 1094人阅读 评论(0) 收藏 举报 androidActionBar   目录(?)[+]   第4 ...

  10. Log4J入门教程(二) 参数讲解

    继续接着Log4J入门教程(一)中的例子进行讲解,其中log4j.properties中的内容为    Log4j的三个重要组件—— Loggers, Appenders, Layouts ,这三个组 ...