65.9K
CodeProject 正在变化。 阅读更多。
Home

自动化重复任务的关键序列,引用 XML 数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.15/5 (9投票s)

2004 年 1 月 14 日

4分钟阅读

viewsIcon

44251

downloadIcon

1251

自动化重复任务的关键序列。从 XML 文件向表单输入数据。

Sample Image - Automator-Screenshot.jpg

引言

该自动化工具可用于基于按键操作自动化一系列重复任务。任务本身是可配置的(在 ksq 文件中)。该工具还可以引用 XML 文件中的数据来与按键序列一起使用。

历史

我编写此工具是为了将 bug 输入到 bug 跟踪工具中。人们会给我发送 bug 文件,让我输入到他们无法访问的工具中,然后我将使用此工具自动将 XML bug 文件输入到该工具中。所以,如果您看到一些特定于 bug 输入工具的代码/变量,您就知道它们来自哪里。:-)

示例

除了演示之外,还有一些您可以尝试的示例。在文本编辑器中打开 .ksq 文件,其顶部有如何运行示例的说明。

使用演示应用程序

  1. 选择一个关键序列文件,它将是该工具将运行的关键序列集。
  2. 选择应用程序将执行关键序列的进程标题。
  3. 如果您的关键序列不引用任何 XML 文件,请勾选“无需数据文件”。
  4. 如果您的 ksq 文件引用了 XML 文件,请将其添加到数据文件列表中。
  5. 确保目标应用程序正在运行(如果想看到正在发生的事情,请将其并排显示)。
  6. 选择“运行”。

要编写自己的关键序列,请访问网站:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformssendkeysclasstopic.asp

此外,还可以使用以下自定义关键序列:

a) WAIT:n

其中 n 是应用程序在发送下一个按键之前必须暂停的秒数。

例如:WAIT:3 //等待 3 秒

b) REPEAT:REPEATEND:

重复指定次数的中间关键序列。

例如:REPEAT:3
{ENTER}
REPEATEND: //重复 Enter 键 3 次

c) XML:

访问当前 XML 文件中指定的元素值。

例如:XML:ELEMENT1 //如果当前 XML 包含 <ROOT><ELEMENT1>abcd</ELEMENT1></ROOT>,则返回“abcd”的值

d) //

指定注释。任何地方 // 之后的所有内容都是注释。

请参阅示例文件 sample1.ksq 和 sample2.ksq 来澄清上述命令。

代码

我将在此尝试解释代码的一些相关部分。

1. 匹配进程标题

   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;
      .
      .
    }
   }

程序会遍历正在运行的进程列表,并尝试匹配指定的进程标题。

2. 使用 Platform Invoke 激活目标应用程序

 
 [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);

.NET API 中没有直接调用来激活窗口。因此,我使用 DllImport 属性引用相关的 win32 调用,并使用我在第 1 步找到的目标应用程序的进程句柄来调用函数。

3. 使用 XML 引用执行关键序列

  
  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);

我们创建 XML 文件的 XPathDocument 。这将使我们能够使用 XPath 搜索/选择命令轻松查找数据。

4. Repeat stack (重复堆栈)

  
  struct RepeatInfo 
  {
   public int repeatStartPos;
   public int repeatCount;

   public RepeatInfo(int repeatStartPos, int repeatCount)
   {
    this.repeatCount = repeatCount;
    this.repeatStartPos = repeatStartPos;
   }
  }

在实现关键序列时,我不得不扩展序列以包含控制结构。其中一个必不可少的功能是循环。上面的结构代表一个重复循环。每当代码在关键序列中找到 REPEAT: 时,它会将一个重复信息条目添加到堆栈中,并重复该部分关键序列,直到达到指定次数的 REPEATEND:。上面的结构 RepeatInfo 存储重复信息:重复的起始位置和剩余重复次数。

  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 (执行关键序列)

此函数遍历关键序列集。它会检查关键序列的类型——如果它是像 WAITREPEATREPEATEND//XML 这样的特殊关键序列,还是需要输入到目标应用程序的按键。根据类型,程序会提取或分配要发送到应用程序的按键。

   
   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;
    }
   } ...

最后,我们使用 SendKeys 方法将按键发送到应用程序。请注意,此时目标应用程序需要处于活动状态。

  SendKeys.SendWait(sendChars);

后续考虑

程序存在一些问题。此程序假设理想情况——如果出现意外对话框(如错误对话框)或 UI 更改,则程序将无法正常工作。如果在程序运行期间出现另一个窗口处于活动状态,则关键序列将发送到该应用程序;我无法确定这到底是功能还是 bug。

代码是为满足我的需求而编写的,并且在大多数情况下是匆忙完成的。此后我添加了一些非常小的功能,但它们大多是零散的。因此,整个程序的核心本身可能还可以改进。添加更多的控制结构,如 IF-THENGOTO 等,也会很棒。**祝您好运!**

© . All rights reserved.