Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Automate key sequences for repetitive tasks, reference XML data

0.00/5 (No votes)
13 Jan 2004 2  
Automate key sequences for repetitive tasks. Enter data into forms from XML files.

Sample Image - Automator-Screenshot.jpg

Introduction

The 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.

History

I 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. :-)

Samples

Along 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

  1. Choose a key sequence file which will be the set of key sequences that the tool will run.
  2. Choose the title of a process that the application will execute the key sequnces on.
  3. Check "No data files required" if your key sequence is not referencing any XML file.
  4. If your ksq file is referncing an XML file then add it to the data files list.
  5. Make sure the target application is running (keep it side by side if you want to see what is happening).
  6. Choose Run.

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) WAIT:n

Where n is the number of seconds that the application must pause before sending next keys.

Eg. WAIT:3 //to wait for 3 seconds

b) REPEAT: and REPEATEND:

To repeat the key sequences in between the specified number of times.

Eg. REPEAT:3
{ENTER}
REPEATEND: //to repeat the enter key 3 times

c)XML:

To access the specified element value in the current xml file.

Eg. XML:ELEMENT1 //will return the value "abcd" if the current xml contained <ROOT><ELEMENT1>abcd</ELEMENT1></ROOT>

d)//

Specifies a comment. Everything after a // anywhere is a comment.

See the sample files sample1.ksq and sample2.ksq to clarify the above commands.

Code

I'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 DllImport attribute and call the function with the process handle of the target application I found in step 1.

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 XPathDocument of the xml file. This will allow us to use XPath search/select commands to find data easily.

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 REPEAT: in the key sequence, it adds a repeat info entry into a stack and repeats that part of the key sequence until REPEATEND: for the specified number of times. The above structure, RepeatInfo, stores the repeat information: start position of repeat and the number of times left to repeat respectively.

  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. ExecuteKeySequence

This function loops through the set of key sequences. It checks the type of key sequence - if it is a special key sequence like WAIT, REPEAT, REPEATEND, // or XML or whether they are keys to be entered on the target application. Depending on the type, the program extracts or assigns keys that are to be sent to the application.

   
   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 ahead

There 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 IF-THEN, GOTO etc. would be cool too. Best of luck!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here