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. Orchard 候补神器说明

    Orchard学习视频已登录百度传课: http://www.chuanke.com/3027295-124882.html 获取Orchard候补神器请加qq群432158140  ! 候补神器是一 ...

  2. NYOJ-32 组合数 AC 分类: NYOJ 2013-12-30 07:42 189人阅读 评论(0) 收藏

    #include<stdio.h> int num[100]; int pnum(int n,int v); int mv=0; int main(){ int n,v; scanf(&q ...

  3. GS LiveMgr心跳管理类

    struct LiveMgr { private: int m_nCount; ///< 管理数量 std::vector<int> m_vecChannels; ///< 所 ...

  4. Swift-3-字符串和字符

    // Playground - noun: a place where people can play import UIKit var someString = "some string ...

  5. iOS开发之runtime的运用-获取当前网络状态

    之前写过runtime的一些东西,这次通过runtime获取一些苹果官方不想让你拿到的东西,比如,状态栏内部的控件属性.本文将通过runtime带你一步步拿到状态栏中显示网络状态的控件,然后通过监测该 ...

  6. 移动端页面调试工具——UC浏览器开发者版

    在移动页面的开发中,我们很难像PC端那样很方便的调试,网上也有各种各样的调试方式.但在工作中,我主要还是用chorme自带的模拟器来模拟各种移动设备,但是用久了之后发现毕竟是模拟的,与真机调试还是会有 ...

  7. Performance tips

    HTML5 Techniques for Optimizing Mobile Performance Scrolling Performance layout-performance

  8. IE6下div遮盖select的最优解决方案

    a.本节精选html5/css频道里一款IE6下div遮盖select的最优解决方案 原理:利用iframe来遮挡select,再用div来遮挡iframe,就这么简单. 1)首先,建一个div层和i ...

  9. ”sql Server2008 应用程序无法启动,因为应用程序的并行配置不正确。 找不到从属程序集。“C:\windows\SysWOW64\DTSPipelinePerf100.dll”的激活上下文生成失败“的解决方案

    一:控制面板->管理工具->事件查看器->windows日志->应用程序   查看错误原因: 二:在其他机子上拷贝一个DTSWizard.exe.config文件替换本机上已经 ...

  10. c3p0 --1

    # # This file is detritus from various testing attempts  # the values below may change, and often do ...