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






4.15/5 (9投票s)
2004 年 1 月 14 日
4分钟阅读

44251

1251
自动化重复任务的关键序列。从 XML 文件向表单输入数据。
引言
该自动化工具可用于基于按键操作自动化一系列重复任务。任务本身是可配置的(在 ksq 文件中)。该工具还可以引用 XML 文件中的数据来与按键序列一起使用。
历史
我编写此工具是为了将 bug 输入到 bug 跟踪工具中。人们会给我发送 bug 文件,让我输入到他们无法访问的工具中,然后我将使用此工具自动将 XML bug 文件输入到该工具中。所以,如果您看到一些特定于 bug 输入工具的代码/变量,您就知道它们来自哪里。:-)
示例
除了演示之外,还有一些您可以尝试的示例。在文本编辑器中打开 .ksq 文件,其顶部有如何运行示例的说明。
使用演示应用程序
- 选择一个关键序列文件,它将是该工具将运行的关键序列集。
- 选择应用程序将执行关键序列的进程标题。
- 如果您的关键序列不引用任何 XML 文件,请勾选“无需数据文件”。
- 如果您的 ksq 文件引用了 XML 文件,请将其添加到数据文件列表中。
- 确保目标应用程序正在运行(如果想看到正在发生的事情,请将其并排显示)。
- 选择“运行”。
要编写自己的关键序列,请访问网站: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 (执行关键序列)
此函数遍历关键序列集。它会检查关键序列的类型——如果它是像 WAIT
、REPEAT
、REPEATEND
、//
或 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-
THEN
、GOTO
等,也会很棒。**祝您好运!**