Please refer the earlier posts for understanding basics of configuring CCA
1. Customer Care Accelerator – Part 1
2. Customer Care Accelerator – Part 2
3. Customer Care Accelerator – Part 3
Here let us take a scenario where we would be hosting the Bing search page using Hosted Control and then entering a search term in the search box and clicking Enter to see the results.
All this we will configure to happen without any user interaction i.e. we will automate it.
To begin with, add a new class library project to the Agent Desktop Solution.
Add a new User Control class to the project
Add references to the following assemblies
Extend the following classes HostedControl, IDesktopUserActionsConsumer
Source code for the BingHostedControl class. Please check the comments within code to understand things better.
using System;
using System.Windows.Automation;
using System.Windows.Forms;
using Microsoft.Uii.Common.Logging;
using Microsoft.Uii.Csr;
using Microsoft.Uii.Csr.Browser.Web;
using Microsoft.Uii.Desktop.Core;
namespace MyHostedControl
{
/// <summary>
/// Class BingHostedControl
/// </summary>
public partial class BingHostedControl : HostedControl, IDesktopUserActionsConsumer
{
/// <summary>
/// The navigation URL
/// </summary>
private readonly string _navigationUrl;
/// <summary>
/// Represents the web page dom
/// </summary>
private HtmlDocument _doc;
/// <summary>
/// The web browser extension
/// </summary>
private WebBrowserExtended _webBrowser;
/// <summary>
/// The application name being hosted
/// </summary>
private string applicationName;
/// <summary>
/// The bing window after set parent
/// </summary>
private AutomationElement bingWindowAfterSetParent;
/// <summary>
/// The desktop access
/// </summary>
private IDesktopUserActions desktopAccess;
/// <summary>
/// Initializes a new instance of the <see cref="BingHostedControl" /> class.
/// </summary>
public BingHostedControl()
{
InitializeComponent();
}
/// <summary>
/// Initializes a new instance of the <see cref="BingHostedControl" /> class.
/// </summary>
/// <param name="appId">The app id.</param>
/// <param name="appName">Name of the app.</param>
/// <param name="extensionXml">The extension XML.</param>
public BingHostedControl(Guid appId, string appName, string extensionXml)
: base(appId, appName, extensionXml)
{
_navigationUrl = "http://www.bing.com";
applicationName = appName;
InitializeComponent();
}
/// <summary>
/// Sets the desktop user actions access.
/// </summary>
/// <param name="desktopAccess">The desktop access.</param>
public void SetDesktopUserActionsAccess(IDesktopUserActions desktopAccess)
{
this.desktopAccess = desktopAccess;
}
/// <summary>
/// Initializes this instance.
/// </summary>
public override void Initialize()
{
try
{
InitializeBrowser();
base.Initialize();
}
catch (Exception exception)
{
Logging.Error("Bing Hosted App", "Error occurred initializing the Bing control", exception);
throw;
}
}
/// <summary>
/// Initialize Browser
/// </summary>
private void InitializeBrowser()
{
_webBrowser = new WebBrowserExtended(false, true, true);
_webBrowser.Dock = DockStyle.Fill;
_webBrowser.Visible = true;
Controls.Add(_webBrowser);
}
/// <summary>
/// Method is used to perform automation
/// </summary>
/// <param name="args">event arguments</param>
protected override void DoAction(RequestActionEventArgs args)
{
try
{
if (args == null)
{
return;
}
if (args.Action.Equals("default"))
{
_webBrowser.DocumentComplete += WebBrowser_DocumentComplete;
_webBrowser.Navigate(_navigationUrl);
//BingSearch();
}
base.DoAction(args);
}
catch (Exception exception)
{
Logging.Error("Bing Hosted App", "Error occurred in Do Action method of the Bing control", exception);
throw;
}
}
/// <summary>
/// Gets the window UI automation element after setting parent window.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>AutomationElement.</returns>
private AutomationElement GetWindowUIAutomationElementAfterSetParentWindow(object name)
{
try
{
AutomationElementCollection automationElementCollection = AutomationElement.RootElement.FindAll(
TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "AgentDesktop"));
foreach (AutomationElement automationElement in automationElementCollection)
{
if (automationElement.Current.ControlType == ControlType.Window)
{
return automationElement;
}
}
}
catch (Exception ex)
{
Logging.Error("Bing Hosted App", "Error occurred in GetWindowUIAutomationElementAfterSetParentWindow method of the Bing control", ex);
throw;
}
return null;
}
/// <summary>
/// TypeTextBoxSearch functions sets the text box with the search term and inputs the Return key to display search results
/// </summary>
/// <param name="textBoxName">Name of the text box.</param>
/// <param name="textBoxValue">The text box value.</param>
public void TypeTextBoxSearch(string textBoxName, string textBoxValue)
{
try
{
bingWindowAfterSetParent = GetWindowUIAutomationElementAfterSetParentWindow("AgentDesktop");
AutomationElementCollection bingWindowChildItems =
bingWindowAfterSetParent.FindAll(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, textBoxName));
foreach (AutomationElement bingWindowChildItem in bingWindowChildItems)
{
if (bingWindowChildItem.Current.Name.Equals(textBoxName) &&
bingWindowChildItem.Current.ControlType == ControlType.Edit)
{
AutomationElement textBox = bingWindowChildItem;
var valuePattern = (ValuePattern) textBox.GetCurrentPattern(ValuePattern.Pattern);
valuePattern.SetValue(textBoxValue);
Win32HelperMethods.KeyBoardEntry(Win32HelperMethods.KeyboardInput.SpecialKeys.RETURN);
break;
}
}
}
catch (Exception ex)
{
Logging.Error("Bing Hosted App", "Error occurred in TypeTextBoxSearch method of the Bing control", ex);
throw;
}
}
/// <summary>
/// Called when web application is completely loaded
/// </summary>
/// <param name="sender">sender object</param>
/// <param name="e">event arguments</param>
private void WebBrowser_DocumentComplete(object sender, DocumentCompleteEventArgs e)
{
try
{
if (_webBrowser != null &&
(_webBrowser.ReadyState == WebBrowserReadyState.Complete ||
_webBrowser.ReadyState == WebBrowserReadyState.Loaded))
{
_doc = _webBrowser.Document as HtmlDocument;
if (_doc != null)
{
// perform DOM operation as reuquired
//_document = _doc.GetElementsByTagName("FRAME");
}
}
}
catch (InvalidOperationException exception)
{
Close();
Logging.Error("Bing Hosted App", "Error occurred while loading Bing control", exception);
throw;
}
catch (Exception exception)
{
Close();
Logging.Error("Bing Hosted App", "Error occurred while loading Bing control", exception);
throw;
}
}
/// <summary>
/// Desktops the loading complete.
/// </summary>
public void DesktopLoadingComplete()
{
TypeTextBoxSearch("Enter your search term", "CCA R1");
}
/// <summary>
/// Close the web browser process before closing the hosted control
/// </summary>
public override void Close()
{
try
{
if (_webBrowser != null)
{
_webBrowser.CloseBrowser();
_webBrowser.Dispose();
}
base.Close();
}
catch (Exception exception)
{
Logging.Error("Bing Hosted App", "Error occurred closing the Bing control", exception);
throw;
}
}
}
}
Win32Helper Class
public static class Win32HelperMethods
{
/// <summary>
/// Win32 SendInput Method
/// </summary>
/// <param name="numberOfInputs">The number of inputs</param>
/// <param name="input">The input sent</param>
/// <param name="structSize">the size of the input structure</param>
/// <returns>returns exit code</returns>
[DllImport("user32.dll")]
private static extern int SendInput(int numberOfInputs, ref Input input, int structSize);
/// <summary>
/// Win32 GetMessageExtraInfo Method
/// </summary>
/// <returns>Message Pointer</returns>
[DllImport("user32.dll")]
private static extern IntPtr GetMessageExtraInfo();
/// <summary>
/// Win32 SetCursorPosition Method.
/// </summary>
/// <param name="cursorInfo">The cursor position point.</param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool SetCursorPos(System.Drawing.Point cursorInfo);
/// <summary>
/// Win32 GetKeyState Method
/// </summary>
/// <param name="virtKey">The key for which state is retrieved.</param>
/// <returns>state of the key.</returns>
[DllImport("user32.dll")]
private static extern ushort GetKeyState(uint virtKey);
/// <summary>
/// Win32 VkKeyScan Method
/// </summary>
/// <param name="ch">The key to be scanned.</param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern short VkKeyScan(char ch);
/// <summary>
/// The structure for Mouse Input Details
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct MouseInput
{
private int dx;
private int dy;
private int mouseData;
private int dwFlags;
private int time;
private IntPtr dwExtraInfo;
public MouseInput(int dwFlags, IntPtr dwExtraInfo)
{
this.dwFlags = dwFlags;
this.dwExtraInfo = dwExtraInfo;
this.dx = 0;
this.dy = 0;
this.time = 0;
this.mouseData = 0;
}
}
/// <summary>
/// The structure for Hardware Input Details
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct HardwareInput
{
private int uMsg;
private short wParamL;
private short wParamH;
}
/// <summary>
/// The structure for Keyboard Input Details
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardInput
{
private short wVk;
private short wScan;
private KeyUpDown dwFlags;
private int time;
private IntPtr dwExtraInfo;
public KeyboardInput(short wVk, KeyUpDown dwFlags, IntPtr dwExtraInfo)
{
this.wVk = wVk;
this.wScan = 0;
this.dwFlags = dwFlags;
this.time = 0;
this.dwExtraInfo = dwExtraInfo;
}
// Nested Types
public enum KeyUpDown
{
KEYEVENTF_KEYDOWN,
KEYEVENTF_EXTENDEDKEY,
KEYEVENTF_KEYUP
}
public enum SpecialKeys
{
ALT = 0x12,
BACKSPACE = 8,
CAPS = 20,
CONTROL = 0x11,
DELETE = 0x2e,
DOWN = 40,
END = 0x23,
ESCAPE = 0x1b,
F1 = 0x70,
F10 = 0x79,
F11 = 0x7a,
F12 = 0x7b,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 120,
HOME = 0x24,
INSERT = 0x2d,
LEFT = 0x25,
LEFT_ALT = 0xa4,
PAGEDOWN = 0x22,
PAGEUP = 0x21,
PRINT = 0x2a,
PRINTSCREEN = 0x2c,
RETURN = 13,
RIGHT = 0x27,
RIGHT_ALT = 0xa5,
SHIFT = 0x10,
SPACE = 0x20,
TAB = 9,
UP = 0x26
}
}
/// <summary>
/// The structure capturing the User Input Details
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct Input
{
// Fields
[FieldOffset(4)]
private HardwareInput hi;
[FieldOffset(4)]
private KeyboardInput ki;
[FieldOffset(4)]
private MouseInput mi;
[FieldOffset(0)]
private int type;
//// Methods
/// <summary>
/// Returns User Input for Mouse Input
/// </summary>
/// <param name="mouseInput">The mouse input.</param>
/// <returns>The user input structure</returns>
public static Input Mouse(MouseInput mouseInput)
{
Input input = new Input();
input.type = 0;
input.mi = mouseInput;
return input;
}
/// <summary>
/// Returns User Input for Keyboard Input
/// </summary>
/// <param name="keyboardInput">The keyboard input.</param>
/// <returns>The user input structure</returns>
public static Input Keyboard(KeyboardInput keyboardInput)
{
Input input = new Input();
input.type = 1;
input.ki = keyboardInput;
return input;
}
}
/// <summary>
/// The Item Click Method.
/// </summary>
/// <param name="item">The automation element item to be clicked.</param>
internal static void ItemClick(AutomationElement item)
{
System.Windows.Rect rect = item.Current.BoundingRectangle;
System.Windows.Point center = new System.Windows.Point((double)((int)(rect.Left + ((rect.Right - rect.Left) / 2.0))), (double)((int)(rect.Top + ((rect.Bottom - rect.Top) / 2.0))));
if (SetCursorPos(new System.Drawing.Point((int)center.X, (int)center.Y)))
{
MouseLeftKeyPress();
}
}
/// <summary>
/// The Left Mouse Click Method.
/// </summary>
private static void MouseLeftKeyPress()
{
MouseLeftKeyDown();
MouseLeftKeyUp();
}
/// <summary>
/// The Left Mouse Key Down Method.
/// </summary>
private static void MouseLeftKeyDown()
{
Input input = Input.Mouse(new MouseInput(2, GetMessageExtraInfo()));//left key down
SendInput(1, ref input, Marshal.SizeOf(input));
}
/// <summary>
/// The Left Mouse Key Up Method.
/// </summary>
private static void MouseLeftKeyUp()
{
Input input = Input.Mouse(new MouseInput(4, GetMessageExtraInfo()));//left key up
SendInput(1, ref input, Marshal.SizeOf(input));
}
/// <summary>
/// The method to enter text using Keyboard input.
/// </summary>
/// <param name="text">The input text.</param>
internal static void EnterText(string text)
{
KeyPress((short)KeyboardInput.SpecialKeys.HOME, true);
KeyHold((short)KeyboardInput.SpecialKeys.SHIFT, true);
KeyPress((short)KeyboardInput.SpecialKeys.END, true);
KeyLeave((short)KeyboardInput.SpecialKeys.SHIFT, true);
KeyPress((short)KeyboardInput.SpecialKeys.DELETE, true);
if (CapsLockIsOn())//set caps lock to false
{
KeyPress((short)KeyboardInput.SpecialKeys.CAPS, true);
}
foreach (char c in text)
{
short key = VkKeyScan(c);
if (!c.Equals('\r'))
{
if (ShiftKeyIsNeeded(key))
{
SendKeyDown(0x10, false);
}
KeyPress(key, false);
if (ShiftKeyIsNeeded(key))
{
SendKeyUp(0x10, false);
}
}
}
}
/// <summary>
/// Sends a special key entry from keyboard
/// </summary>
/// <param name="key">the special key input</param>
internal static void KeyBoardEntry(KeyboardInput.SpecialKeys key)
{
KeyPress((short)key, true);
}
/// <summary>
/// Checks if Caps Lock is on.
/// </summary>
/// <returns>state if caps lock key.</returns>
private static bool CapsLockIsOn()
{
return (GetKeyState(20) != 0);
}
/// <summary>
/// Checks if Shift Key is needed.
/// </summary>
/// <param name="key">the input key.</param>
/// <returns>result</returns>
private static bool ShiftKeyIsNeeded(short key)
{
return (((key >> 8) & 1) == 1);
}
/// <summary>
/// Keyboard key press Method.
/// </summary>
/// <param name="key">the input key</param>
/// <param name="specialKey">flag if special key.</param>
private static void KeyPress(short key, bool specialKey)
{
SendKeyDown(key, specialKey);
SendKeyUp(key, specialKey);
}
/// <summary>
/// Method to hold the key pressed.
/// </summary>
/// <param name="key">The input key.</param>
/// <param name="specialKey">flag if special key.</param>
private static void KeyHold(short key, bool specialKey)
{
SendKeyDown(key, specialKey);
}
/// <summary>
/// Method to leave the held key.
/// </summary>
/// <param name="key">The input key.</param>
/// <param name="specialKey">flag if special key.</param>
private static void KeyLeave(short key, bool specialKey)
{
SendKeyUp(key, specialKey);
}
/// <summary>
/// Method for key down.
/// </summary>
/// <param name="key">The input key.</param>
/// <param name="specialKey">flag if special key.</param>
private static void SendKeyDown(short key, bool specialKey)
{
KeyboardInput.KeyUpDown keyUpDown = KeyboardInput.KeyUpDown.KEYEVENTF_KEYDOWN;
if (specialKey)
{
keyUpDown |= KeyboardInput.KeyUpDown.KEYEVENTF_EXTENDEDKEY;
}
Input input = Input.Keyboard(new KeyboardInput(key, keyUpDown, GetMessageExtraInfo()));
SendInput(1, ref input, Marshal.SizeOf(typeof(Input)));
}
/// <summary>
/// Method for key up.
/// </summary>
/// <param name="key">The input key.</param>
/// <param name="specialKey">flag if special key.</param>
private static void SendKeyUp(short key, bool specialKey)
{
KeyboardInput.KeyUpDown keyUpDown = KeyboardInput.KeyUpDown.KEYEVENTF_KEYUP;
if (specialKey)
{
keyUpDown |= KeyboardInput.KeyUpDown.KEYEVENTF_EXTENDEDKEY;
}
Input input = Input.Keyboard(new KeyboardInput(key, keyUpDown, GetMessageExtraInfo()));
SendInput(1, ref input, Marshal.SizeOf(typeof(Input)));
}
}
Add the reference to this assembly inside the AgentDesktop project.
Here TypeTextBoxSearch(“Enter your search term”, “CCA R1”); is the function which accepts the Name of the Search Box Control and the search term.
To get the Name of the Search Box Control of bing, we can make use of UiSpy tool
http://msdn.microsoft.com/en-us/library/ms727247.aspx
- Run the UiSpy tool.
- Set Mode as Focus Tracking
- Open Bing
- Put the foucs on the Bing Search Box and get its name and other details from the UISpy window.
Create a new UII Hosted Application record in CRM
Run the Agent Desktop.exe
When we run the Agent Desktop we can see the Bing search page hosted inside the Agent Desktop as a Hosted Control. And the search box being auto populated with CCA R1 text, followed by Click on Enter to display search results.
Hope it helps.