|
|||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThe automator tool may be used to automate a set of repetitive tasks based on key presses. The tasks themselves are configurable (in ksq files). The tool can also reference XML files for data to use with the key sequences. HistoryI wrote this tool to enter bugs into a bug tracking tool. People would send me bug files to enter them into a tool that they could not access and I would use this to automatically enter the xml bug files into the tool. So if you see some code/variables specific to a bug entry tool, you know where it came from. :-) SamplesAlong with the demo are samples that u can try out. Open the .ksq file in some text editor and it has instructions at the top on how to run the sample. Using the demo application
For writing your own key sequences check the site: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformssendkeysclasstopic.asp Additionally the following custom key sequences can be used a) Where Eg. b) To repeat the key sequences in between the specified number of times. Eg. c) To access the specified element value in the current xml file. Eg. d) Specifies a comment. Everything after a See the sample files sample1.ksq and sample2.ksq to clarify the above commands. CodeI'll attempt to explain some relevant parts of the code here. 1. Matching the process title: this.processWindowHandle = IntPtr.Zero;
Process[] plist = Process.GetProcesses();
foreach (Process p in plist)
{
if (!this.processWindowHandle.Equals(IntPtr.Zero))
{
break;
}
switch(this.titlePositionComboBox.SelectedItem.ToString().ToUpper())
{
case "BEGIN":
if (p.MainWindowTitle.StartsWith(this.targetAppTitleTextBox.Text))
{
this.processWindowHandle = p.MainWindowHandle;
}
break;
.
.
}
}
The program loops through the list of running processes and tries to match the title of the process given. 2. Platform Invoke to activate target application
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd
);
[DllImport("User32.dll")]
private static extern bool SetActiveWindow(IntPtr hWnd
);
SetForegroundWindow(this.processWindowHandle);
SetActiveWindow(this.processWindowHandle);
There is no direct call in the .NET api to activate a window. Hence I reference the relevant win32 calls using the 3. Executing key sequence with xml reference
XPathNavigator nav;
try
{
XPathDocument xpDoc = new XPathDocument(xmlFileName);
nav = xpDoc.CreateNavigator();
}
catch(Exception ex)
{
bufFileItem.SubItems[1].Text = "Error! in xml document.";
continue;
}
ExecuteKeySequence(nav);
We create an 4. Repeat stack
struct RepeatInfo
{
public int repeatStartPos;
public int repeatCount;
public RepeatInfo(int repeatStartPos, int repeatCount)
{
this.repeatCount = repeatCount;
this.repeatStartPos = repeatStartPos;
}
}
While implementing the key sequence I had to extend the sequences to include control structures. One of the essentials was looping. The structure above represents a repeat loop. Whenever the code finds a if (s.StartsWith("REPEAT:"))
{
int repeatCount = Convert.ToInt32(s.Replace("REPEAT:", "").Trim());
repeatStack.Push(new RepeatInfo(cmdIndex, repeatCount));
continue;
}
else if (s.StartsWith("REPEATEND:"))
{
if (repeatStack.Count >= 1)
{
RepeatInfo lastRepeatInfo = (RepeatInfo)repeatStack.Pop();
if (lastRepeatInfo.repeatCount > 1)
{
cmdIndex = lastRepeatInfo.repeatStartPos;
lastRepeatInfo.repeatCount--;
repeatStack.Push(lastRepeatInfo);
}
}
continue;
}
5. ExecuteKeySequenceThis function loops through the set of key sequences. It checks the type of key sequence - if it is a special key sequence like
while (cmdIndex+1 < this.sendKeysSequence.Length)
{
cmdIndex++;
string s = this.sendKeysSequence[cmdIndex];
string sendChars = "";
if (s.StartsWith("XML:"))
{
if (nav == null)
{
throw new Exception("Error. XML navigator was null. " +
"No file associated with XML: command "+ s);
}
string xmlName = s.Replace("XML:", "");
XPathNodeIterator iterator = nav.Select("//" + xmlName);
if (iterator.MoveNext())
{
sendChars = iterator.Current.Value;
foreach(string specialChar in this.specialCharsInSendKeys)
{
sendChars = sendChars.Replace(specialChar, "{"+specialChar+"}");
}
}
}
else if (s.StartsWith("WAIT:"))
{
int sleepTime = 5000;
sleepTime = Convert.ToInt32(s.Replace("WAIT:", ""))*1000;
Thread.Sleep(sleepTime);
continue;
}
} ...
Finally we send the keys to the application using the SendKeys method. Note that the target application needs to be active at this point. SendKeys.SendWait(sendChars);
Further aheadThere are quite a few issues in the program. This program assumes ideal case - if there are unexpected dialogs (like error dialogs) or changes in UI, then this will not work. If another window becomes active while the program is running, the key sequences will be sent to that application; I can't quite decide if that is a feature or a bug. The code was written to suit my needs and for the most part it was quickly thrown together. I added a few itty bitty features thereafter but they were mostly make up. So the core of the whole program itself could possibly be improved. Adding more control structures like
|
||||||||||||||||||||||||||||||||||||||||||||