The last time we used windows API and xml configuration to simulate the keyboard and mouse, we did not handle logical errors or exceptions in the simulation (for example, after the simulation is enabled, some forms or business logic errors of the current program do not need to be prompted, and the simulation program must exit normally to ensure data security .), In addition, the Label-like controls in the form cannot capture the form handle or traverse the sub-form handle to obtain the corresponding information. Therefore, after a period of research and rectification, the ErrorTo attribute and the Error node are added to some configuration nodes. When a logical Error or exception occurs, the nodes directly jump to the corresponding node to run the corresponding normal exit step.
I set the ErrorTo attribute to be set on all nodes, but if the child node does not exist, the setting of the parent node will be used. The configuration changes are as follows:
1 <Program Name = "kp" Value = "\ BIN \ kp.exe" ErrorTo = "No2"> SOFTWARE \ aerospace information \ counterfeit invoicing \ path </Program>
For the above changes, we need to modify the AbstractStep and make some changes to the default Init initialization method. The code changes are as follows:
1 /// <summary>
2 // Initialization
3 /// </summary>
4 // <param name = "step"> previous step </param>
5 // <param name = "node"> node </param>
6 public virtual void Init (AbstractStep step, XElement node)
7 {
8 // the original code is omitted
9 // Add code
10 this. ErrorTo = this. GetAttribute ("ErrorTo ");
11 if (string. IsNullOrEmpty (this. ErrorTo ))
12 {
13 this. ErrorTo = this. ParentStep. ErrorTo;
14}
15}
As for the newly added Error node, the main function is to configure a normal exit step, because we may open multiple forms, so we need to close them in sequence, or cancel the storage of some data. In addition to the main nodes, other sub-steps are the original configurations, which are roughly as follows:
1 <Error Name = "No1">
2 <Form Class = "TInvQueryForm" Caption = "select invoice number query">
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 = "VAT anti-counterfeiting Tax Control System invoicing subsystem">
10 <ClickTo> 504,330 </ClickTo>
11 </Form>
12 </Form>
13 </Error>
When adding a node, you need to make some judgment on the XML parsing configuration. The changes are as follows:
1 /// <summary>
2 // obtain the subnode
3 /// </summary>
4 /// <param name = "parentStep"> step object of the parent node </param>
5 /// <param name = "childList"> subnode list </param>
6 /// <param name = "nowList"> List of subnode steps </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 // the original code is omitted
20}
21}
22}
The basic configuration and resolution are almost the same, and the rest is about the modifications that need to be made during the internal operation. Because we need to handle logical errors and abnormal processes, it is relatively simple to handle exceptions. We only need to use Try in the Tick event of Timer... catch can be captured. If the logic is incorrect, it involves a wide range of aspects, such: the corresponding form handle cannot be captured, the retrieved handle value does not exist in the comparison data, and the handle is unavailable. Because we have set an enumeration to determine the status of the Step --- EnumStepState, therefore, we need to add two enumerated values, for example, LogicError and Exception, and then refer to the possible errors. Therefore, we need to modify the step of the derived class: fromStep, ChildStep, IfStep, EachStep, and ProgramStep.
FormStep: A logical error occurs when a form does not exist after the waiting time is exceeded.
ChildStep: similar to FormStep, the range is found in the subform of the form.
IfStep: This is complicated because If will trigger another simulation device. Therefore, we need to set the normal end of the If simulation device to the start of the original simulation device, and set the configuration of the original simulation device to the If simulation device.
EachStep: similar to IfStep, only the returned values in the cyclic step need to be returned with one more status for logical errors.
ProgramStep: determines the program to be started. If yes, the logic is incorrect.
At this point, we will almost modify the error. Next, we will use the capture memory method for values similar to Label. Because it is only a separate test, it has not been added to the original function. In this case, we need to use another windows api, kernel32.dll. The main api is as follows:
1 /// <summary>
2 // close the handle Resource
3 /// </summary>
4 /// <param name = "processWnd"> process form handle </param>
5 [DllImport ("kernel32.dll")]
6 public static extern void CloseHandle (IntPtr processWnd );
7
8 /// <summary>
9 // open the process handle Resource
10 /// </summary>
11 /// <param name = "wndDesiredAccess"> the form requires an access restriction. The maximum permission is 0x1F0FFF. </param>
12 /// <param name = "bInheritHandle"> whether to inherit the processing? </param>
13 /// <param name = "processId"> process 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 // read the value of the memory specified by the process into the buffer zone
20 /// </summary>
21 /// <param name = "processWnd"> process form handle </param>
22 /// <param name = "addressWnd"> memory address handle </param>
23 // <param name = "bufferWnd"> buffer address handle </param>
24 /// <param name = "bufferSize"> buffer size </param>
25 /// <param name = "bufferReadWnd"> handle of the number of bytes read by the buffer </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 // write the value to the memory specified by the Process
32 /// </summary>
33 // <param name = "processWnd"> process form handle </param>
34 /// <param name = "addressWnd"> memory address handle </param>
35 /// <param name = "values"> value array </param>
36 /// <param name = "bufferSize"> buffer size </param>
37 // <param name = "bufferWriteWnd"> handle of the number of bytes written to the buffer </param>
38 /// <returns> </returns>
39 [DllImportAttribute ("kernel32.dll", EntryPoint = "WriteProcessMemory")]
40 public static extern bool WriteProcessMemory (IntPtr processWnd, IntPtr addressWnd, int [] values, int bufferSize, IntPtr bufferWriteWnd );
In addition, we need to use our own methods, because I personally prefer to use the extension method, so there will be more extension methods. The Code is as follows:
1 /// <summary>
2 // obtain the process based on the process name
3 /// </summary>
4 // <param name = "name"> process name </param>
5 /// <returns> </returns>
6 public static Process GetProcessByName (string name)
7 {
8 Process process = null;
9 try
10 {
11 Process [] processes = Process. GetProcessesByName (name );
12 process = processes [0];
13}
14 catch (Exception)
15 {
16 throw new Exception (string. Format ("the process name does not exist {0 }. ", Name ));
17}
18 return process;
19}
20
21 /// <summary>
22 // obtain the process based on the process title
23 /// </summary>
24 // <param name = "title"> process title </param>
25 /// <returns> </returns>
26 public static Process GetProcessByTitle (string title)
27 {
28 Process process = null;
29 try
30 {
31 Process [] processes = Process. GetProcesses ();
32 foreach (var p in processes)
33 {
34 if (p. MainWindowTitle. IndexOf (title )! =-1)
35 {
36 process = p;
37}
38}
39}
40 catch (Exception)
41 {
42 throw new Exception (string. Format ("the process title does not exist: {0 }. ", Title ));
43}
44 return process;
45}
46
47 -------------------------------------------------- Extension Method ----------------------------------------------
48 /// <summary>
49 // read the memory value of the process
50 /// </summary>
51 // <param name = "p"> Process </param>
52 // <param name = "address"> memory address </param>
53 // <returns> </returns>
54 public static int ReadProcessMemoryValue (this Process p, int address)
55 {
56 // set the buffer, open the process handle resource, read the memory, and close the Process Handle Resource
57 byte [] buffer = new byte [4];
58 IntPtr byteAddress = Marshal. UnsafeAddrOfPinnedArrayElement (buffer, 0 );
59 IntPtr processWnd = Memory. OpenProcess (0x1F0FFF, false, p. Id );
60 Memory. ReadProcessMemory (processWnd, (IntPtr) address, byteAddress, 4, IntPtr. Zero );
61 Memory. CloseHandle (processWnd );
62 return Marshal. ReadInt32 (byteAddress );
63}
64
65 // <summary>
66 // write the value to the memory specified by the Process
67 /// </summary>
68 // <param name = "baseAddress"> </param>
69 // <param name = "processName"> </param>
70 // <param name = "value"> </param>
71 public static void WriteProcessMemoryValue (this Process p, int address, int value)
72 {
73 // open the process handle resource, write the value to the specified memory address, and release the Process Handle Resource
74 IntPtr processWnd = Memory. OpenProcess (0x1F0FFF, false, p. Id); // 0x1F0FFF maximum permission
75 Memory. WriteProcessMemory (processWnd, (IntPtr) address, new int [] {value}, 4, IntPtr. Zero );
76 Memory. CloseHandle (processWnd );
77}
After we have the method, we also need to use a tool to help-CheatEngine, which can help us filter the required values and find the memory address. The Code is as follows:
1 var p = memory.getprocpolicytitle(this.txt ProcessTitle. Text );
2 var address = 0x014C14DC;
3 var value = p. ReadProcessMemoryValue (address );
Because of the time, there are not many learnings. It's almost time to come. Thank you. I was very sorry because I forgot to provide the source code last time. This time I made up the code and wrote it poorly. I'm sorry, haha. Source code here! <------ Remember to download
Author ahl5esoft