C#模拟键盘鼠标之二:错误跳转以及内存查看
上一次我们利用windows API以及xml配置来模拟键盘鼠标,但是并没有对模拟中出现的逻辑错误或者异常进行处理(例如:模拟开启之后,当前程序的部分窗体或者业务逻辑上出现错误,这时我们不只要提示相应的错误,而且必须要让模拟程序将当前正常退出,以保证数据安全。),而且在窗体内部类似Label的控件也没有办法通过抓取窗体句柄或者遍历子窗体句柄来获取相应的信息,因此在一段时间的研究和整改之后,对于一些配置节点增加了ErrorTo属性以及新增Error节点,在出现逻辑错误或者异常的时候,直接跳转到对应的节点运行对应的正常退出步骤。
我设定是可将ErrorTo的属性设置在所有节点之上,但是如果子节点不存在的话,则会使用父节点的设置。配置更改如下:
1 <Program Name="kp" Value="\BIN\kp.exe" ErrorTo="No2">SOFTWARE\航天信息\防伪开票\路径</Program>
关于以上的变更,我们需要对AbstractStep进行修改,将默认的Init初始化方法做一些改变,代码变化如下:
1 ///<summary>
2 /// 初始化
3 ///</summary>
4 ///<param name="step">上一步</param>
5 ///<param name="node">节点</param>
6 public virtual void Init(AbstractStep step, XElement node)
7 {
8 //原始代码省略
9 //新增代码
10 this.ErrorTo = this.GetAttribute("ErrorTo");
11 if (string.IsNullOrEmpty(this.ErrorTo))
12 {
13 this.ErrorTo = this.ParentStep.ErrorTo;
14 }
15 }
至于新增的Error节点,则主要的作用是做一个正常退出的步骤配置,因为我们可能开启多个窗体,因此需要依次进行关闭,或者对于某些数据进行存储撤消等等。除了主要节点以外,其他的子步骤还是原来的那些配置,大致如下:
1 <Error Name="No1">
2 <Form Class="TInvQueryForm" Caption="选择发票号码查询">
3 <KeyBoard>{ESC}</KeyBoard>
4 <KeyBoard>{ESC}</KeyBoard>
5 <KeyBoard>{ESC}</KeyBoard>
6 <Form Class="TMainForm" Caption="增值税防伪税控系统防伪开票子系统">
7 <ClickTo>-5,5</ClickTo>
8 </Form>
9 <Form Class="TFaceForm" Caption="增值税防伪税控系统开票子系统">
10 <ClickTo>504,330</ClickTo>
11 </Form>
12 </Form>
13 </Error>
新增节点的话,则需要对解析XML配置做一些判断,改动如下:
1 ///<summary>
2 /// 获取子节点步骤
3 ///</summary>
4 ///<param name="parentStep">父节点步骤对象</param>
5 ///<param name="childList">子节点列表</param>
6 ///<param name="nowList">子节点步骤列表</param>
7 void GetChildStep(AbstractStep parentStep, IEnumerable<XElement> childList, IList<AbstractStep> nowList)
8 {
9 foreach (var node in childList)
10 {
11 if (node.Name.LocalName == "Error")
12 {
13 var type = node.Attribute("Name").Value;
14 this.dicError[type] = new List<AbstractStep>();
15 this.GetChildStep(parentStep, node.Elements(), this.dicError[type]);
16 }
17 else
18 {
19 //原始代码省略
20 }
21 }
22 }
基本的配置和解析就差不多了,剩下的就是关于内部运行时需要做的一些修改了。因为我们需要对逻辑错误和异常进程处理,异常的话还是比较简单的,只需要在Timer的Tick事件内,使用Try...Catch进行捕捉就可以了,逻辑错误的话,因为涉及到的方面比较广泛,例如:抓取不到对应的窗体句柄、抓取的句柄值在比对数据中不存在、句柄不可用等等问题,由于我们前期已经设置了一个枚举用于判断步骤的状态---EnumStepState,因此我们需要增加2个枚举值,例如:LogicError、Exception,然后再参照可能会出现的错误情况,因此需要修改的步骤派生类为:FromStep、ChildStep、IfStep、EachStep以及ProgramStep。
FormStep:判断超过等待时间后,不存在窗体时,出现逻辑错误。
ChildStep:与FormStep类似,只是范围是在窗体的子窗体中寻找。
IfStep:这个就比较复杂了,因为If会引发另一个模拟装置。因此我们要将If的模拟装置的正常结束设定为原模拟装置的启动,并且将原本的模拟装置的配置设定与If的模拟装置上。
EachStep:类似IfStep,只是循环步骤中的返回值,需要多一个对于逻辑错误的状态返回。
ProgramStep:对于需要启动的程序进行判断,如果存在,则逻辑错误。
到此,我们对于错误的修改也就差不多了,接下来,我们要对类似Label的取值,使用抓取内存的方式来使用。因为只是单独的测试,因此还未加入到原本的功能里面。这时我们需要用到另外一个windows api----kernel32.dll,主要的api如下:
1 ///<summary>
2 /// 关闭句柄资源
3 ///</summary>
4 ///<param name="processWnd">进程窗体句柄</param>
5 [DllImport("kernel32.dll")]
6 public static extern void CloseHandle(IntPtr processWnd);
7
8 ///<summary>
9 /// 打开进程句柄资源
10 ///</summary>
11 ///<param name="wndDesiredAccess">窗体需要访问权限值,最高权限为0x1F0FFF</param>
12 ///<param name="bInheritHandle">是否继承处理</param>
13 ///<param name="processId">进程ID</param>
14 ///<returns></returns>
15 [DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
16 public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int processId);
17
18 ///<summary>
19 /// 将进程指定内存的值读入缓冲区
20 ///</summary>
21 ///<param name="processWnd">进程窗体句柄</param>
22 ///<param name="addressWnd">内存地址句柄</param>
23 ///<param name="bufferWnd">缓冲区地址句柄</param>
24 ///<param name="bufferSize">缓冲区大小</param>
25 ///<param name="bufferReadWnd">缓冲区读取的字节数句柄</param>
26 ///<returns></returns>
27 [DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")]
28 public static extern bool ReadProcessMemory(IntPtr processWnd, IntPtr addressWnd, IntPtr bufferWnd, int bufferSize, IntPtr bufferReadWnd);
29
30 ///<summary>
31 /// 将值写入进程指定的内存中
32 ///</summary>
33 ///<param name="processWnd">进程窗体句柄</param>
34 ///<param name="ad
补充:软件开发 , C# ,