ft-scout 9.0.4 → 9.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.firebase/hosting.d2Vi.cache +8 -7
- package/AUTONOMOUS_FEATURES.md +192 -0
- package/README.md +67 -35
- package/bin/src/commands/agent.d.ts.map +1 -1
- package/bin/src/commands/agent.js +21 -8
- package/bin/src/commands/agent.js.map +1 -1
- package/bin/src/commands/login.d.ts.map +1 -1
- package/bin/src/commands/login.js +3 -1
- package/bin/src/commands/login.js.map +1 -1
- package/bin/src/engine/actionVerifier.d.ts.map +1 -1
- package/bin/src/engine/actionVerifier.js +9 -1
- package/bin/src/engine/actionVerifier.js.map +1 -1
- package/bin/src/engine/agentEngine.d.ts.map +1 -1
- package/bin/src/engine/agentEngine.js +319 -13
- package/bin/src/engine/agentEngine.js.map +1 -1
- package/bin/src/engine/browser/browserController.d.ts.map +1 -1
- package/bin/src/engine/browser/browserController.js +53 -13
- package/bin/src/engine/browser/browserController.js.map +1 -1
- package/bin/src/engine/computer/computerController.d.ts.map +1 -1
- package/bin/src/engine/computer/computerController.js +564 -296
- package/bin/src/engine/computer/computerController.js.map +1 -1
- package/bin/src/engine/computer/computerDiagnostic.d.ts.map +1 -0
- package/bin/src/engine/computer/computerDiagnostic.js +71 -0
- package/bin/src/engine/computer/computerDiagnostic.js.map +1 -0
- package/bin/src/engine/computer/index.d.ts.map +1 -1
- package/bin/src/engine/computer/index.js +1 -0
- package/bin/src/engine/computer/index.js.map +1 -1
- package/bin/src/engine/computer/mockComputer.d.ts.map +1 -1
- package/bin/src/engine/computer/mockComputer.js +104 -8
- package/bin/src/engine/computer/mockComputer.js.map +1 -1
- package/bin/src/engine/computer/scoutDriver.cs +1161 -0
- package/bin/src/engine/computer/scout_driver.exe +0 -0
- package/bin/src/engine/runtime/agentRuntime.d.ts.map +1 -1
- package/bin/src/engine/runtime/agentRuntime.js +375 -20
- package/bin/src/engine/runtime/agentRuntime.js.map +1 -1
- package/bin/src/engine/safety.d.ts.map +1 -1
- package/bin/src/engine/safety.js +17 -6
- package/bin/src/engine/safety.js.map +1 -1
- package/bin/src/engine/toolRegistry.d.ts.map +1 -1
- package/bin/src/engine/toolRegistry.js +209 -0
- package/bin/src/engine/toolRegistry.js.map +1 -1
- package/bin/src/engine/workspace/agentWorkspace.d.ts.map +1 -1
- package/bin/src/engine/workspace/agentWorkspace.js +21 -0
- package/bin/src/engine/workspace/agentWorkspace.js.map +1 -1
- package/bin/src/index.js +5 -4
- package/bin/src/index.js.map +1 -1
- package/bin/src/utils/auth.d.ts.map +1 -1
- package/bin/src/utils/auth.js +14 -1
- package/bin/src/utils/auth.js.map +1 -1
- package/bin/src/utils/branding.js +1 -1
- package/bin/src/utils/holoseneAuth.d.ts.map +1 -1
- package/bin/src/utils/holoseneAuth.js +169 -41
- package/bin/src/utils/holoseneAuth.js.map +1 -1
- package/firebase.json +8 -0
- package/package.json +1 -1
- package/web/app.js +3 -3
- package/web/styles.css +2 -2
|
@@ -0,0 +1,1161 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.IO;
|
|
3
|
+
using System.Text;
|
|
4
|
+
using System.Threading;
|
|
5
|
+
using System.Drawing;
|
|
6
|
+
using System.Drawing.Imaging;
|
|
7
|
+
using System.Windows.Forms;
|
|
8
|
+
using System.Runtime.InteropServices;
|
|
9
|
+
using System.Diagnostics;
|
|
10
|
+
using System.Collections.Generic;
|
|
11
|
+
|
|
12
|
+
namespace Scout.ComputerControl
|
|
13
|
+
{
|
|
14
|
+
public class ScoutDriver
|
|
15
|
+
{
|
|
16
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
17
|
+
public static extern IntPtr OpenWindowStation(string lpszWinSta, bool fInherit, uint dwDesiredAccess);
|
|
18
|
+
|
|
19
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
20
|
+
public static extern bool SetProcessWindowStation(IntPtr hWinSta);
|
|
21
|
+
|
|
22
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
23
|
+
public static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, uint dwDesiredAccess);
|
|
24
|
+
|
|
25
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
26
|
+
public static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
|
|
27
|
+
|
|
28
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
29
|
+
public static extern bool SetThreadDesktop(IntPtr hDesktop);
|
|
30
|
+
|
|
31
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
32
|
+
public static extern bool CloseDesktop(IntPtr hDesktop);
|
|
33
|
+
|
|
34
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
35
|
+
public static extern bool GetCursorPos(out POINT lpPoint);
|
|
36
|
+
|
|
37
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
38
|
+
public static extern bool SetCursorPos(int X, int Y);
|
|
39
|
+
|
|
40
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
41
|
+
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
|
|
42
|
+
|
|
43
|
+
[DllImport("user32.dll")]
|
|
44
|
+
public static extern int GetSystemMetrics(int nIndex);
|
|
45
|
+
|
|
46
|
+
[DllImport("user32.dll")]
|
|
47
|
+
public static extern bool SetProcessDPIAware();
|
|
48
|
+
|
|
49
|
+
[DllImport("user32.dll")]
|
|
50
|
+
public static extern IntPtr GetForegroundWindow();
|
|
51
|
+
|
|
52
|
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|
53
|
+
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
|
54
|
+
|
|
55
|
+
[DllImport("user32.dll")]
|
|
56
|
+
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
57
|
+
|
|
58
|
+
[DllImport("user32.dll")]
|
|
59
|
+
public static extern bool BringWindowToTop(IntPtr hWnd);
|
|
60
|
+
|
|
61
|
+
[DllImport("user32.dll")]
|
|
62
|
+
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
63
|
+
|
|
64
|
+
[DllImport("user32.dll")]
|
|
65
|
+
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
66
|
+
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
67
|
+
|
|
68
|
+
[DllImport("user32.dll")]
|
|
69
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
70
|
+
public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
71
|
+
|
|
72
|
+
[DllImport("user32.dll")]
|
|
73
|
+
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
74
|
+
|
|
75
|
+
[DllImport("user32.dll")]
|
|
76
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
77
|
+
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
78
|
+
|
|
79
|
+
[DllImport("user32.dll")]
|
|
80
|
+
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
|
|
81
|
+
|
|
82
|
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|
83
|
+
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
84
|
+
|
|
85
|
+
public const uint WM_CLOSE = 0x0010;
|
|
86
|
+
public const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
|
|
87
|
+
public const uint KEYEVENTF_KEYUP = 0x0002;
|
|
88
|
+
|
|
89
|
+
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|
90
|
+
public static extern bool CreateProcess(
|
|
91
|
+
string lpApplicationName,
|
|
92
|
+
string lpCommandLine,
|
|
93
|
+
IntPtr lpProcessAttributes,
|
|
94
|
+
IntPtr lpThreadAttributes,
|
|
95
|
+
bool bInheritHandles,
|
|
96
|
+
uint dwCreationFlags,
|
|
97
|
+
IntPtr lpEnvironment,
|
|
98
|
+
string lpCurrentDirectory,
|
|
99
|
+
ref STARTUPINFO lpStartupInfo,
|
|
100
|
+
out PROCESS_INFORMATION lpProcessInformation
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
public const uint MOUSEEVENTF_MOVE = 0x0001;
|
|
104
|
+
public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
|
|
105
|
+
public const uint MOUSEEVENTF_LEFTUP = 0x0004;
|
|
106
|
+
public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
|
|
107
|
+
public const uint MOUSEEVENTF_RIGHTUP = 0x0010;
|
|
108
|
+
public const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020;
|
|
109
|
+
public const uint MOUSEEVENTF_MIDDLEUP = 0x0040;
|
|
110
|
+
public const uint MOUSEEVENTF_WHEEL = 0x0800;
|
|
111
|
+
|
|
112
|
+
[DllImport("user32.dll")]
|
|
113
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
114
|
+
public static extern bool IsIconic(IntPtr hWnd);
|
|
115
|
+
|
|
116
|
+
[DllImport("user32.dll")]
|
|
117
|
+
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
|
118
|
+
|
|
119
|
+
[DllImport("kernel32.dll")]
|
|
120
|
+
public static extern uint GetCurrentThreadId();
|
|
121
|
+
|
|
122
|
+
public const int SM_CXSCREEN = 0;
|
|
123
|
+
public const int SM_CYSCREEN = 1;
|
|
124
|
+
public const int SM_CXVIRTUALSCREEN = 78;
|
|
125
|
+
public const int SM_CYVIRTUALSCREEN = 79;
|
|
126
|
+
public const int SW_SHOW = 5;
|
|
127
|
+
public const int SW_RESTORE = 9;
|
|
128
|
+
|
|
129
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
130
|
+
public struct RECT
|
|
131
|
+
{
|
|
132
|
+
public int Left;
|
|
133
|
+
public int Top;
|
|
134
|
+
public int Right;
|
|
135
|
+
public int Bottom;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
139
|
+
public struct STARTUPINFO
|
|
140
|
+
{
|
|
141
|
+
public int cb;
|
|
142
|
+
public string lpReserved;
|
|
143
|
+
public string lpDesktop;
|
|
144
|
+
public string lpTitle;
|
|
145
|
+
public int dwX;
|
|
146
|
+
public int dwY;
|
|
147
|
+
public int dwXSize;
|
|
148
|
+
public int dwYSize;
|
|
149
|
+
public int dwXCountChars;
|
|
150
|
+
public int dwYCountChars;
|
|
151
|
+
public int dwFillAttribute;
|
|
152
|
+
public int dwFlags;
|
|
153
|
+
public short wShowWindow;
|
|
154
|
+
public short cbReserved2;
|
|
155
|
+
public IntPtr lpReserved2;
|
|
156
|
+
public IntPtr hStdInput;
|
|
157
|
+
public IntPtr hStdOutput;
|
|
158
|
+
public IntPtr hStdError;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
162
|
+
public struct PROCESS_INFORMATION
|
|
163
|
+
{
|
|
164
|
+
public IntPtr hProcess;
|
|
165
|
+
public IntPtr hThread;
|
|
166
|
+
public int dwProcessId;
|
|
167
|
+
public int dwThreadId;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
171
|
+
public struct POINT
|
|
172
|
+
{
|
|
173
|
+
public int X;
|
|
174
|
+
public int Y;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public static void AttachInputDesktop()
|
|
178
|
+
{
|
|
179
|
+
try
|
|
180
|
+
{
|
|
181
|
+
SetProcessDPIAware();
|
|
182
|
+
IntPtr hwinsta = OpenWindowStation("WinSta0", false, 0x10000000);
|
|
183
|
+
if (hwinsta != IntPtr.Zero)
|
|
184
|
+
{
|
|
185
|
+
SetProcessWindowStation(hwinsta);
|
|
186
|
+
}
|
|
187
|
+
IntPtr hdesk = OpenInputDesktop(0, false, 0x10000000);
|
|
188
|
+
if (hdesk == IntPtr.Zero)
|
|
189
|
+
{
|
|
190
|
+
hdesk = OpenDesktop("Default", 0, false, 0x10000000);
|
|
191
|
+
}
|
|
192
|
+
if (hdesk != IntPtr.Zero)
|
|
193
|
+
{
|
|
194
|
+
SetThreadDesktop(hdesk);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch { }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public static POINT GetPosition()
|
|
201
|
+
{
|
|
202
|
+
POINT pt = new POINT();
|
|
203
|
+
GetCursorPos(out pt);
|
|
204
|
+
return pt;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public static bool Move(int x, int y)
|
|
208
|
+
{
|
|
209
|
+
SetCursorPos(x, y);
|
|
210
|
+
Thread.Sleep(20);
|
|
211
|
+
POINT actual = GetPosition();
|
|
212
|
+
return (Math.Abs(actual.X - x) <= 5 && Math.Abs(actual.Y - y) <= 5);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public static bool Click(string button, int x, int y, int count)
|
|
216
|
+
{
|
|
217
|
+
Move(x, y);
|
|
218
|
+
Thread.Sleep(30);
|
|
219
|
+
|
|
220
|
+
uint downFlag = MOUSEEVENTF_LEFTDOWN;
|
|
221
|
+
uint upFlag = MOUSEEVENTF_LEFTUP;
|
|
222
|
+
|
|
223
|
+
if (button == "right")
|
|
224
|
+
{
|
|
225
|
+
downFlag = MOUSEEVENTF_RIGHTDOWN;
|
|
226
|
+
upFlag = MOUSEEVENTF_RIGHTUP;
|
|
227
|
+
}
|
|
228
|
+
else if (button == "middle")
|
|
229
|
+
{
|
|
230
|
+
downFlag = MOUSEEVENTF_MIDDLEDOWN;
|
|
231
|
+
upFlag = MOUSEEVENTF_MIDDLEUP;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
for (int i = 0; i < count; i++)
|
|
235
|
+
{
|
|
236
|
+
mouse_event(downFlag, 0, 0, 0, 0);
|
|
237
|
+
Thread.Sleep(40);
|
|
238
|
+
mouse_event(upFlag, 0, 0, 0, 0);
|
|
239
|
+
if (count > 1 && i < count - 1)
|
|
240
|
+
{
|
|
241
|
+
Thread.Sleep(70);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public static bool MouseDown(string button, int x, int y)
|
|
248
|
+
{
|
|
249
|
+
if (x >= 0 && y >= 0)
|
|
250
|
+
{
|
|
251
|
+
Move(x, y);
|
|
252
|
+
Thread.Sleep(30);
|
|
253
|
+
}
|
|
254
|
+
uint downFlag = MOUSEEVENTF_LEFTDOWN;
|
|
255
|
+
if (button == "right") downFlag = MOUSEEVENTF_RIGHTDOWN;
|
|
256
|
+
else if (button == "middle") downFlag = MOUSEEVENTF_MIDDLEDOWN;
|
|
257
|
+
|
|
258
|
+
mouse_event(downFlag, 0, 0, 0, 0);
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
public static bool MouseUp(string button, int x, int y)
|
|
263
|
+
{
|
|
264
|
+
if (x >= 0 && y >= 0)
|
|
265
|
+
{
|
|
266
|
+
Move(x, y);
|
|
267
|
+
Thread.Sleep(30);
|
|
268
|
+
}
|
|
269
|
+
uint upFlag = MOUSEEVENTF_LEFTUP;
|
|
270
|
+
if (button == "right") upFlag = MOUSEEVENTF_RIGHTUP;
|
|
271
|
+
else if (button == "middle") upFlag = MOUSEEVENTF_MIDDLEUP;
|
|
272
|
+
|
|
273
|
+
mouse_event(upFlag, 0, 0, 0, 0);
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public static bool Drag(int startX, int startY, int endX, int endY)
|
|
278
|
+
{
|
|
279
|
+
Move(startX, startY);
|
|
280
|
+
Thread.Sleep(50);
|
|
281
|
+
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
|
|
282
|
+
Thread.Sleep(50);
|
|
283
|
+
|
|
284
|
+
int steps = 15;
|
|
285
|
+
for (int i = 1; i <= steps; i++)
|
|
286
|
+
{
|
|
287
|
+
int curX = startX + (int)((endX - startX) * ((double)i / steps));
|
|
288
|
+
int curY = startY + (int)((endY - startY) * ((double)i / steps));
|
|
289
|
+
SetCursorPos(curX, curY);
|
|
290
|
+
Thread.Sleep(15);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
Move(endX, endY);
|
|
294
|
+
Thread.Sleep(50);
|
|
295
|
+
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
|
|
296
|
+
Thread.Sleep(30);
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
public static bool Scroll(int deltaX, int deltaY)
|
|
301
|
+
{
|
|
302
|
+
if (deltaY != 0)
|
|
303
|
+
{
|
|
304
|
+
int amount = -deltaY * 120;
|
|
305
|
+
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, (uint)amount, 0);
|
|
306
|
+
}
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
public static string GetActiveWindowTitle()
|
|
311
|
+
{
|
|
312
|
+
try
|
|
313
|
+
{
|
|
314
|
+
IntPtr hwnd = GetForegroundWindow();
|
|
315
|
+
if (hwnd == IntPtr.Zero) return "Desktop";
|
|
316
|
+
StringBuilder sb = new StringBuilder(512);
|
|
317
|
+
GetWindowText(hwnd, sb, sb.Capacity);
|
|
318
|
+
string title = sb.ToString().Trim();
|
|
319
|
+
return string.IsNullOrEmpty(title) ? "Desktop" : title;
|
|
320
|
+
}
|
|
321
|
+
catch
|
|
322
|
+
{
|
|
323
|
+
return "Desktop";
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public static List<string> GetVisibleWindows()
|
|
328
|
+
{
|
|
329
|
+
List<string> list = new List<string>();
|
|
330
|
+
try
|
|
331
|
+
{
|
|
332
|
+
EnumWindows((hWnd, lParam) =>
|
|
333
|
+
{
|
|
334
|
+
if (IsWindowVisible(hWnd))
|
|
335
|
+
{
|
|
336
|
+
RECT rect;
|
|
337
|
+
if (GetWindowRect(hWnd, out rect))
|
|
338
|
+
{
|
|
339
|
+
int w = rect.Right - rect.Left;
|
|
340
|
+
int h = rect.Bottom - rect.Top;
|
|
341
|
+
if (w > 30 && h > 30)
|
|
342
|
+
{
|
|
343
|
+
StringBuilder sb = new StringBuilder(512);
|
|
344
|
+
GetWindowText(hWnd, sb, sb.Capacity);
|
|
345
|
+
string t = sb.ToString().Trim();
|
|
346
|
+
if (!string.IsNullOrEmpty(t) && t != "Default IME" && t != "MSCTFIME UI")
|
|
347
|
+
{
|
|
348
|
+
if (!list.Contains(t)) list.Add(t);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return true;
|
|
354
|
+
}, IntPtr.Zero);
|
|
355
|
+
}
|
|
356
|
+
catch { }
|
|
357
|
+
return list;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
public static bool FocusWindow(string titleOrApp)
|
|
361
|
+
{
|
|
362
|
+
try
|
|
363
|
+
{
|
|
364
|
+
string target = titleOrApp.ToLower().Trim();
|
|
365
|
+
IntPtr foundHwnd = IntPtr.Zero;
|
|
366
|
+
|
|
367
|
+
EnumWindows((hWnd, lParam) =>
|
|
368
|
+
{
|
|
369
|
+
if (IsWindowVisible(hWnd))
|
|
370
|
+
{
|
|
371
|
+
StringBuilder sb = new StringBuilder(512);
|
|
372
|
+
GetWindowText(hWnd, sb, sb.Capacity);
|
|
373
|
+
string t = sb.ToString().Trim().ToLower();
|
|
374
|
+
|
|
375
|
+
uint pid;
|
|
376
|
+
GetWindowThreadProcessId(hWnd, out pid);
|
|
377
|
+
string pName = "";
|
|
378
|
+
try { pName = Process.GetProcessById((int)pid).ProcessName.ToLower(); } catch { }
|
|
379
|
+
|
|
380
|
+
if (t.Contains(target) || pName.Contains(target))
|
|
381
|
+
{
|
|
382
|
+
foundHwnd = hWnd;
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return true;
|
|
387
|
+
}, IntPtr.Zero);
|
|
388
|
+
|
|
389
|
+
if (foundHwnd != IntPtr.Zero)
|
|
390
|
+
{
|
|
391
|
+
if (IsIconic(foundHwnd))
|
|
392
|
+
{
|
|
393
|
+
ShowWindow(foundHwnd, SW_RESTORE);
|
|
394
|
+
}
|
|
395
|
+
else
|
|
396
|
+
{
|
|
397
|
+
ShowWindow(foundHwnd, SW_SHOW);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
IntPtr fgHwnd = GetForegroundWindow();
|
|
401
|
+
uint fgPid;
|
|
402
|
+
uint fgThreadId = GetWindowThreadProcessId(fgHwnd, out fgPid);
|
|
403
|
+
uint curThreadId = GetCurrentThreadId();
|
|
404
|
+
if (fgThreadId != 0 && fgThreadId != curThreadId)
|
|
405
|
+
{
|
|
406
|
+
AttachThreadInput(curThreadId, fgThreadId, true);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
keybd_event(0x12, 0, 0, UIntPtr.Zero);
|
|
410
|
+
keybd_event(0x12, 0, 0x0002, UIntPtr.Zero);
|
|
411
|
+
SetForegroundWindow(foundHwnd);
|
|
412
|
+
BringWindowToTop(foundHwnd);
|
|
413
|
+
|
|
414
|
+
if (fgThreadId != 0 && fgThreadId != curThreadId)
|
|
415
|
+
{
|
|
416
|
+
AttachThreadInput(curThreadId, fgThreadId, false);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
Thread.Sleep(150);
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch { }
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
public static RECT? GetWindowBounds(string titleOrApp)
|
|
428
|
+
{
|
|
429
|
+
try
|
|
430
|
+
{
|
|
431
|
+
string target = titleOrApp.ToLower().Trim();
|
|
432
|
+
IntPtr foundHwnd = IntPtr.Zero;
|
|
433
|
+
|
|
434
|
+
EnumWindows((hWnd, lParam) =>
|
|
435
|
+
{
|
|
436
|
+
if (IsWindowVisible(hWnd))
|
|
437
|
+
{
|
|
438
|
+
StringBuilder sb = new StringBuilder(512);
|
|
439
|
+
GetWindowText(hWnd, sb, sb.Capacity);
|
|
440
|
+
string t = sb.ToString().Trim().ToLower();
|
|
441
|
+
|
|
442
|
+
uint pid;
|
|
443
|
+
GetWindowThreadProcessId(hWnd, out pid);
|
|
444
|
+
string pName = "";
|
|
445
|
+
try { pName = Process.GetProcessById((int)pid).ProcessName.ToLower(); } catch { }
|
|
446
|
+
|
|
447
|
+
if (t.Contains(target) || pName.Contains(target))
|
|
448
|
+
{
|
|
449
|
+
foundHwnd = hWnd;
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return true;
|
|
454
|
+
}, IntPtr.Zero);
|
|
455
|
+
|
|
456
|
+
if (foundHwnd != IntPtr.Zero)
|
|
457
|
+
{
|
|
458
|
+
RECT rect;
|
|
459
|
+
if (GetWindowRect(foundHwnd, out rect))
|
|
460
|
+
{
|
|
461
|
+
return rect;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch { }
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
public static bool LaunchApp(string appName, out int pid)
|
|
470
|
+
{
|
|
471
|
+
pid = 0;
|
|
472
|
+
try
|
|
473
|
+
{
|
|
474
|
+
STARTUPINFO si = new STARTUPINFO();
|
|
475
|
+
si.cb = Marshal.SizeOf(si);
|
|
476
|
+
si.lpDesktop = @"WinSta0\Default";
|
|
477
|
+
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
|
|
478
|
+
|
|
479
|
+
string cmd = appName;
|
|
480
|
+
if (!cmd.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
|
|
481
|
+
{
|
|
482
|
+
cmd += ".exe";
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
bool ok = CreateProcess(null, cmd, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi);
|
|
486
|
+
if (ok)
|
|
487
|
+
{
|
|
488
|
+
pid = pi.dwProcessId;
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Fallback to Process.Start with UseShellExecute
|
|
493
|
+
try
|
|
494
|
+
{
|
|
495
|
+
ProcessStartInfo psi = new ProcessStartInfo(appName);
|
|
496
|
+
psi.UseShellExecute = true;
|
|
497
|
+
Process p = Process.Start(psi);
|
|
498
|
+
if (p != null)
|
|
499
|
+
{
|
|
500
|
+
pid = p.Id;
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
catch { }
|
|
505
|
+
}
|
|
506
|
+
catch { }
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
public static string Screenshot(string savePath)
|
|
511
|
+
{
|
|
512
|
+
int width = GetSystemMetrics(SM_CXSCREEN);
|
|
513
|
+
int height = GetSystemMetrics(SM_CYSCREEN);
|
|
514
|
+
if (width <= 0) width = 1920;
|
|
515
|
+
if (height <= 0) height = 1080;
|
|
516
|
+
|
|
517
|
+
string dir = Path.GetDirectoryName(savePath);
|
|
518
|
+
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
|
519
|
+
{
|
|
520
|
+
Directory.CreateDirectory(dir);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
using (Bitmap bmp = new Bitmap(width, height))
|
|
524
|
+
{
|
|
525
|
+
using (Graphics g = Graphics.FromImage(bmp))
|
|
526
|
+
{
|
|
527
|
+
g.CopyFromScreen(0, 0, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
|
|
528
|
+
}
|
|
529
|
+
bmp.Save(savePath, ImageFormat.Png);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return string.Format("{{\"success\":true,\"action\":\"screenshot\",\"width\":{0},\"height\":{1},\"path\":{2},\"activeWindow\":{3}}}",
|
|
533
|
+
width, height, JsonEscape(savePath), JsonEscape(GetActiveWindowTitle()));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
public static bool TypeText(string text)
|
|
537
|
+
{
|
|
538
|
+
if (string.IsNullOrEmpty(text)) return true;
|
|
539
|
+
try
|
|
540
|
+
{
|
|
541
|
+
SendKeys.SendWait(EscapeForSendKeys(text));
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
catch
|
|
545
|
+
{
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
public static byte GetVirtualKey(string key)
|
|
551
|
+
{
|
|
552
|
+
if (string.IsNullOrEmpty(key)) return 0;
|
|
553
|
+
string k = key.ToLower().Trim();
|
|
554
|
+
|
|
555
|
+
// Modifiers
|
|
556
|
+
if (k == "ctrl" || k == "control" || k == "lctrl" || k == "rctrl") return 0x11; // VK_CONTROL
|
|
557
|
+
if (k == "alt" || k == "alternate" || k == "menu" || k == "lalt" || k == "ralt") return 0x12; // VK_MENU
|
|
558
|
+
if (k == "shift" || k == "lshift" || k == "rshift") return 0x10; // VK_SHIFT
|
|
559
|
+
if (k == "win" || k == "windows" || k == "super" || k == "lwin" || k == "cmd" || k == "command") return 0x5B; // VK_LWIN
|
|
560
|
+
if (k == "rwin") return 0x5C; // VK_RWIN
|
|
561
|
+
|
|
562
|
+
// Function keys F1 - F24
|
|
563
|
+
if (k == "f1") return 0x70;
|
|
564
|
+
if (k == "f2") return 0x71;
|
|
565
|
+
if (k == "f3") return 0x72;
|
|
566
|
+
if (k == "f4") return 0x73;
|
|
567
|
+
if (k == "f5") return 0x74;
|
|
568
|
+
if (k == "f6") return 0x75;
|
|
569
|
+
if (k == "f7") return 0x76;
|
|
570
|
+
if (k == "f8") return 0x77;
|
|
571
|
+
if (k == "f9") return 0x78;
|
|
572
|
+
if (k == "f10") return 0x79;
|
|
573
|
+
if (k == "f11") return 0x7A;
|
|
574
|
+
if (k == "f12") return 0x7B;
|
|
575
|
+
if (k == "f13") return 0x7C;
|
|
576
|
+
if (k == "f14") return 0x7D;
|
|
577
|
+
if (k == "f15") return 0x7E;
|
|
578
|
+
if (k == "f16") return 0x7F;
|
|
579
|
+
|
|
580
|
+
// Common special keys
|
|
581
|
+
if (k == "enter" || k == "return") return 0x0D; // VK_RETURN
|
|
582
|
+
if (k == "esc" || k == "escape") return 0x1B; // VK_ESCAPE
|
|
583
|
+
if (k == "tab") return 0x09; // VK_TAB
|
|
584
|
+
if (k == "space" || k == "spacebar") return 0x20; // VK_SPACE
|
|
585
|
+
if (k == "backspace" || k == "back" || k == "bksp") return 0x08; // VK_BACK
|
|
586
|
+
if (k == "del" || k == "delete") return 0x2E; // VK_DELETE
|
|
587
|
+
if (k == "ins" || k == "insert") return 0x2D; // VK_INSERT
|
|
588
|
+
if (k == "home") return 0x24; // VK_HOME
|
|
589
|
+
if (k == "end") return 0x23; // VK_END
|
|
590
|
+
if (k == "pageup" || k == "pgup") return 0x21; // VK_PRIOR
|
|
591
|
+
if (k == "pagedown" || k == "pgdn") return 0x22; // VK_NEXT
|
|
592
|
+
if (k == "left") return 0x25; // VK_LEFT
|
|
593
|
+
if (k == "up") return 0x26; // VK_UP
|
|
594
|
+
if (k == "right") return 0x27; // VK_RIGHT
|
|
595
|
+
if (k == "down") return 0x28; // VK_DOWN
|
|
596
|
+
if (k == "prtscn" || k == "printscreen" || k == "snapshot") return 0x2C; // VK_SNAPSHOT
|
|
597
|
+
|
|
598
|
+
// Single letters a-z
|
|
599
|
+
if (k.Length == 1 && k[0] >= 'a' && k[0] <= 'z')
|
|
600
|
+
{
|
|
601
|
+
return (byte)('A' + (k[0] - 'a'));
|
|
602
|
+
}
|
|
603
|
+
// Single digits 0-9
|
|
604
|
+
if (k.Length == 1 && k[0] >= '0' && k[0] <= '9')
|
|
605
|
+
{
|
|
606
|
+
return (byte)k[0];
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Punctuation and symbols
|
|
610
|
+
if (k == "+" || k == "plus" || k == "add") return 0xBB; // VK_OEM_PLUS
|
|
611
|
+
if (k == "-" || k == "minus" || k == "subtract") return 0xBD; // VK_OEM_MINUS
|
|
612
|
+
if (k == "=" || k == "equal" || k == "equals") return 0xBB;
|
|
613
|
+
if (k == "," || k == "comma") return 0xBC; // VK_OEM_COMMA
|
|
614
|
+
if (k == "." || k == "period" || k == "dot") return 0xBE; // VK_OEM_PERIOD
|
|
615
|
+
if (k == "/" || k == "slash") return 0xBF; // VK_OEM_2
|
|
616
|
+
if (k == "\\" || k == "backslash") return 0xDC; // VK_OEM_5
|
|
617
|
+
if (k == ";" || k == "semicolon") return 0xBA; // VK_OEM_1
|
|
618
|
+
if (k == "'" || k == "quote" || k == "singlequote") return 0xDE; // VK_OEM_7
|
|
619
|
+
if (k == "[" || k == "openbracket") return 0xDB; // VK_OEM_4
|
|
620
|
+
if (k == "]" || k == "closebracket") return 0xDD; // VK_OEM_6
|
|
621
|
+
if (k == "`" || k == "backtick") return 0xC0; // VK_OEM_3
|
|
622
|
+
|
|
623
|
+
return 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
public static string FormatKeyForSendKeys(string key)
|
|
627
|
+
{
|
|
628
|
+
if (string.IsNullOrEmpty(key)) return "";
|
|
629
|
+
string k = key.ToLower().Trim();
|
|
630
|
+
if (k == "enter" || k == "return") return "{ENTER}";
|
|
631
|
+
if (k == "tab") return "{TAB}";
|
|
632
|
+
if (k == "esc" || k == "escape") return "{ESC}";
|
|
633
|
+
if (k == "backspace" || k == "back" || k == "bksp") return "{BACKSPACE}";
|
|
634
|
+
if (k == "del" || k == "delete") return "{DEL}";
|
|
635
|
+
if (k == "ins" || k == "insert") return "{INS}";
|
|
636
|
+
if (k == "up") return "{UP}";
|
|
637
|
+
if (k == "down") return "{DOWN}";
|
|
638
|
+
if (k == "left") return "{LEFT}";
|
|
639
|
+
if (k == "right") return "{RIGHT}";
|
|
640
|
+
if (k == "space" || k == "spacebar") return " ";
|
|
641
|
+
if (k == "home") return "{HOME}";
|
|
642
|
+
if (k == "end") return "{END}";
|
|
643
|
+
if (k == "pageup" || k == "pgup") return "{PGUP}";
|
|
644
|
+
if (k == "pagedown" || k == "pgdn") return "{PGDN}";
|
|
645
|
+
|
|
646
|
+
if (k.Length >= 2 && k[0] == 'f')
|
|
647
|
+
{
|
|
648
|
+
int fnum;
|
|
649
|
+
if (int.TryParse(k.Substring(1), out fnum) && fnum >= 1 && fnum <= 24)
|
|
650
|
+
{
|
|
651
|
+
return "{" + k.ToUpper() + "}";
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (k.Length == 1)
|
|
656
|
+
{
|
|
657
|
+
char c = k[0];
|
|
658
|
+
if (c == '+' || c == '^' || c == '%' || c == '~' || c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']')
|
|
659
|
+
{
|
|
660
|
+
return "{" + c + "}";
|
|
661
|
+
}
|
|
662
|
+
return k;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return "{" + key.ToUpper() + "}";
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
public static bool KeyPress(string key)
|
|
669
|
+
{
|
|
670
|
+
byte vk = GetVirtualKey(key);
|
|
671
|
+
if (vk != 0)
|
|
672
|
+
{
|
|
673
|
+
keybd_event(vk, 0, 0, UIntPtr.Zero);
|
|
674
|
+
Thread.Sleep(20);
|
|
675
|
+
keybd_event(vk, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
try
|
|
679
|
+
{
|
|
680
|
+
string sendStr = FormatKeyForSendKeys(key);
|
|
681
|
+
SendKeys.SendWait(sendStr);
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
catch
|
|
685
|
+
{
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
public static bool Hotkey(string[] keys)
|
|
691
|
+
{
|
|
692
|
+
if (keys == null || keys.Length == 0) return false;
|
|
693
|
+
|
|
694
|
+
List<byte> vkList = new List<byte>();
|
|
695
|
+
foreach (string k in keys)
|
|
696
|
+
{
|
|
697
|
+
byte vk = GetVirtualKey(k);
|
|
698
|
+
if (vk != 0) vkList.Add(vk);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (vkList.Count == keys.Length)
|
|
702
|
+
{
|
|
703
|
+
// Native virtual key press via keybd_event
|
|
704
|
+
for (int i = 0; i < vkList.Count; i++)
|
|
705
|
+
{
|
|
706
|
+
keybd_event(vkList[i], 0, 0, UIntPtr.Zero);
|
|
707
|
+
Thread.Sleep(20);
|
|
708
|
+
}
|
|
709
|
+
Thread.Sleep(40);
|
|
710
|
+
for (int i = vkList.Count - 1; i >= 0; i--)
|
|
711
|
+
{
|
|
712
|
+
keybd_event(vkList[i], 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
|
|
713
|
+
Thread.Sleep(20);
|
|
714
|
+
}
|
|
715
|
+
return true;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Fallback to SendKeys with proper formatting
|
|
719
|
+
try
|
|
720
|
+
{
|
|
721
|
+
string prefix = "";
|
|
722
|
+
string mainKey = "";
|
|
723
|
+
foreach (string k in keys)
|
|
724
|
+
{
|
|
725
|
+
string lk = k.ToLower().Trim();
|
|
726
|
+
if (lk == "ctrl" || lk == "control") prefix += "^";
|
|
727
|
+
else if (lk == "alt" || lk == "alternate") prefix += "%";
|
|
728
|
+
else if (lk == "shift") prefix += "+";
|
|
729
|
+
else mainKey = k;
|
|
730
|
+
}
|
|
731
|
+
string sendStr = prefix + FormatKeyForSendKeys(mainKey);
|
|
732
|
+
SendKeys.SendWait(sendStr);
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
catch
|
|
736
|
+
{
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
public static bool CloseWindow(string titleOrApp)
|
|
742
|
+
{
|
|
743
|
+
try
|
|
744
|
+
{
|
|
745
|
+
IntPtr targetHwnd = IntPtr.Zero;
|
|
746
|
+
if (string.IsNullOrEmpty(titleOrApp))
|
|
747
|
+
{
|
|
748
|
+
targetHwnd = GetForegroundWindow();
|
|
749
|
+
}
|
|
750
|
+
else
|
|
751
|
+
{
|
|
752
|
+
string target = titleOrApp.ToLower().Trim();
|
|
753
|
+
EnumWindows((hWnd, lParam) =>
|
|
754
|
+
{
|
|
755
|
+
if (IsWindowVisible(hWnd))
|
|
756
|
+
{
|
|
757
|
+
StringBuilder sb = new StringBuilder(256);
|
|
758
|
+
GetWindowText(hWnd, sb, 256);
|
|
759
|
+
string title = sb.ToString();
|
|
760
|
+
if (!string.IsNullOrEmpty(title) && title.ToLower().Contains(target))
|
|
761
|
+
{
|
|
762
|
+
targetHwnd = hWnd;
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return true;
|
|
767
|
+
}, IntPtr.Zero);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (targetHwnd != IntPtr.Zero)
|
|
771
|
+
{
|
|
772
|
+
PostMessage(targetHwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
catch { }
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
public static string EscapeForSendKeys(string text)
|
|
781
|
+
{
|
|
782
|
+
if (string.IsNullOrEmpty(text)) return "";
|
|
783
|
+
StringBuilder sb = new StringBuilder();
|
|
784
|
+
foreach (char c in text)
|
|
785
|
+
{
|
|
786
|
+
if (c == '\r') continue;
|
|
787
|
+
if (c == '\n')
|
|
788
|
+
{
|
|
789
|
+
sb.Append("{ENTER}");
|
|
790
|
+
}
|
|
791
|
+
else if (c == '\t')
|
|
792
|
+
{
|
|
793
|
+
sb.Append("{TAB}");
|
|
794
|
+
}
|
|
795
|
+
else if (c == '+' || c == '^' || c == '%' || c == '~' || c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']')
|
|
796
|
+
{
|
|
797
|
+
sb.Append("{" + c + "}");
|
|
798
|
+
}
|
|
799
|
+
else
|
|
800
|
+
{
|
|
801
|
+
sb.Append(c);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return sb.ToString();
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
public static string JsonEscape(string text)
|
|
808
|
+
{
|
|
809
|
+
if (text == null) return "null";
|
|
810
|
+
StringBuilder sb = new StringBuilder();
|
|
811
|
+
sb.Append("\"");
|
|
812
|
+
foreach (char c in text)
|
|
813
|
+
{
|
|
814
|
+
if (c == '\\') sb.Append("\\\\");
|
|
815
|
+
else if (c == '\"') sb.Append("\\\"");
|
|
816
|
+
else if (c == '\n') sb.Append("\\n");
|
|
817
|
+
else if (c == '\r') sb.Append("\\r");
|
|
818
|
+
else if (c == '\t') sb.Append("\\t");
|
|
819
|
+
else if (c < ' ') sb.Append(string.Format("\\u{0:x4}", (int)c));
|
|
820
|
+
else sb.Append(c);
|
|
821
|
+
}
|
|
822
|
+
sb.Append("\"");
|
|
823
|
+
return sb.ToString();
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
public static string ExecuteAction(string action, string[] args)
|
|
827
|
+
{
|
|
828
|
+
try
|
|
829
|
+
{
|
|
830
|
+
string act = action.ToLower().Trim();
|
|
831
|
+
|
|
832
|
+
if (act == "move")
|
|
833
|
+
{
|
|
834
|
+
int x = int.Parse(args[0]);
|
|
835
|
+
int y = int.Parse(args[1]);
|
|
836
|
+
bool ok = Move(x, y);
|
|
837
|
+
POINT pos = GetPosition();
|
|
838
|
+
return string.Format("{{\"success\":{0},\"action\":\"move\",\"coordinates\":{{\"x\":{1},\"y\":{2}}},\"activeWindow\":{3}}}",
|
|
839
|
+
ok ? "true" : "false", pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
if (act == "click" || act == "left_click")
|
|
843
|
+
{
|
|
844
|
+
int x = int.Parse(args[0]);
|
|
845
|
+
int y = int.Parse(args[1]);
|
|
846
|
+
string btn = (args.Length > 2 && !string.IsNullOrEmpty(args[2])) ? args[2] : "left";
|
|
847
|
+
bool ok = Click(btn, x, y, 1);
|
|
848
|
+
POINT pos = GetPosition();
|
|
849
|
+
return string.Format("{{\"success\":{0},\"action\":\"click\",\"button\":\"{1}\",\"coordinates\":{{\"x\":{2},\"y\":{3}}},\"activeWindow\":{4}}}",
|
|
850
|
+
ok ? "true" : "false", btn, pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (act == "right_click")
|
|
854
|
+
{
|
|
855
|
+
int x = int.Parse(args[0]);
|
|
856
|
+
int y = int.Parse(args[1]);
|
|
857
|
+
bool ok = Click("right", x, y, 1);
|
|
858
|
+
POINT pos = GetPosition();
|
|
859
|
+
return string.Format("{{\"success\":{0},\"action\":\"right_click\",\"button\":\"right\",\"coordinates\":{{\"x\":{1},\"y\":{2}}},\"activeWindow\":{3}}}",
|
|
860
|
+
ok ? "true" : "false", pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (act == "double_click")
|
|
864
|
+
{
|
|
865
|
+
int x = int.Parse(args[0]);
|
|
866
|
+
int y = int.Parse(args[1]);
|
|
867
|
+
bool ok = Click("left", x, y, 2);
|
|
868
|
+
POINT pos = GetPosition();
|
|
869
|
+
return string.Format("{{\"success\":{0},\"action\":\"double_click\",\"coordinates\":{{\"x\":{1},\"y\":{2}}},\"activeWindow\":{3}}}",
|
|
870
|
+
ok ? "true" : "false", pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (act == "mouse_down")
|
|
874
|
+
{
|
|
875
|
+
int x = args.Length > 0 ? int.Parse(args[0]) : -1;
|
|
876
|
+
int y = args.Length > 1 ? int.Parse(args[1]) : -1;
|
|
877
|
+
string btn = (args.Length > 2 && !string.IsNullOrEmpty(args[2])) ? args[2] : "left";
|
|
878
|
+
bool ok = MouseDown(btn, x, y);
|
|
879
|
+
POINT pos = GetPosition();
|
|
880
|
+
return string.Format("{{\"success\":{0},\"action\":\"mouse_down\",\"button\":\"{1}\",\"coordinates\":{{\"x\":{2},\"y\":{3}}}}}",
|
|
881
|
+
ok ? "true" : "false", btn, pos.X, pos.Y);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (act == "mouse_up")
|
|
885
|
+
{
|
|
886
|
+
int x = args.Length > 0 ? int.Parse(args[0]) : -1;
|
|
887
|
+
int y = args.Length > 1 ? int.Parse(args[1]) : -1;
|
|
888
|
+
string btn = (args.Length > 2 && !string.IsNullOrEmpty(args[2])) ? args[2] : "left";
|
|
889
|
+
bool ok = MouseUp(btn, x, y);
|
|
890
|
+
POINT pos = GetPosition();
|
|
891
|
+
return string.Format("{{\"success\":{0},\"action\":\"mouse_up\",\"button\":\"{1}\",\"coordinates\":{{\"x\":{2},\"y\":{3}}}}}",
|
|
892
|
+
ok ? "true" : "false", btn, pos.X, pos.Y);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (act == "drag")
|
|
896
|
+
{
|
|
897
|
+
int sx = int.Parse(args[0]);
|
|
898
|
+
int sy = int.Parse(args[1]);
|
|
899
|
+
int ex = int.Parse(args[2]);
|
|
900
|
+
int ey = int.Parse(args[3]);
|
|
901
|
+
bool ok = Drag(sx, sy, ex, ey);
|
|
902
|
+
POINT pos = GetPosition();
|
|
903
|
+
return string.Format("{{\"success\":{0},\"action\":\"drag\",\"from\":{{\"x\":{1},\"y\":{2}}},\"to\":{{\"x\":{3},\"y\":{4}}},\"coordinates\":{{\"x\":{5},\"y\":{6}}}}}",
|
|
904
|
+
ok ? "true" : "false", sx, sy, ex, ey, pos.X, pos.Y);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (act == "scroll")
|
|
908
|
+
{
|
|
909
|
+
int dx = args.Length > 0 ? int.Parse(args[0]) : 0;
|
|
910
|
+
int dy = args.Length > 1 ? int.Parse(args[1]) : 0;
|
|
911
|
+
bool ok = Scroll(dx, dy);
|
|
912
|
+
return string.Format("{{\"success\":{0},\"action\":\"scroll\",\"deltaX\":{1},\"deltaY\":{2}}}",
|
|
913
|
+
ok ? "true" : "false", dx, dy);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (act == "position")
|
|
917
|
+
{
|
|
918
|
+
POINT pos = GetPosition();
|
|
919
|
+
return string.Format("{{\"success\":true,\"action\":\"position\",\"coordinates\":{{\"x\":{0},\"y\":{1}}}}}",
|
|
920
|
+
pos.X, pos.Y);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
if (act == "screen_info")
|
|
924
|
+
{
|
|
925
|
+
int width = GetSystemMetrics(SM_CXSCREEN);
|
|
926
|
+
int height = GetSystemMetrics(SM_CYSCREEN);
|
|
927
|
+
POINT pos = GetPosition();
|
|
928
|
+
return string.Format("{{\"success\":true,\"width\":{0},\"height\":{1},\"cursor\":{{\"x\":{2},\"y\":{3}}},\"activeWindow\":{4}}}",
|
|
929
|
+
width, height, pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (act == "screenshot")
|
|
933
|
+
{
|
|
934
|
+
string path = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "screen.png");
|
|
935
|
+
return Screenshot(path);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
if (act == "type")
|
|
939
|
+
{
|
|
940
|
+
string text = args.Length > 0 ? args[0] : "";
|
|
941
|
+
bool ok = TypeText(text);
|
|
942
|
+
return string.Format("{{\"success\":{0},\"action\":\"type\",\"chars\":{1},\"activeWindow\":{2}}}",
|
|
943
|
+
ok ? "true" : "false", text.Length, JsonEscape(GetActiveWindowTitle()));
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (act == "key" || act == "key_press")
|
|
947
|
+
{
|
|
948
|
+
string key = args.Length > 0 ? args[0] : "";
|
|
949
|
+
bool ok = KeyPress(key);
|
|
950
|
+
return string.Format("{{\"success\":{0},\"action\":\"key_press\",\"key\":{1},\"activeWindow\":{2}}}",
|
|
951
|
+
ok ? "true" : "false", JsonEscape(key), JsonEscape(GetActiveWindowTitle()));
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
if (act == "hotkey")
|
|
955
|
+
{
|
|
956
|
+
string[] keys;
|
|
957
|
+
if (args.Length > 1)
|
|
958
|
+
{
|
|
959
|
+
keys = args;
|
|
960
|
+
}
|
|
961
|
+
else if (args.Length == 1)
|
|
962
|
+
{
|
|
963
|
+
keys = args[0].Split(new char[] { '+', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
964
|
+
}
|
|
965
|
+
else
|
|
966
|
+
{
|
|
967
|
+
keys = new string[0];
|
|
968
|
+
}
|
|
969
|
+
for (int i = 0; i < keys.Length; i++)
|
|
970
|
+
{
|
|
971
|
+
keys[i] = keys[i].Trim();
|
|
972
|
+
}
|
|
973
|
+
bool ok = Hotkey(keys);
|
|
974
|
+
return string.Format("{{\"success\":{0},\"action\":\"hotkey\",\"keys\":{1},\"activeWindow\":{2}}}",
|
|
975
|
+
ok ? "true" : "false", JsonEscape(string.Join("+", keys)), JsonEscape(GetActiveWindowTitle()));
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (act == "active_window")
|
|
979
|
+
{
|
|
980
|
+
string title = GetActiveWindowTitle();
|
|
981
|
+
return string.Format("{{\"success\":true,\"action\":\"active_window\",\"activeWindow\":{0}}}",
|
|
982
|
+
JsonEscape(title));
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
if (act == "list_windows")
|
|
986
|
+
{
|
|
987
|
+
List<string> wins = GetVisibleWindows();
|
|
988
|
+
StringBuilder sb = new StringBuilder();
|
|
989
|
+
sb.Append("[");
|
|
990
|
+
for (int i = 0; i < wins.Count; i++)
|
|
991
|
+
{
|
|
992
|
+
sb.Append(JsonEscape(wins[i]));
|
|
993
|
+
if (i < wins.Count - 1) sb.Append(",");
|
|
994
|
+
}
|
|
995
|
+
sb.Append("]");
|
|
996
|
+
return string.Format("{{\"success\":true,\"action\":\"list_windows\",\"activeWindow\":{0},\"windows\":{1}}}",
|
|
997
|
+
JsonEscape(GetActiveWindowTitle()), sb.ToString());
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (act == "focus_window")
|
|
1001
|
+
{
|
|
1002
|
+
string win = args.Length > 0 ? args[0] : "";
|
|
1003
|
+
bool ok = FocusWindow(win);
|
|
1004
|
+
return string.Format("{{\"success\":{0},\"action\":\"focus_window\",\"target\":{1},\"activeWindow\":{2}}}",
|
|
1005
|
+
ok ? "true" : "false", JsonEscape(win), JsonEscape(GetActiveWindowTitle()));
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (act == "close_window" || act == "close_app")
|
|
1009
|
+
{
|
|
1010
|
+
string win = args.Length > 0 ? args[0] : "";
|
|
1011
|
+
bool ok = CloseWindow(win);
|
|
1012
|
+
return string.Format("{{\"success\":{0},\"action\":\"close_window\",\"target\":{1},\"activeWindow\":{2}}}",
|
|
1013
|
+
ok ? "true" : "false", JsonEscape(win), JsonEscape(GetActiveWindowTitle()));
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
if (act == "launch" || act == "open_app")
|
|
1017
|
+
{
|
|
1018
|
+
string app = args.Length > 0 ? args[0] : "";
|
|
1019
|
+
int pid;
|
|
1020
|
+
bool ok = LaunchApp(app, out pid);
|
|
1021
|
+
return string.Format("{{\"success\":{0},\"action\":\"launch\",\"app\":{1},\"pid\":{2}}}",
|
|
1022
|
+
ok ? "true" : "false", JsonEscape(app), pid);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (act == "window_bounds")
|
|
1026
|
+
{
|
|
1027
|
+
string win = args.Length > 0 ? args[0] : "";
|
|
1028
|
+
RECT? r = GetWindowBounds(win);
|
|
1029
|
+
if (r.HasValue)
|
|
1030
|
+
{
|
|
1031
|
+
int w = r.Value.Right - r.Value.Left;
|
|
1032
|
+
int h = r.Value.Bottom - r.Value.Top;
|
|
1033
|
+
return string.Format("{{\"success\":true,\"action\":\"window_bounds\",\"x\":{0},\"y\":{1},\"width\":{2},\"height\":{3}}}",
|
|
1034
|
+
r.Value.Left, r.Value.Top, w, h);
|
|
1035
|
+
}
|
|
1036
|
+
return string.Format("{{\"success\":false,\"action\":\"window_bounds\",\"error\":{0}}}", JsonEscape("Window not found: " + win));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
if (act == "diagnostic")
|
|
1040
|
+
{
|
|
1041
|
+
int width = GetSystemMetrics(SM_CXSCREEN);
|
|
1042
|
+
int height = GetSystemMetrics(SM_CYSCREEN);
|
|
1043
|
+
POINT pos = GetPosition();
|
|
1044
|
+
string title = GetActiveWindowTitle();
|
|
1045
|
+
List<string> wins = GetVisibleWindows();
|
|
1046
|
+
|
|
1047
|
+
return string.Format("{{\"success\":true,\"action\":\"diagnostic\",\"width\":{0},\"height\":{1},\"mouse\":{{\"x\":{2},\"y\":{3}}},\"activeWindow\":{4},\"windowCount\":{5}}}",
|
|
1048
|
+
width, height, pos.X, pos.Y, JsonEscape(title), wins.Count);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
return string.Format("{{\"success\":false,\"error\":{0},\"recoverable\":true}}",
|
|
1052
|
+
JsonEscape("Unknown action: " + action));
|
|
1053
|
+
}
|
|
1054
|
+
catch (Exception ex)
|
|
1055
|
+
{
|
|
1056
|
+
return string.Format("{{\"success\":false,\"action\":{0},\"error\":{1},\"recoverable\":true}}",
|
|
1057
|
+
JsonEscape(action), JsonEscape(ex.Message));
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
public static void RunServer()
|
|
1062
|
+
{
|
|
1063
|
+
AttachInputDesktop();
|
|
1064
|
+
string line;
|
|
1065
|
+
while ((line = Console.ReadLine()) != null)
|
|
1066
|
+
{
|
|
1067
|
+
line = line.Trim();
|
|
1068
|
+
if (string.IsNullOrEmpty(line)) continue;
|
|
1069
|
+
if (line == "exit" || line == "quit") break;
|
|
1070
|
+
|
|
1071
|
+
// Simple format: action arg1 arg2 ... or base64
|
|
1072
|
+
string result = "";
|
|
1073
|
+
try
|
|
1074
|
+
{
|
|
1075
|
+
string[] parts = ParseCommandLine(line);
|
|
1076
|
+
if (parts.Length > 0)
|
|
1077
|
+
{
|
|
1078
|
+
string action = parts[0];
|
|
1079
|
+
string[] args = new string[parts.Length - 1];
|
|
1080
|
+
Array.Copy(parts, 1, args, 0, args.Length);
|
|
1081
|
+
result = ExecuteAction(action, args);
|
|
1082
|
+
}
|
|
1083
|
+
else
|
|
1084
|
+
{
|
|
1085
|
+
result = "{\"success\":false,\"error\":\"Empty command\"}";
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
catch (Exception ex)
|
|
1089
|
+
{
|
|
1090
|
+
result = string.Format("{{\"success\":false,\"error\":{0}}}", JsonEscape(ex.Message));
|
|
1091
|
+
}
|
|
1092
|
+
Console.WriteLine(result);
|
|
1093
|
+
Console.Out.Flush();
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
private static string[] ParseCommandLine(string cmdLine)
|
|
1098
|
+
{
|
|
1099
|
+
List<string> args = new List<string>();
|
|
1100
|
+
bool inQuotes = false;
|
|
1101
|
+
StringBuilder current = new StringBuilder();
|
|
1102
|
+
|
|
1103
|
+
for (int i = 0; i < cmdLine.Length; i++)
|
|
1104
|
+
{
|
|
1105
|
+
char c = cmdLine[i];
|
|
1106
|
+
if (c == '\"')
|
|
1107
|
+
{
|
|
1108
|
+
inQuotes = !inQuotes;
|
|
1109
|
+
}
|
|
1110
|
+
else if (c == ' ' && !inQuotes)
|
|
1111
|
+
{
|
|
1112
|
+
if (current.Length > 0)
|
|
1113
|
+
{
|
|
1114
|
+
args.Add(current.ToString());
|
|
1115
|
+
current.Clear();
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
else
|
|
1119
|
+
{
|
|
1120
|
+
current.Append(c);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
if (current.Length > 0)
|
|
1124
|
+
{
|
|
1125
|
+
args.Add(current.ToString());
|
|
1126
|
+
}
|
|
1127
|
+
return args.ToArray();
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
[STAThread]
|
|
1131
|
+
public static void Main(string[] args)
|
|
1132
|
+
{
|
|
1133
|
+
// Thread execution with desktop attachment
|
|
1134
|
+
Thread t = new Thread(() =>
|
|
1135
|
+
{
|
|
1136
|
+
AttachInputDesktop();
|
|
1137
|
+
|
|
1138
|
+
if (args.Length > 0 && args[0] == "--server")
|
|
1139
|
+
{
|
|
1140
|
+
RunServer();
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
if (args.Length == 0)
|
|
1145
|
+
{
|
|
1146
|
+
Console.WriteLine(ExecuteAction("diagnostic", new string[0]));
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
string action = args[0];
|
|
1151
|
+
string[] actionArgs = new string[args.Length - 1];
|
|
1152
|
+
Array.Copy(args, 1, actionArgs, 0, actionArgs.Length);
|
|
1153
|
+
Console.WriteLine(ExecuteAction(action, actionArgs));
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
t.SetApartmentState(ApartmentState.STA);
|
|
1157
|
+
t.Start();
|
|
1158
|
+
t.Join();
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|