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. Leetcode#92 Reverse Linked List II

    原题地址 第一步,找到将要翻转的位置,记录翻转部分前一个节点(prev) 第二步,翻转,记录翻转完成后这部分的首(reverseHead)和尾(reverseTail),以及翻转部分之后的一个节点(p ...

  2. 阿里云centos配置ftp和svn全过程

    1.下载xshell 2.登录centos 3.安装vsftpd [root@xxx]# yum install vsftpd //安装vsftpd [root@xxx]# chkconfig vsf ...

  3. 在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static

    在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static ! 在C语言中,我们使用pthread_create ...

  4. 更改DEVExpress的Column的DisplayFormat为自定义的方法。

    更改DEVExpress的Column的DisplayFormat为自定义的方法. public partial class Form1 : XtraForm { public Form1() { I ...

  5. Sqli-labs less 55

    Less-55 本关的sql语句为: $sql="SELECT * FROM security.users WHERE id=($id) LIMIT 0,1"; 其余和less54 ...

  6. 充分发挥异步在 ASP.NET 中的强大优势

    作者:Brij Bhushan Mishra 最近几年,异步编程受到极大关注,主要是出于两个关键原因:首先,它有助于提供更好的用户体验,因为不会阻塞 UI 线程,避免了处理结束前出现 UI 界面挂起. ...

  7. ios网站,博客

    中文 网站系列 objcio.cncocoachina.comcode4app.com泰然网 博客系列唐巧地球人都知道哈.http://blog.devtang.com/巧哥新出书了,速度入手吧. 虾 ...

  8. Android屏幕适应详解(一)

    一.关于布局适配 1.不要使用绝对布局 2.尽量使用match_parent 而不是fill_parent . 3.能够使用权重的地方尽量使用权重(android:layout_weight) 4.如 ...

  9. How to say "no"?

    How to say "no"?7招教你如何拒绝别人      Do you have a hard time saying no to others? Do you say “y ...

  10. JavaPersistenceWithHibernate第二版笔记-第四章-Mapping persistent classes-001区分entities and value types

    一.介绍 1.这种引用方式不对,但删除时不能级联 要这种引用方式 2.The Bid class could be a problem. In object-oriented modeling, th ...