The Hosted Application Toolkit (HAT) is a non-intrusive automation framework. With HAT, legacy systems (or not !) can be automated at the User Interface level without modifying the application itself. The different components are represented in the diagram below :
HAT Software Factory enables to visually inspect a legacy application right within Visual Studio and to build automation leveraging Windows Workflow Foundation.
Data-Driven Adapters (DDA) are specific to a particular application type and handle the interaction between the automation and the user interface. CCF 2009 provides DDAs for Windows, Web and Java applications. DDAs can also be extended and new DDAs can be developed by third party. This is the purpose of this post.
Let's assume we want to listen to a custom event "TextChanged" on the TextBox of a Calculator. First, extend the WinDataDrivenAdapter class and identify when this event is occurring:
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using Microsoft.Ccf.HostedApplicationToolkit.DataDrivenAdapter;
5: using System.Collections.ObjectModel;
6:
7: namespace ExtendedDDA
8: {
9: public class ExtendedWinDDA : WinDataDrivenAdapter
10: {
11: const string EventNameTextChanged = "TextChanged";
12:
13: public ExtendedWinDDA(System.Xml.XmlDocument initStr, object appObj)
14: : base(initStr, appObj)
15: {
16: this.AccEventListener.AccEventOccurred += new EventHandler<Microsoft.Ccf.HostedApplicationToolkit.DataDrivenAdapter.AccEventArgs>(AccEventListener_AccEventOccurred);
17: }
18:
19: public override ReadOnlyCollection<string> GetAvailableApplicationEvents()
20: {
21: return base.GetAvailableApplicationEvents();
22: }
23:
24: public override ReadOnlyCollection<string> GetAvailableControlEvents()
25: {
26: ReadOnlyCollection<string> baseColl = base.GetAvailableControlEvents();
27: string[] coll = new string[baseColl.Count + 1];
28: coll[0] = EventNameTextChanged;
29: baseColl.CopyTo(coll, 1);
30: return new ReadOnlyCollection<string>(coll);
31: }
32:
33: void AccEventListener_AccEventOccurred(object sender, Microsoft.Ccf.HostedApplicationToolkit.DataDrivenAdapter.AccEventArgs e)
34: {
35:
36: if (e.WinEvent == "EVENT_OBJECT_VALUECHANGE")
37: {
38: System.Diagnostics.Trace.WriteLine("************* AccEventOccurred - RAISED ON TEXT CHANGED **************");
39:
40: // Get the control name
41: string strControlName = KnownControls.GetControlName(e.AutomationId);
42:
43: // for the given control raise the "TextChanged" event
44: base.RaiseEvent(sender, EventNameTextChanged, strControlName, e.Value);
45: }
46: }
47:
48: public override bool RegisterEventListener(string eventName, string controlName, EventHandler<ControlChangedEventArgs> listenerCallback)
49: {
50: bool isSuccessful = false;
51:
52: switch (eventName)
53: {
54: case "TextChanged":
55: isSuccessful = base.RegisterEventListenerBase(eventName, controlName, listenerCallback);
56: if (isSuccessful)
57: {
58: this.StartAccEventListenerIfNecessary();
59: if (((controlName != null) && !this.KnownControls.IsKnown(controlName)) && !this.OperationHandler(OperationType.FindControl, controlName, null).Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase))
60: {
61: throw new DataDrivenAdapterException("DDA0400: Unable to find control on the user interface.");
62: }
63: }
64: return isSuccessful;
65: }
66: base.RegisterEventListener(eventName, controlName, listenerCallback);
67: return isSuccessful;
68: }
69: }
70: }
Declare the Calculator application in the CCF Administration console, update its application definition (initstring) and specify which DDA needs to be exercised on the UI (in this sample, the DDA is deployed in the root directory of the CCF integrated desktop):
1: <initstring>
2: <showmenu />
3: <interopAssembly>
4: <URL>%WINDIR%\System32\calc.exe</URL>
5: <WorkingDirectory></WorkingDirectory>
6: <hostInside />
7: </interopAssembly>
8: <adapter>
9: <URL>Microsoft.Ccf.HostedApplicationToolkit.AutomationHosting</URL>
10: <type>Microsoft.Ccf.HostedApplicationToolkit.AutomationHosting.AutomationAdapter</type>
11: </adapter>
12: <DataDrivenAdapterBindings>
13: <Type>ExtendedDDAs.ExtendedWinDDA, ExtendedDDAs</Type>
14: <Controls>
15: <AccControl name="TextBox" type="editable text">
16: <Path>
17: <FindWindow>
18: <Find>
19: <Class>Edit</Class>
20: </Find>
21: </FindWindow>
22: </Path>
23: </AccControl>
24: </Controls>
25: </DataDrivenAdapterBindings>
26: <displayGroup>MainPanel</displayGroup>
27: <optimumSize x="0" y="0" />
28: <minimumSize x="0" y="0" />
29: </initstring>
Design an automation and listen to the newly created "TextChanged" event using the RegisterActionForEvent HAT activity:
Configure the Automation in the Administration Console and you are done !