chocolatito-code 1.0.0
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/.mcp.json.ejemplo +19 -0
- package/COMPUTER-USE.md +484 -0
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/agent/context.d.ts +9 -0
- package/dist/agent/context.js +123 -0
- package/dist/agent/fileTracker.d.ts +16 -0
- package/dist/agent/fileTracker.js +53 -0
- package/dist/agent/loop.d.ts +25 -0
- package/dist/agent/loop.js +524 -0
- package/dist/agent/mentions.d.ts +11 -0
- package/dist/agent/mentions.js +43 -0
- package/dist/agent/retry.d.ts +17 -0
- package/dist/agent/retry.js +57 -0
- package/dist/agent/subagent.d.ts +2 -0
- package/dist/agent/subagent.js +116 -0
- package/dist/agent/syntaxValidator.d.ts +5 -0
- package/dist/agent/syntaxValidator.js +71 -0
- package/dist/agent/tracker.d.ts +19 -0
- package/dist/agent/tracker.js +38 -0
- package/dist/agent/undoManager.d.ts +37 -0
- package/dist/agent/undoManager.js +95 -0
- package/dist/config/constants.d.ts +77 -0
- package/dist/config/constants.js +146 -0
- package/dist/config/engine.d.ts +49 -0
- package/dist/config/engine.js +86 -0
- package/dist/config/env.d.ts +13 -0
- package/dist/config/env.js +103 -0
- package/dist/config/license.d.ts +39 -0
- package/dist/config/license.js +306 -0
- package/dist/config/permissions.d.ts +38 -0
- package/dist/config/permissions.js +239 -0
- package/dist/config/quota.d.ts +58 -0
- package/dist/config/quota.js +156 -0
- package/dist/hooks/manager.d.ts +60 -0
- package/dist/hooks/manager.js +193 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +470 -0
- package/dist/mcp/client.d.ts +55 -0
- package/dist/mcp/client.js +282 -0
- package/dist/mcp/manager.d.ts +26 -0
- package/dist/mcp/manager.js +167 -0
- package/dist/memory/manager.d.ts +9 -0
- package/dist/memory/manager.js +59 -0
- package/dist/prompts/systemPrompt.d.ts +5 -0
- package/dist/prompts/systemPrompt.js +177 -0
- package/dist/sessions/manager.d.ts +21 -0
- package/dist/sessions/manager.js +54 -0
- package/dist/skills/manager.d.ts +14 -0
- package/dist/skills/manager.js +86 -0
- package/dist/tools/browserCdp.d.ts +26 -0
- package/dist/tools/browserCdp.js +610 -0
- package/dist/tools/browserExtension.d.ts +58 -0
- package/dist/tools/browserExtension.js +479 -0
- package/dist/tools/browserLive.d.ts +12 -0
- package/dist/tools/browserLive.js +265 -0
- package/dist/tools/changeDir.d.ts +5 -0
- package/dist/tools/changeDir.js +63 -0
- package/dist/tools/computerUse.d.ts +58 -0
- package/dist/tools/computerUse.js +567 -0
- package/dist/tools/createImage.d.ts +1 -0
- package/dist/tools/createImage.js +34 -0
- package/dist/tools/definitions.d.ts +8 -0
- package/dist/tools/definitions.js +401 -0
- package/dist/tools/deleteFile.d.ts +1 -0
- package/dist/tools/deleteFile.js +17 -0
- package/dist/tools/editFile.d.ts +5 -0
- package/dist/tools/editFile.js +88 -0
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/findFiles.js +33 -0
- package/dist/tools/getSystemInfo.d.ts +1 -0
- package/dist/tools/getSystemInfo.js +23 -0
- package/dist/tools/gitTools.d.ts +3 -0
- package/dist/tools/gitTools.js +47 -0
- package/dist/tools/grepSearch.d.ts +1 -0
- package/dist/tools/grepSearch.js +37 -0
- package/dist/tools/listDir.d.ts +1 -0
- package/dist/tools/listDir.js +33 -0
- package/dist/tools/moveCopyFile.d.ts +1 -0
- package/dist/tools/moveCopyFile.js +21 -0
- package/dist/tools/runCommand.d.ts +1 -0
- package/dist/tools/runCommand.js +61 -0
- package/dist/tools/runner.d.ts +5 -0
- package/dist/tools/runner.js +177 -0
- package/dist/tools/safety.d.ts +17 -0
- package/dist/tools/safety.js +33 -0
- package/dist/tools/todoTool.d.ts +8 -0
- package/dist/tools/todoTool.js +27 -0
- package/dist/tools/toolDefsComputer.d.ts +13 -0
- package/dist/tools/toolDefsComputer.js +205 -0
- package/dist/tools/truncator.d.ts +11 -0
- package/dist/tools/truncator.js +24 -0
- package/dist/tools/viewFile.d.ts +1 -0
- package/dist/tools/viewFile.js +45 -0
- package/dist/tools/visionBridge.d.ts +37 -0
- package/dist/tools/visionBridge.js +107 -0
- package/dist/tools/webFetch.d.ts +1 -0
- package/dist/tools/webFetch.js +25 -0
- package/dist/tools/webSearch.d.ts +1 -0
- package/dist/tools/webSearch.js +35 -0
- package/dist/tools/win/hostClient.d.ts +31 -0
- package/dist/tools/win/hostClient.js +158 -0
- package/dist/tools/win/hostScript.d.ts +1 -0
- package/dist/tools/win/hostScript.js +925 -0
- package/dist/tools/writeFile.d.ts +1 -0
- package/dist/tools/writeFile.js +13 -0
- package/dist/ui/banner.d.ts +10 -0
- package/dist/ui/banner.js +165 -0
- package/dist/ui/diffViewer.d.ts +1 -0
- package/dist/ui/diffViewer.js +60 -0
- package/dist/ui/interrupt.d.ts +17 -0
- package/dist/ui/interrupt.js +156 -0
- package/dist/ui/markdownStream.d.ts +23 -0
- package/dist/ui/markdownStream.js +0 -0
- package/dist/ui/modes.d.ts +28 -0
- package/dist/ui/modes.js +57 -0
- package/dist/ui/permissionPrompt.d.ts +35 -0
- package/dist/ui/permissionPrompt.js +271 -0
- package/dist/ui/prompt.d.ts +7 -0
- package/dist/ui/prompt.js +466 -0
- package/dist/ui/reasoningStream.d.ts +6 -0
- package/dist/ui/reasoningStream.js +38 -0
- package/dist/ui/renderer.d.ts +10 -0
- package/dist/ui/renderer.js +142 -0
- package/dist/ui/spinner.d.ts +24 -0
- package/dist/ui/spinner.js +111 -0
- package/dist/ui/theme.d.ts +36 -0
- package/dist/ui/theme.js +37 -0
- package/dist/ui/typeAhead.d.ts +31 -0
- package/dist/ui/typeAhead.js +87 -0
- package/extension/README.md +74 -0
- package/extension/background.js +575 -0
- package/extension/content.js +446 -0
- package/extension/manifest.json +36 -0
- package/extension/popup.html +22 -0
- package/extension/popup.js +12 -0
- package/package.json +66 -0
- package/skills/complex-refactor/SKILL.md +13 -0
- package/skills/deep-debugger/SKILL.md +14 -0
- package/skills/software-architect/SKILL.md +20 -0
|
@@ -0,0 +1,925 @@
|
|
|
1
|
+
// Fuente del host persistente de Windows (PowerShell + C#).
|
|
2
|
+
// Se lanza UNA sola vez por sesion: compila los tipos nativos al arrancar y luego
|
|
3
|
+
// atiende comandos JSON por stdin. Evita el coste de ~1s por accion que tenia la
|
|
4
|
+
// version anterior (un powershell.exe + Add-Type nuevo en cada clic).
|
|
5
|
+
export const WIN_HOST_SCRIPT = String.raw `
|
|
6
|
+
$ErrorActionPreference = 'Stop'
|
|
7
|
+
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
8
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
9
|
+
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
|
|
10
|
+
|
|
11
|
+
$csharp = @'
|
|
12
|
+
using System;
|
|
13
|
+
using System.Collections.Generic;
|
|
14
|
+
using System.Drawing;
|
|
15
|
+
using System.Drawing.Imaging;
|
|
16
|
+
using System.Globalization;
|
|
17
|
+
using System.Runtime.InteropServices;
|
|
18
|
+
using System.Text;
|
|
19
|
+
using System.Threading;
|
|
20
|
+
using System.Windows.Automation;
|
|
21
|
+
|
|
22
|
+
public static class Choco
|
|
23
|
+
{
|
|
24
|
+
// ================= P/Invoke =================
|
|
25
|
+
[StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; }
|
|
26
|
+
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
|
|
27
|
+
|
|
28
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
29
|
+
struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
|
|
30
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
31
|
+
struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
|
|
32
|
+
[StructLayout(LayoutKind.Explicit)]
|
|
33
|
+
struct INPUTUNION { [FieldOffset(0)] public MOUSEINPUT mi; [FieldOffset(0)] public KEYBDINPUT ki; }
|
|
34
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
35
|
+
struct INPUT { public uint type; public INPUTUNION u; }
|
|
36
|
+
|
|
37
|
+
delegate bool EnumProc(IntPtr hWnd, IntPtr lParam);
|
|
38
|
+
|
|
39
|
+
[DllImport("user32.dll")] static extern IntPtr SetProcessDpiAwarenessContext(IntPtr value);
|
|
40
|
+
[DllImport("user32.dll")] static extern bool SetProcessDPIAware();
|
|
41
|
+
[DllImport("user32.dll")] static extern int GetSystemMetrics(int index);
|
|
42
|
+
[DllImport("user32.dll")] static extern bool SetCursorPos(int x, int y);
|
|
43
|
+
[DllImport("user32.dll")] static extern bool GetCursorPos(out POINT p);
|
|
44
|
+
[DllImport("user32.dll", SetLastError = true)] static extern uint SendInput(uint n, INPUT[] inputs, int cb);
|
|
45
|
+
[DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();
|
|
46
|
+
[DllImport("user32.dll")] static extern int GetWindowText(IntPtr h, StringBuilder s, int c);
|
|
47
|
+
[DllImport("user32.dll")] static extern int GetClassName(IntPtr h, StringBuilder s, int c);
|
|
48
|
+
[DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h);
|
|
49
|
+
[DllImport("user32.dll")] static extern bool IsIconic(IntPtr h);
|
|
50
|
+
[DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr h, out RECT r);
|
|
51
|
+
[DllImport("user32.dll")] static extern bool GetClientRect(IntPtr h, out RECT r);
|
|
52
|
+
[DllImport("user32.dll")] static extern bool ClientToScreen(IntPtr h, ref POINT p);
|
|
53
|
+
[DllImport("user32.dll")] static extern bool ScreenToClient(IntPtr h, ref POINT p);
|
|
54
|
+
[DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr l);
|
|
55
|
+
[DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
|
|
56
|
+
[DllImport("user32.dll")] static extern bool PrintWindow(IntPtr h, IntPtr hdc, uint flags);
|
|
57
|
+
[DllImport("user32.dll")] static extern bool PostMessage(IntPtr h, uint msg, IntPtr w, IntPtr l);
|
|
58
|
+
[DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr h);
|
|
59
|
+
[DllImport("user32.dll")] static extern bool ShowWindow(IntPtr h, int n);
|
|
60
|
+
[DllImport("user32.dll")] static extern bool BringWindowToTop(IntPtr h);
|
|
61
|
+
[DllImport("user32.dll")] static extern bool AttachThreadInput(uint a, uint b, bool attach);
|
|
62
|
+
[DllImport("user32.dll")] static extern short VkKeyScan(char c);
|
|
63
|
+
[DllImport("kernel32.dll")] static extern uint GetCurrentThreadId();
|
|
64
|
+
|
|
65
|
+
const uint MOUSEEVENTF_MOVE = 0x0001, MOUSEEVENTF_LEFTDOWN = 0x0002, MOUSEEVENTF_LEFTUP = 0x0004;
|
|
66
|
+
const uint MOUSEEVENTF_RIGHTDOWN = 0x0008, MOUSEEVENTF_RIGHTUP = 0x0010;
|
|
67
|
+
const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020, MOUSEEVENTF_MIDDLEUP = 0x0040;
|
|
68
|
+
const uint MOUSEEVENTF_WHEEL = 0x0800, MOUSEEVENTF_HWHEEL = 0x01000, MOUSEEVENTF_ABSOLUTE = 0x8000;
|
|
69
|
+
const uint KEYEVENTF_KEYUP = 0x0002, KEYEVENTF_UNICODE = 0x0004, KEYEVENTF_EXTENDEDKEY = 0x0001;
|
|
70
|
+
const uint WM_LBUTTONDOWN = 0x0201, WM_LBUTTONUP = 0x0202, WM_RBUTTONDOWN = 0x0204, WM_RBUTTONUP = 0x0205;
|
|
71
|
+
const uint WM_MOUSEMOVE = 0x0200, WM_CHAR = 0x0102, WM_KEYDOWN = 0x0100, WM_KEYUP = 0x0101;
|
|
72
|
+
const uint WM_LBUTTONDBLCLK = 0x0203;
|
|
73
|
+
|
|
74
|
+
// ================= Estado =================
|
|
75
|
+
static Dictionary<int, AutomationElement> _snap = new Dictionary<int, AutomationElement>();
|
|
76
|
+
static Dictionary<int, string> _snapLabel = new Dictionary<int, string>();
|
|
77
|
+
static IntPtr _snapWindow = IntPtr.Zero;
|
|
78
|
+
|
|
79
|
+
// ================= Utilidades JSON =================
|
|
80
|
+
public static string Esc(string s)
|
|
81
|
+
{
|
|
82
|
+
if (s == null) return "";
|
|
83
|
+
StringBuilder b = new StringBuilder();
|
|
84
|
+
char bs = (char)92;
|
|
85
|
+
foreach (char c in s)
|
|
86
|
+
{
|
|
87
|
+
if (c == (char)34) { b.Append(bs); b.Append((char)34); }
|
|
88
|
+
else if (c == bs) { b.Append(bs); b.Append(bs); }
|
|
89
|
+
else if (c == (char)10) { b.Append(bs); b.Append('n'); }
|
|
90
|
+
else if (c == (char)13) { b.Append(bs); b.Append('r'); }
|
|
91
|
+
else if (c == (char)9) { b.Append(bs); b.Append('t'); }
|
|
92
|
+
else if (c < 32 || c == 127) { b.Append(bs); b.Append('u'); b.Append(((int)c).ToString("x4")); }
|
|
93
|
+
else b.Append(c);
|
|
94
|
+
}
|
|
95
|
+
return b.ToString();
|
|
96
|
+
}
|
|
97
|
+
static string Q(string s) { return "\"" + Esc(s) + "\""; }
|
|
98
|
+
|
|
99
|
+
// Comparacion sin tildes ni mayusculas: en una interfaz en espanol, buscar
|
|
100
|
+
// "Imagenes" tiene que encontrar "Imágenes". Si no, el agente cree que el
|
|
101
|
+
// control no existe y se cae innecesariamente a la vision.
|
|
102
|
+
public static string Norm(string s)
|
|
103
|
+
{
|
|
104
|
+
if (s == null) return "";
|
|
105
|
+
string d = s.Normalize(NormalizationForm.FormD);
|
|
106
|
+
StringBuilder b = new StringBuilder(d.Length);
|
|
107
|
+
for (int i = 0; i < d.Length; i++)
|
|
108
|
+
{
|
|
109
|
+
if (CharUnicodeInfo.GetUnicodeCategory(d[i]) != UnicodeCategory.NonSpacingMark) b.Append(d[i]);
|
|
110
|
+
}
|
|
111
|
+
return b.ToString().Normalize(NormalizationForm.FormC).ToLowerInvariant();
|
|
112
|
+
}
|
|
113
|
+
static string N(int v) { return v.ToString(CultureInfo.InvariantCulture); }
|
|
114
|
+
|
|
115
|
+
// ================= DPI =================
|
|
116
|
+
public static string SetDpiAware()
|
|
117
|
+
{
|
|
118
|
+
string mode = "none";
|
|
119
|
+
try { if (SetProcessDpiAwarenessContext(new IntPtr(-4)) != IntPtr.Zero) mode = "per-monitor-v2"; }
|
|
120
|
+
catch { }
|
|
121
|
+
if (mode == "none") { try { if (SetProcessDPIAware()) mode = "system"; } catch { } }
|
|
122
|
+
return mode;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public static string ScreenInfo()
|
|
126
|
+
{
|
|
127
|
+
int w = GetSystemMetrics(0), h = GetSystemMetrics(1);
|
|
128
|
+
int vx = GetSystemMetrics(76), vy = GetSystemMetrics(77);
|
|
129
|
+
int vw = GetSystemMetrics(78), vh = GetSystemMetrics(79);
|
|
130
|
+
POINT c; GetCursorPos(out c);
|
|
131
|
+
IntPtr fg = GetForegroundWindow();
|
|
132
|
+
return "{\"width\":" + N(w) + ",\"height\":" + N(h) +
|
|
133
|
+
",\"virtualX\":" + N(vx) + ",\"virtualY\":" + N(vy) +
|
|
134
|
+
",\"virtualWidth\":" + N(vw) + ",\"virtualHeight\":" + N(vh) +
|
|
135
|
+
",\"cursorX\":" + N(c.X) + ",\"cursorY\":" + N(c.Y) +
|
|
136
|
+
",\"foreground\":" + Q(Title(fg)) + "}";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static string Title(IntPtr h)
|
|
140
|
+
{
|
|
141
|
+
StringBuilder sb = new StringBuilder(512);
|
|
142
|
+
GetWindowText(h, sb, 512);
|
|
143
|
+
return sb.ToString();
|
|
144
|
+
}
|
|
145
|
+
static string ClassOf(IntPtr h)
|
|
146
|
+
{
|
|
147
|
+
StringBuilder sb = new StringBuilder(256);
|
|
148
|
+
GetClassName(h, sb, 256);
|
|
149
|
+
return sb.ToString();
|
|
150
|
+
}
|
|
151
|
+
static string ProcOf(IntPtr h)
|
|
152
|
+
{
|
|
153
|
+
uint pid; GetWindowThreadProcessId(h, out pid);
|
|
154
|
+
try { return System.Diagnostics.Process.GetProcessById((int)pid).ProcessName; }
|
|
155
|
+
catch { return ""; }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ================= Ventanas =================
|
|
159
|
+
public static string ListWindows(string filter)
|
|
160
|
+
{
|
|
161
|
+
List<string> rows = new List<string>();
|
|
162
|
+
string f = Norm(filter);
|
|
163
|
+
EnumWindows(delegate(IntPtr h, IntPtr l)
|
|
164
|
+
{
|
|
165
|
+
if (!IsWindowVisible(h)) return true;
|
|
166
|
+
string t = Title(h);
|
|
167
|
+
if (t.Length == 0) return true;
|
|
168
|
+
string cls = ClassOf(h);
|
|
169
|
+
if (cls == "Progman" || cls == "Shell_TrayWnd" || cls == "WorkerW") return true;
|
|
170
|
+
RECT r; GetWindowRect(h, out r);
|
|
171
|
+
int w = r.Right - r.Left, hh = r.Bottom - r.Top;
|
|
172
|
+
if (w <= 0 || hh <= 0) return true;
|
|
173
|
+
string proc = ProcOf(h);
|
|
174
|
+
if (f.Length > 0 && Norm(t).IndexOf(f) < 0 && Norm(proc).IndexOf(f) < 0) return true;
|
|
175
|
+
rows.Add("{\"hwnd\":" + N((int)h) + ",\"title\":" + Q(t) + ",\"process\":" + Q(proc) +
|
|
176
|
+
",\"class\":" + Q(cls) + ",\"x\":" + N(r.Left) + ",\"y\":" + N(r.Top) +
|
|
177
|
+
",\"width\":" + N(w) + ",\"height\":" + N(hh) +
|
|
178
|
+
",\"minimized\":" + (IsIconic(h) ? "true" : "false") + "}");
|
|
179
|
+
return true;
|
|
180
|
+
}, IntPtr.Zero);
|
|
181
|
+
return "[" + string.Join(",", rows.ToArray()) + "]";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Busca la mejor ventana: prioriza coincidencia de titulo, luego proceso, y
|
|
185
|
+
// entre varias elige la de mayor area visible (evita ventanas fantasma de 1x1).
|
|
186
|
+
public static IntPtr FindWindow(string query)
|
|
187
|
+
{
|
|
188
|
+
if (query == null || query.Trim().Length == 0) return GetForegroundWindow();
|
|
189
|
+
string q = Norm(query);
|
|
190
|
+
IntPtr best = IntPtr.Zero;
|
|
191
|
+
int bestScore = -1;
|
|
192
|
+
_bestTier = -1; _tieCount = 0; _tieTitles = "";
|
|
193
|
+
EnumWindows(delegate(IntPtr h, IntPtr l)
|
|
194
|
+
{
|
|
195
|
+
if (!IsWindowVisible(h)) return true;
|
|
196
|
+
string t = Title(h);
|
|
197
|
+
if (t.Length == 0) return true;
|
|
198
|
+
string cls = ClassOf(h);
|
|
199
|
+
if (cls == "Progman" || cls == "Shell_TrayWnd" || cls == "WorkerW") return true;
|
|
200
|
+
string tl = Norm(t);
|
|
201
|
+
string pl = Norm(ProcOf(h));
|
|
202
|
+
RECT r; GetWindowRect(h, out r);
|
|
203
|
+
int area = (r.Right - r.Left) * (r.Bottom - r.Top);
|
|
204
|
+
if (area <= 0) return true;
|
|
205
|
+
int score = -1;
|
|
206
|
+
if (tl == q) score = 400;
|
|
207
|
+
else if (tl.IndexOf(q) >= 0) score = 300;
|
|
208
|
+
else if (pl == q) score = 200;
|
|
209
|
+
else if (pl.IndexOf(q) >= 0) score = 100;
|
|
210
|
+
if (score < 0) return true;
|
|
211
|
+
if (IsIconic(h)) score -= 50;
|
|
212
|
+
// Se guarda el nivel de coincidencia aparte del desempate por area,
|
|
213
|
+
// para poder detectar despues si la consulta era ambigua.
|
|
214
|
+
if (score > _bestTier) { _bestTier = score; _tieCount = 1; _tieTitles = Title(h); }
|
|
215
|
+
else if (score == _bestTier) { _tieCount++; if (_tieTitles.Length < 300) _tieTitles += " | " + Title(h); }
|
|
216
|
+
score = score * 1000000 + Math.Min(area / 1000, 900000);
|
|
217
|
+
if (score > bestScore) { bestScore = score; best = h; }
|
|
218
|
+
return true;
|
|
219
|
+
}, IntPtr.Zero);
|
|
220
|
+
return best;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
static int _bestTier = -1;
|
|
224
|
+
static int _tieCount = 0;
|
|
225
|
+
static string _tieTitles = "";
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Cuantas ventanas empataban en la ultima busqueda. Si son varias, quien
|
|
229
|
+
* llama debe negarse a actuar en vez de elegir una a ciegas: elegir mal aqui
|
|
230
|
+
* significa mandar un Alt+F4 a la ventana equivocada.
|
|
231
|
+
*/
|
|
232
|
+
public static string Ambiguity()
|
|
233
|
+
{
|
|
234
|
+
return "{\"matches\":" + N(_tieCount) + ",\"candidates\":" + Q(_tieTitles) + "}";
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public static string WindowInfo(IntPtr h)
|
|
238
|
+
{
|
|
239
|
+
if (h == IntPtr.Zero) return "{\"found\":false}";
|
|
240
|
+
RECT r; GetWindowRect(h, out r);
|
|
241
|
+
return "{\"found\":true,\"hwnd\":" + N((int)h) + ",\"title\":" + Q(Title(h)) +
|
|
242
|
+
",\"process\":" + Q(ProcOf(h)) + ",\"class\":" + Q(ClassOf(h)) +
|
|
243
|
+
",\"x\":" + N(r.Left) + ",\"y\":" + N(r.Top) +
|
|
244
|
+
",\"width\":" + N(r.Right - r.Left) + ",\"height\":" + N(r.Bottom - r.Top) +
|
|
245
|
+
",\"minimized\":" + (IsIconic(h) ? "true" : "false") +
|
|
246
|
+
",\"foreground\":" + (GetForegroundWindow() == h ? "true" : "false") + "}";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
public static string FocusWindow(IntPtr h)
|
|
250
|
+
{
|
|
251
|
+
if (h == IntPtr.Zero) return "{\"ok\":false,\"error\":\"ventana no encontrada\"}";
|
|
252
|
+
if (IsIconic(h)) ShowWindow(h, 9);
|
|
253
|
+
// AttachThreadInput evita que Windows bloquee el cambio de foco entre procesos.
|
|
254
|
+
uint target = GetWindowThreadProcessId(h, out _dummyPid);
|
|
255
|
+
uint self = GetCurrentThreadId();
|
|
256
|
+
bool attached = false;
|
|
257
|
+
if (target != self) attached = AttachThreadInput(self, target, true);
|
|
258
|
+
// Windows bloquea que un proceso de fondo robe el primer plano salvo que
|
|
259
|
+
// haya habido entrada de teclado reciente. Un toque de ALT levanta ese
|
|
260
|
+
// bloqueo; sin esto SetForegroundWindow devuelve false y las teclas
|
|
261
|
+
// acaban en la ventana equivocada.
|
|
262
|
+
SendVk(0x12, false);
|
|
263
|
+
SendVk(0x12, true);
|
|
264
|
+
BringWindowToTop(h);
|
|
265
|
+
SetForegroundWindow(h);
|
|
266
|
+
if (attached) AttachThreadInput(self, target, false);
|
|
267
|
+
Thread.Sleep(150);
|
|
268
|
+
// Lo que importa no es lo que devuelva la API, sino si la ventana quedo
|
|
269
|
+
// realmente delante: se comprueba.
|
|
270
|
+
bool ok = (GetForegroundWindow() == h);
|
|
271
|
+
return "{\"ok\":" + (ok ? "true" : "false") + ",\"title\":" + Q(Title(h)) +
|
|
272
|
+
",\"foreground\":" + Q(Title(GetForegroundWindow())) + "}";
|
|
273
|
+
}
|
|
274
|
+
static uint _dummyPid = 0;
|
|
275
|
+
|
|
276
|
+
// ================= Captura =================
|
|
277
|
+
// Escala la imagen respetando el limite de lado largo de la spec de Anthropic
|
|
278
|
+
// (1568 px para modelos clasicos) y devuelve el factor para poder traducir
|
|
279
|
+
// coordenadas del modelo -> pantalla fisica.
|
|
280
|
+
static double SaveScaled(Bitmap bmp, string path, int maxLongEdge)
|
|
281
|
+
{
|
|
282
|
+
int w = bmp.Width, h = bmp.Height;
|
|
283
|
+
double scale = 1.0;
|
|
284
|
+
int longEdge = Math.Max(w, h);
|
|
285
|
+
if (maxLongEdge > 0 && longEdge > maxLongEdge) scale = (double)maxLongEdge / (double)longEdge;
|
|
286
|
+
if (scale >= 0.999)
|
|
287
|
+
{
|
|
288
|
+
bmp.Save(path, ImageFormat.Png);
|
|
289
|
+
return 1.0;
|
|
290
|
+
}
|
|
291
|
+
int nw = (int)Math.Round(w * scale), nh = (int)Math.Round(h * scale);
|
|
292
|
+
using (Bitmap dst = new Bitmap(nw, nh))
|
|
293
|
+
using (Graphics g = Graphics.FromImage(dst))
|
|
294
|
+
{
|
|
295
|
+
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
|
296
|
+
g.DrawImage(bmp, 0, 0, nw, nh);
|
|
297
|
+
dst.Save(path, ImageFormat.Png);
|
|
298
|
+
}
|
|
299
|
+
return scale;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public static string CaptureScreen(string path, int maxLongEdge, bool drawCursor)
|
|
303
|
+
{
|
|
304
|
+
int vx = GetSystemMetrics(76), vy = GetSystemMetrics(77);
|
|
305
|
+
int vw = GetSystemMetrics(78), vh = GetSystemMetrics(79);
|
|
306
|
+
using (Bitmap bmp = new Bitmap(vw, vh, PixelFormat.Format32bppArgb))
|
|
307
|
+
{
|
|
308
|
+
using (Graphics g = Graphics.FromImage(bmp))
|
|
309
|
+
{
|
|
310
|
+
g.CopyFromScreen(vx, vy, 0, 0, new Size(vw, vh), CopyPixelOperation.SourceCopy);
|
|
311
|
+
if (drawCursor)
|
|
312
|
+
{
|
|
313
|
+
POINT c; GetCursorPos(out c);
|
|
314
|
+
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
|
|
315
|
+
using (Pen p = new Pen(Color.FromArgb(255, 235, 75, 75), 3))
|
|
316
|
+
{
|
|
317
|
+
g.DrawEllipse(p, c.X - vx - 16, c.Y - vy - 16, 32, 32);
|
|
318
|
+
g.DrawLine(p, c.X - vx - 24, c.Y - vy, c.X - vx + 24, c.Y - vy);
|
|
319
|
+
g.DrawLine(p, c.X - vx, c.Y - vy - 24, c.X - vx, c.Y - vy + 24);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
double s = SaveScaled(bmp, path, maxLongEdge);
|
|
324
|
+
return "{\"ok\":true,\"path\":" + Q(path) + ",\"screenWidth\":" + N(vw) + ",\"screenHeight\":" + N(vh) +
|
|
325
|
+
",\"originX\":" + N(vx) + ",\"originY\":" + N(vy) +
|
|
326
|
+
",\"imageWidth\":" + N((int)Math.Round(vw * s)) + ",\"imageHeight\":" + N((int)Math.Round(vh * s)) +
|
|
327
|
+
",\"scale\":" + s.ToString("0.#####", CultureInfo.InvariantCulture) + "}";
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Captura UNA ventana concreta sin traerla al frente.
|
|
332
|
+
// PrintWindow(PW_RENDERFULLCONTENT=2) pide a la ventana que se redibuje en
|
|
333
|
+
// nuestro DC, asi funciona aunque este tapada o en segundo plano.
|
|
334
|
+
public static string CaptureWindow(IntPtr h, string path, int maxLongEdge, bool clientOnly)
|
|
335
|
+
{
|
|
336
|
+
if (h == IntPtr.Zero) return "{\"ok\":false,\"error\":\"ventana no encontrada\"}";
|
|
337
|
+
RECT r;
|
|
338
|
+
if (clientOnly) { GetClientRect(h, out r); } else { GetWindowRect(h, out r); }
|
|
339
|
+
int w = r.Right - r.Left, hh = r.Bottom - r.Top;
|
|
340
|
+
if (w <= 0 || hh <= 0) return "{\"ok\":false,\"error\":\"ventana sin area visible (minimizada?)\"}";
|
|
341
|
+
using (Bitmap bmp = new Bitmap(w, hh, PixelFormat.Format32bppArgb))
|
|
342
|
+
{
|
|
343
|
+
bool printed = false;
|
|
344
|
+
using (Graphics g = Graphics.FromImage(bmp))
|
|
345
|
+
{
|
|
346
|
+
IntPtr hdc = g.GetHdc();
|
|
347
|
+
try { printed = PrintWindow(h, hdc, 2); }
|
|
348
|
+
finally { g.ReleaseHdc(hdc); }
|
|
349
|
+
}
|
|
350
|
+
// Chrome/Electron a veces devuelven negro con PrintWindow: si sale casi
|
|
351
|
+
// todo negro y la ventana esta visible, caemos a copia de pantalla.
|
|
352
|
+
if (!printed || IsMostlyBlank(bmp))
|
|
353
|
+
{
|
|
354
|
+
RECT wr; GetWindowRect(h, out wr);
|
|
355
|
+
using (Graphics g2 = Graphics.FromImage(bmp))
|
|
356
|
+
g2.CopyFromScreen(wr.Left, wr.Top, 0, 0, new Size(w, hh), CopyPixelOperation.SourceCopy);
|
|
357
|
+
}
|
|
358
|
+
double s = SaveScaled(bmp, path, maxLongEdge);
|
|
359
|
+
RECT wr2; GetWindowRect(h, out wr2);
|
|
360
|
+
return "{\"ok\":true,\"path\":" + Q(path) + ",\"title\":" + Q(Title(h)) +
|
|
361
|
+
",\"windowX\":" + N(wr2.Left) + ",\"windowY\":" + N(wr2.Top) +
|
|
362
|
+
",\"windowWidth\":" + N(w) + ",\"windowHeight\":" + N(hh) +
|
|
363
|
+
",\"imageWidth\":" + N((int)Math.Round(w * s)) + ",\"imageHeight\":" + N((int)Math.Round(hh * s)) +
|
|
364
|
+
",\"scale\":" + s.ToString("0.#####", CultureInfo.InvariantCulture) + "}";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
static bool IsMostlyBlank(Bitmap b)
|
|
369
|
+
{
|
|
370
|
+
int step = Math.Max(1, Math.Min(b.Width, b.Height) / 24);
|
|
371
|
+
int total = 0, blank = 0;
|
|
372
|
+
for (int y = 0; y < b.Height; y += step)
|
|
373
|
+
for (int x = 0; x < b.Width; x += step)
|
|
374
|
+
{
|
|
375
|
+
Color c = b.GetPixel(x, y);
|
|
376
|
+
total++;
|
|
377
|
+
if (c.A == 0 || (c.R < 8 && c.G < 8 && c.B < 8)) blank++;
|
|
378
|
+
}
|
|
379
|
+
return total > 0 && ((double)blank / (double)total) > 0.97;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ================= UI Automation (arbol de accesibilidad) =================
|
|
383
|
+
// Pieza clave: en vez de adivinar pixeles, enumeramos los controles reales de
|
|
384
|
+
// la ventana con su nombre y su rectangulo. El modelo elige por "ref", no por
|
|
385
|
+
// coordenada inventada, y ademas se puede activar sin tocar el raton.
|
|
386
|
+
static string RoleOf(AutomationElement e)
|
|
387
|
+
{
|
|
388
|
+
try { return e.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""); }
|
|
389
|
+
catch { return "Unknown"; }
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static bool IsInteractive(string role)
|
|
393
|
+
{
|
|
394
|
+
switch (role)
|
|
395
|
+
{
|
|
396
|
+
case "Button": case "Edit": case "Document": case "ComboBox": case "CheckBox":
|
|
397
|
+
case "RadioButton": case "MenuItem": case "ListItem": case "TabItem": case "Hyperlink":
|
|
398
|
+
case "TreeItem": case "Slider": case "SplitButton": case "Spinner": case "Custom":
|
|
399
|
+
case "DataItem": case "Menu": case "Tab": case "List":
|
|
400
|
+
return true;
|
|
401
|
+
default: return false;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
static string PatternsOf(AutomationElement e)
|
|
406
|
+
{
|
|
407
|
+
List<string> p = new List<string>();
|
|
408
|
+
try { if ((bool)e.GetCurrentPropertyValue(AutomationElement.IsInvokePatternAvailableProperty)) p.Add("invoke"); } catch { }
|
|
409
|
+
try { if ((bool)e.GetCurrentPropertyValue(AutomationElement.IsValuePatternAvailableProperty)) p.Add("setvalue"); } catch { }
|
|
410
|
+
try { if ((bool)e.GetCurrentPropertyValue(AutomationElement.IsTogglePatternAvailableProperty)) p.Add("toggle"); } catch { }
|
|
411
|
+
try { if ((bool)e.GetCurrentPropertyValue(AutomationElement.IsExpandCollapsePatternAvailableProperty)) p.Add("expand"); } catch { }
|
|
412
|
+
try { if ((bool)e.GetCurrentPropertyValue(AutomationElement.IsSelectionItemPatternAvailableProperty)) p.Add("select"); } catch { }
|
|
413
|
+
return string.Join("|", p.ToArray());
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
public static string Snapshot(IntPtr hwnd, int maxNodes, string filter, bool interactiveOnly)
|
|
417
|
+
{
|
|
418
|
+
if (hwnd == IntPtr.Zero) return "{\"ok\":false,\"error\":\"ventana no encontrada\"}";
|
|
419
|
+
AutomationElement root;
|
|
420
|
+
try { root = AutomationElement.FromHandle(hwnd); }
|
|
421
|
+
catch (Exception ex) { return "{\"ok\":false,\"error\":" + Q("UIA no pudo abrir la ventana: " + ex.Message) + "}"; }
|
|
422
|
+
if (root == null) return "{\"ok\":false,\"error\":\"UIA devolvio null\"}";
|
|
423
|
+
|
|
424
|
+
_snap.Clear(); _snapLabel.Clear(); _snapWindow = hwnd;
|
|
425
|
+
List<string> rows = new List<string>();
|
|
426
|
+
string f = Norm(filter);
|
|
427
|
+
int counter = 0;
|
|
428
|
+
int budget = (maxNodes <= 0) ? 250 : maxNodes;
|
|
429
|
+
TreeWalker walker = TreeWalker.ControlViewWalker;
|
|
430
|
+
|
|
431
|
+
// Recorrido en anchura: los controles utiles suelen estar arriba en el
|
|
432
|
+
// arbol y asi no desbordamos la pila en apps enormes como Chrome.
|
|
433
|
+
Queue<KeyValuePair<AutomationElement, int>> queue = new Queue<KeyValuePair<AutomationElement, int>>();
|
|
434
|
+
queue.Enqueue(new KeyValuePair<AutomationElement, int>(root, 0));
|
|
435
|
+
int visited = 0;
|
|
436
|
+
|
|
437
|
+
while (queue.Count > 0 && rows.Count < budget && visited < 6000)
|
|
438
|
+
{
|
|
439
|
+
KeyValuePair<AutomationElement, int> cur = queue.Dequeue();
|
|
440
|
+
AutomationElement el = cur.Key;
|
|
441
|
+
int depth = cur.Value;
|
|
442
|
+
visited++;
|
|
443
|
+
try
|
|
444
|
+
{
|
|
445
|
+
string role = RoleOf(el);
|
|
446
|
+
string name = "";
|
|
447
|
+
string val = "";
|
|
448
|
+
bool enabled = true, offscreen = false;
|
|
449
|
+
System.Windows.Rect rect = System.Windows.Rect.Empty;
|
|
450
|
+
try { name = el.Current.Name; } catch { }
|
|
451
|
+
try { enabled = el.Current.IsEnabled; } catch { }
|
|
452
|
+
try { offscreen = el.Current.IsOffscreen; } catch { }
|
|
453
|
+
try { rect = el.Current.BoundingRectangle; } catch { }
|
|
454
|
+
try
|
|
455
|
+
{
|
|
456
|
+
object vpo;
|
|
457
|
+
if (el.TryGetCurrentPattern(ValuePattern.Pattern, out vpo))
|
|
458
|
+
val = ((ValuePattern)vpo).Current.Value;
|
|
459
|
+
}
|
|
460
|
+
catch { }
|
|
461
|
+
if (name == null) name = "";
|
|
462
|
+
if (val == null) val = "";
|
|
463
|
+
|
|
464
|
+
bool hasArea = !rect.IsEmpty && rect.Width >= 2 && rect.Height >= 2;
|
|
465
|
+
bool keep = hasArea && !offscreen && depth > 0;
|
|
466
|
+
if (keep && interactiveOnly && !IsInteractive(role)) keep = false;
|
|
467
|
+
if (keep && name.Length == 0 && val.Length == 0 && role != "Edit" && role != "Document") keep = false;
|
|
468
|
+
if (keep && f.Length > 0)
|
|
469
|
+
{
|
|
470
|
+
string hay = Norm(name + " " + val + " " + role);
|
|
471
|
+
if (hay.IndexOf(f) < 0) keep = false;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (keep)
|
|
475
|
+
{
|
|
476
|
+
counter++;
|
|
477
|
+
_snap[counter] = el;
|
|
478
|
+
_snapLabel[counter] = role + " " + name;
|
|
479
|
+
int cx = (int)Math.Round(rect.X + rect.Width / 2.0);
|
|
480
|
+
int cy = (int)Math.Round(rect.Y + rect.Height / 2.0);
|
|
481
|
+
string shortName = name.Length > 120 ? name.Substring(0, 117) + "..." : name;
|
|
482
|
+
string shortVal = val.Length > 80 ? val.Substring(0, 77) + "..." : val;
|
|
483
|
+
rows.Add("{\"ref\":" + N(counter) + ",\"role\":" + Q(role) + ",\"name\":" + Q(shortName) +
|
|
484
|
+
",\"value\":" + Q(shortVal) + ",\"x\":" + N(cx) + ",\"y\":" + N(cy) +
|
|
485
|
+
",\"left\":" + N((int)rect.X) + ",\"top\":" + N((int)rect.Y) +
|
|
486
|
+
",\"width\":" + N((int)rect.Width) + ",\"height\":" + N((int)rect.Height) +
|
|
487
|
+
",\"enabled\":" + (enabled ? "true" : "false") +
|
|
488
|
+
",\"actions\":" + Q(PatternsOf(el)) + "}");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (depth < 40)
|
|
492
|
+
{
|
|
493
|
+
AutomationElement child = walker.GetFirstChild(el);
|
|
494
|
+
int siblings = 0;
|
|
495
|
+
while (child != null && siblings < 300)
|
|
496
|
+
{
|
|
497
|
+
queue.Enqueue(new KeyValuePair<AutomationElement, int>(child, depth + 1));
|
|
498
|
+
siblings++;
|
|
499
|
+
child = walker.GetNextSibling(child);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch { }
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
RECT wr; GetWindowRect(hwnd, out wr);
|
|
507
|
+
return "{\"ok\":true,\"title\":" + Q(Title(hwnd)) + ",\"hwnd\":" + N((int)hwnd) +
|
|
508
|
+
",\"windowX\":" + N(wr.Left) + ",\"windowY\":" + N(wr.Top) +
|
|
509
|
+
",\"windowWidth\":" + N(wr.Right - wr.Left) + ",\"windowHeight\":" + N(wr.Bottom - wr.Top) +
|
|
510
|
+
",\"truncated\":" + (rows.Count >= budget ? "true" : "false") +
|
|
511
|
+
",\"elements\":[" + string.Join(",", rows.ToArray()) + "]}";
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
static AutomationElement Ref(int id)
|
|
515
|
+
{
|
|
516
|
+
AutomationElement e;
|
|
517
|
+
if (_snap.TryGetValue(id, out e)) return e;
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
public static string RefRect(int id)
|
|
522
|
+
{
|
|
523
|
+
AutomationElement e = Ref(id);
|
|
524
|
+
if (e == null) return "{\"ok\":false,\"error\":\"ref inexistente; repite ui_snapshot\"}";
|
|
525
|
+
try
|
|
526
|
+
{
|
|
527
|
+
System.Windows.Rect r = e.Current.BoundingRectangle;
|
|
528
|
+
return "{\"ok\":true,\"x\":" + N((int)Math.Round(r.X + r.Width / 2.0)) +
|
|
529
|
+
",\"y\":" + N((int)Math.Round(r.Y + r.Height / 2.0)) +
|
|
530
|
+
",\"label\":" + Q(_snapLabel.ContainsKey(id) ? _snapLabel[id] : "") + "}";
|
|
531
|
+
}
|
|
532
|
+
catch (Exception ex) { return "{\"ok\":false,\"error\":" + Q(ex.Message) + "}"; }
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Activa el elemento SIN mover el raton y SIN robar el foco de pantalla.
|
|
536
|
+
public static string RefInvoke(int id)
|
|
537
|
+
{
|
|
538
|
+
AutomationElement e = Ref(id);
|
|
539
|
+
if (e == null) return "{\"ok\":false,\"error\":\"ref inexistente; repite ui_snapshot\"}";
|
|
540
|
+
string label = _snapLabel.ContainsKey(id) ? _snapLabel[id] : ("ref " + N(id));
|
|
541
|
+
try
|
|
542
|
+
{
|
|
543
|
+
try { object sp; if (e.TryGetCurrentPattern(ScrollItemPattern.Pattern, out sp)) ((ScrollItemPattern)sp).ScrollIntoView(); } catch { }
|
|
544
|
+
object p;
|
|
545
|
+
if (e.TryGetCurrentPattern(InvokePattern.Pattern, out p)) { ((InvokePattern)p).Invoke(); return "{\"ok\":true,\"via\":\"invoke\",\"label\":" + Q(label) + "}"; }
|
|
546
|
+
if (e.TryGetCurrentPattern(SelectionItemPattern.Pattern, out p)) { ((SelectionItemPattern)p).Select(); return "{\"ok\":true,\"via\":\"select\",\"label\":" + Q(label) + "}"; }
|
|
547
|
+
if (e.TryGetCurrentPattern(TogglePattern.Pattern, out p)) { ((TogglePattern)p).Toggle(); return "{\"ok\":true,\"via\":\"toggle\",\"label\":" + Q(label) + "}"; }
|
|
548
|
+
if (e.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out p)) { ((ExpandCollapsePattern)p).Expand(); return "{\"ok\":true,\"via\":\"expand\",\"label\":" + Q(label) + "}"; }
|
|
549
|
+
return "{\"ok\":false,\"error\":" + Q("El elemento " + label + " no expone patron activable; usa click sobre sus coordenadas.") + "}";
|
|
550
|
+
}
|
|
551
|
+
catch (Exception ex) { return "{\"ok\":false,\"error\":" + Q(ex.Message) + "}"; }
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
public static string RefSetValue(int id, string text, bool replace)
|
|
555
|
+
{
|
|
556
|
+
AutomationElement e = Ref(id);
|
|
557
|
+
if (e == null) return "{\"ok\":false,\"error\":\"ref inexistente; repite ui_snapshot\"}";
|
|
558
|
+
try
|
|
559
|
+
{
|
|
560
|
+
// ValuePattern.SetValue SUSTITUYE todo el contenido del control. En un
|
|
561
|
+
// campo de una linea es lo que se quiere; en un editor de texto
|
|
562
|
+
// completo eso borra el documento entero del usuario. Por eso un
|
|
563
|
+
// Document nunca se escribe por aqui salvo peticion explicita.
|
|
564
|
+
string role = RoleOf(e);
|
|
565
|
+
if (role == "Document" && !replace)
|
|
566
|
+
{
|
|
567
|
+
return "{\"ok\":false,\"needsTyping\":true,\"error\":\"es un editor de texto completo: escribir por ValuePattern borraria todo su contenido. Se escribe con enfoque y teclado.\"}";
|
|
568
|
+
}
|
|
569
|
+
object p;
|
|
570
|
+
if (e.TryGetCurrentPattern(ValuePattern.Pattern, out p))
|
|
571
|
+
{
|
|
572
|
+
ValuePattern vp = (ValuePattern)p;
|
|
573
|
+
if (vp.Current.IsReadOnly) return "{\"ok\":false,\"error\":\"campo de solo lectura\"}";
|
|
574
|
+
try { e.SetFocus(); } catch { }
|
|
575
|
+
vp.SetValue(text);
|
|
576
|
+
return "{\"ok\":true,\"via\":\"valuepattern\"}";
|
|
577
|
+
}
|
|
578
|
+
return "{\"ok\":false,\"error\":\"el elemento no acepta ValuePattern; usa ui_focus y luego type\"}";
|
|
579
|
+
}
|
|
580
|
+
catch (Exception ex) { return "{\"ok\":false,\"error\":" + Q(ex.Message) + "}"; }
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
public static string RefFocus(int id)
|
|
584
|
+
{
|
|
585
|
+
AutomationElement e = Ref(id);
|
|
586
|
+
if (e == null) return "{\"ok\":false,\"error\":\"ref inexistente; repite ui_snapshot\"}";
|
|
587
|
+
try { e.SetFocus(); Thread.Sleep(60); return "{\"ok\":true}"; }
|
|
588
|
+
catch (Exception ex) { return "{\"ok\":false,\"error\":" + Q(ex.Message) + "}"; }
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// ================= Teclado =================
|
|
592
|
+
static ushort VkOf(string k)
|
|
593
|
+
{
|
|
594
|
+
string s = k.Trim().ToLowerInvariant();
|
|
595
|
+
switch (s)
|
|
596
|
+
{
|
|
597
|
+
case "ctrl": case "control": return 0x11;
|
|
598
|
+
case "alt": case "menu": return 0x12;
|
|
599
|
+
case "shift": return 0x10;
|
|
600
|
+
case "win": case "super": case "cmd": case "meta": return 0x5B;
|
|
601
|
+
case "enter": case "return": return 0x0D;
|
|
602
|
+
case "esc": case "escape": return 0x1B;
|
|
603
|
+
case "tab": return 0x09;
|
|
604
|
+
case "space": case "spacebar": return 0x20;
|
|
605
|
+
case "backspace": case "bksp": return 0x08;
|
|
606
|
+
case "delete": case "del": return 0x2E;
|
|
607
|
+
case "insert": return 0x2D;
|
|
608
|
+
case "home": return 0x24;
|
|
609
|
+
case "end": return 0x23;
|
|
610
|
+
case "pageup": case "prior": return 0x21;
|
|
611
|
+
case "pagedown": case "next": return 0x22;
|
|
612
|
+
case "up": return 0x26;
|
|
613
|
+
case "down": return 0x28;
|
|
614
|
+
case "left": return 0x25;
|
|
615
|
+
case "right": return 0x27;
|
|
616
|
+
case "printscreen": return 0x2C;
|
|
617
|
+
case "capslock": return 0x14;
|
|
618
|
+
case "f1": return 0x70; case "f2": return 0x71; case "f3": return 0x72; case "f4": return 0x73;
|
|
619
|
+
case "f5": return 0x74; case "f6": return 0x75; case "f7": return 0x76; case "f8": return 0x77;
|
|
620
|
+
case "f9": return 0x78; case "f10": return 0x79; case "f11": return 0x7A; case "f12": return 0x7B;
|
|
621
|
+
}
|
|
622
|
+
if (s.Length == 1)
|
|
623
|
+
{
|
|
624
|
+
short v = VkKeyScan(s[0]);
|
|
625
|
+
if (v != -1) return (ushort)(v & 0xFF);
|
|
626
|
+
}
|
|
627
|
+
return 0;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
static bool IsExtended(ushort vk)
|
|
631
|
+
{
|
|
632
|
+
return vk == 0x25 || vk == 0x26 || vk == 0x27 || vk == 0x28 || vk == 0x2D ||
|
|
633
|
+
vk == 0x2E || vk == 0x24 || vk == 0x23 || vk == 0x21 || vk == 0x22 || vk == 0x5B;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
static void SendVk(ushort vk, bool up)
|
|
637
|
+
{
|
|
638
|
+
INPUT[] inp = new INPUT[1];
|
|
639
|
+
inp[0].type = 1;
|
|
640
|
+
inp[0].u.ki.wVk = vk;
|
|
641
|
+
inp[0].u.ki.wScan = 0;
|
|
642
|
+
inp[0].u.ki.dwFlags = (up ? KEYEVENTF_KEYUP : 0) | (IsExtended(vk) ? KEYEVENTF_EXTENDEDKEY : 0);
|
|
643
|
+
inp[0].u.ki.time = 0;
|
|
644
|
+
inp[0].u.ki.dwExtraInfo = IntPtr.Zero;
|
|
645
|
+
SendInput(1, inp, Marshal.SizeOf(typeof(INPUT)));
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
static void SendUnicode(char c, bool up)
|
|
649
|
+
{
|
|
650
|
+
INPUT[] inp = new INPUT[1];
|
|
651
|
+
inp[0].type = 1;
|
|
652
|
+
inp[0].u.ki.wVk = 0;
|
|
653
|
+
inp[0].u.ki.wScan = (ushort)c;
|
|
654
|
+
inp[0].u.ki.dwFlags = KEYEVENTF_UNICODE | (up ? KEYEVENTF_KEYUP : 0);
|
|
655
|
+
inp[0].u.ki.time = 0;
|
|
656
|
+
inp[0].u.ki.dwExtraInfo = IntPtr.Zero;
|
|
657
|
+
SendInput(1, inp, Marshal.SizeOf(typeof(INPUT)));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Escribe por SendInput con KEYEVENTF_UNICODE: acepta tildes, enes y emoji sin
|
|
661
|
+
// el infierno de escapes de SendKeys, y no pierde caracteres en textos largos.
|
|
662
|
+
public static string TypeText(string text, int delayMs)
|
|
663
|
+
{
|
|
664
|
+
if (text == null) text = "";
|
|
665
|
+
int d = delayMs <= 0 ? 6 : delayMs;
|
|
666
|
+
foreach (char c in text)
|
|
667
|
+
{
|
|
668
|
+
if (c == '\n' || c == '\r')
|
|
669
|
+
{
|
|
670
|
+
if (c == '\r') continue;
|
|
671
|
+
SendVk(0x0D, false); SendVk(0x0D, true);
|
|
672
|
+
}
|
|
673
|
+
else if (c == '\t') { SendVk(0x09, false); SendVk(0x09, true); }
|
|
674
|
+
else { SendUnicode(c, false); SendUnicode(c, true); }
|
|
675
|
+
Thread.Sleep(d);
|
|
676
|
+
}
|
|
677
|
+
return "{\"ok\":true,\"chars\":" + N(text.Length) + "}";
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Acepta "ctrl+s", "alt+Tab", "Return", "ctrl+shift+t".
|
|
681
|
+
public static string KeyCombo(string combo, int repeat)
|
|
682
|
+
{
|
|
683
|
+
if (combo == null || combo.Trim().Length == 0) return "{\"ok\":false,\"error\":\"combo vacio\"}";
|
|
684
|
+
string[] parts = combo.Replace(" ", "").Split('+');
|
|
685
|
+
List<ushort> mods = new List<ushort>();
|
|
686
|
+
ushort main = 0;
|
|
687
|
+
for (int i = 0; i < parts.Length; i++)
|
|
688
|
+
{
|
|
689
|
+
if (parts[i].Length == 0) { main = VkOf("+"); continue; }
|
|
690
|
+
ushort vk = VkOf(parts[i]);
|
|
691
|
+
if (vk == 0) return "{\"ok\":false,\"error\":" + Q("tecla desconocida: " + parts[i]) + "}";
|
|
692
|
+
bool isMod = (vk == 0x11 || vk == 0x12 || vk == 0x10 || vk == 0x5B);
|
|
693
|
+
if (isMod && i < parts.Length - 1) mods.Add(vk); else main = vk;
|
|
694
|
+
}
|
|
695
|
+
if (main == 0) return "{\"ok\":false,\"error\":\"falta la tecla principal\"}";
|
|
696
|
+
int r = repeat <= 0 ? 1 : Math.Min(repeat, 100);
|
|
697
|
+
for (int k = 0; k < r; k++)
|
|
698
|
+
{
|
|
699
|
+
for (int i = 0; i < mods.Count; i++) SendVk(mods[i], false);
|
|
700
|
+
SendVk(main, false);
|
|
701
|
+
Thread.Sleep(20);
|
|
702
|
+
SendVk(main, true);
|
|
703
|
+
for (int i = mods.Count - 1; i >= 0; i--) SendVk(mods[i], true);
|
|
704
|
+
if (r > 1) Thread.Sleep(30);
|
|
705
|
+
}
|
|
706
|
+
return "{\"ok\":true,\"combo\":" + Q(combo) + ",\"repeat\":" + N(r) + "}";
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ================= Raton =================
|
|
710
|
+
const uint MOUSEEVENTF_VIRTUALDESK = 0x4000;
|
|
711
|
+
|
|
712
|
+
static void MouseAbs(int x, int y, uint extraFlags, uint data)
|
|
713
|
+
{
|
|
714
|
+
int vx = GetSystemMetrics(76), vy = GetSystemMetrics(77);
|
|
715
|
+
int vw = GetSystemMetrics(78), vh = GetSystemMetrics(79);
|
|
716
|
+
if (vw <= 0) vw = 1; if (vh <= 0) vh = 1;
|
|
717
|
+
int nx = (int)Math.Round((double)(x - vx) * 65535.0 / (double)(vw - 1));
|
|
718
|
+
int ny = (int)Math.Round((double)(y - vy) * 65535.0 / (double)(vh - 1));
|
|
719
|
+
INPUT[] inp = new INPUT[1];
|
|
720
|
+
inp[0].type = 0;
|
|
721
|
+
inp[0].u.mi.dx = nx;
|
|
722
|
+
inp[0].u.mi.dy = ny;
|
|
723
|
+
inp[0].u.mi.mouseData = data;
|
|
724
|
+
inp[0].u.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE | extraFlags;
|
|
725
|
+
inp[0].u.mi.time = 0;
|
|
726
|
+
inp[0].u.mi.dwExtraInfo = IntPtr.Zero;
|
|
727
|
+
SendInput(1, inp, Marshal.SizeOf(typeof(INPUT)));
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
public static string MoveMouse(int x, int y)
|
|
731
|
+
{
|
|
732
|
+
MouseAbs(x, y, 0, 0);
|
|
733
|
+
Thread.Sleep(30);
|
|
734
|
+
return "{\"ok\":true,\"x\":" + N(x) + ",\"y\":" + N(y) + "}";
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
public static string Click(int x, int y, string button, int count)
|
|
738
|
+
{
|
|
739
|
+
uint down = MOUSEEVENTF_LEFTDOWN, up = MOUSEEVENTF_LEFTUP;
|
|
740
|
+
string b = (button == null) ? "left" : button.ToLowerInvariant();
|
|
741
|
+
if (b == "right") { down = MOUSEEVENTF_RIGHTDOWN; up = MOUSEEVENTF_RIGHTUP; }
|
|
742
|
+
else if (b == "middle") { down = MOUSEEVENTF_MIDDLEDOWN; up = MOUSEEVENTF_MIDDLEUP; }
|
|
743
|
+
MouseAbs(x, y, 0, 0);
|
|
744
|
+
Thread.Sleep(40);
|
|
745
|
+
int c = count <= 0 ? 1 : Math.Min(count, 3);
|
|
746
|
+
for (int i = 0; i < c; i++)
|
|
747
|
+
{
|
|
748
|
+
MouseAbs(x, y, down, 0);
|
|
749
|
+
Thread.Sleep(18);
|
|
750
|
+
MouseAbs(x, y, up, 0);
|
|
751
|
+
if (i < c - 1) Thread.Sleep(70);
|
|
752
|
+
}
|
|
753
|
+
IntPtr fg = GetForegroundWindow();
|
|
754
|
+
return "{\"ok\":true,\"x\":" + N(x) + ",\"y\":" + N(y) + ",\"button\":" + Q(b) +
|
|
755
|
+
",\"count\":" + N(c) + ",\"window\":" + Q(Title(fg)) + "}";
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
public static string Drag(int x1, int y1, int x2, int y2)
|
|
759
|
+
{
|
|
760
|
+
MouseAbs(x1, y1, 0, 0);
|
|
761
|
+
Thread.Sleep(60);
|
|
762
|
+
MouseAbs(x1, y1, MOUSEEVENTF_LEFTDOWN, 0);
|
|
763
|
+
Thread.Sleep(80);
|
|
764
|
+
int steps = 24;
|
|
765
|
+
for (int i = 1; i <= steps; i++)
|
|
766
|
+
{
|
|
767
|
+
int cx = x1 + (int)Math.Round((x2 - x1) * ((double)i / steps));
|
|
768
|
+
int cy = y1 + (int)Math.Round((y2 - y1) * ((double)i / steps));
|
|
769
|
+
MouseAbs(cx, cy, 0, 0);
|
|
770
|
+
Thread.Sleep(12);
|
|
771
|
+
}
|
|
772
|
+
Thread.Sleep(60);
|
|
773
|
+
MouseAbs(x2, y2, MOUSEEVENTF_LEFTUP, 0);
|
|
774
|
+
return "{\"ok\":true}";
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
public static string Scroll(int x, int y, string direction, int amount)
|
|
778
|
+
{
|
|
779
|
+
string d = (direction == null) ? "down" : direction.ToLowerInvariant();
|
|
780
|
+
int a = amount <= 0 ? 3 : amount;
|
|
781
|
+
if (x >= 0 && y >= 0) { MouseAbs(x, y, 0, 0); Thread.Sleep(30); }
|
|
782
|
+
for (int i = 0; i < a; i++)
|
|
783
|
+
{
|
|
784
|
+
if (d == "up") MouseAbs(x, y, MOUSEEVENTF_WHEEL, 120);
|
|
785
|
+
else if (d == "down") MouseAbs(x, y, MOUSEEVENTF_WHEEL, unchecked((uint)-120));
|
|
786
|
+
else if (d == "right") MouseAbs(x, y, MOUSEEVENTF_HWHEEL, 120);
|
|
787
|
+
else MouseAbs(x, y, MOUSEEVENTF_HWHEEL, unchecked((uint)-120));
|
|
788
|
+
Thread.Sleep(40);
|
|
789
|
+
}
|
|
790
|
+
return "{\"ok\":true,\"direction\":" + Q(d) + ",\"amount\":" + N(a) + "}";
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Clic en SEGUNDO PLANO: manda el mensaje directo a la ventana destino.
|
|
794
|
+
// No mueve el cursor real ni roba el foco, asi el usuario puede seguir
|
|
795
|
+
// trabajando en otra ventana mientras el agente opera.
|
|
796
|
+
public static string BgClick(IntPtr hwnd, int screenX, int screenY, string button, int count)
|
|
797
|
+
{
|
|
798
|
+
if (hwnd == IntPtr.Zero) return "{\"ok\":false,\"error\":\"ventana no encontrada\"}";
|
|
799
|
+
POINT p; p.X = screenX; p.Y = screenY;
|
|
800
|
+
if (!ScreenToClient(hwnd, ref p)) return "{\"ok\":false,\"error\":\"no se pudo convertir a coordenadas de cliente\"}";
|
|
801
|
+
IntPtr lp = (IntPtr)((p.Y << 16) | (p.X & 0xFFFF));
|
|
802
|
+
uint dn = WM_LBUTTONDOWN, up = WM_LBUTTONUP; IntPtr wp = (IntPtr)1;
|
|
803
|
+
string b = (button == null) ? "left" : button.ToLowerInvariant();
|
|
804
|
+
if (b == "right") { dn = WM_RBUTTONDOWN; up = WM_RBUTTONUP; wp = (IntPtr)2; }
|
|
805
|
+
int c = count <= 0 ? 1 : Math.Min(count, 3);
|
|
806
|
+
PostMessage(hwnd, WM_MOUSEMOVE, IntPtr.Zero, lp);
|
|
807
|
+
for (int i = 0; i < c; i++)
|
|
808
|
+
{
|
|
809
|
+
PostMessage(hwnd, dn, wp, lp);
|
|
810
|
+
Thread.Sleep(20);
|
|
811
|
+
PostMessage(hwnd, up, IntPtr.Zero, lp);
|
|
812
|
+
if (i < c - 1) Thread.Sleep(60);
|
|
813
|
+
}
|
|
814
|
+
return "{\"ok\":true,\"mode\":\"background\",\"clientX\":" + N(p.X) + ",\"clientY\":" + N(p.Y) +
|
|
815
|
+
",\"window\":" + Q(Title(hwnd)) + "}";
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
public static string BgType(IntPtr hwnd, string text)
|
|
819
|
+
{
|
|
820
|
+
if (hwnd == IntPtr.Zero) return "{\"ok\":false,\"error\":\"ventana no encontrada\"}";
|
|
821
|
+
if (text == null) text = "";
|
|
822
|
+
foreach (char c in text)
|
|
823
|
+
{
|
|
824
|
+
PostMessage(hwnd, WM_CHAR, (IntPtr)c, IntPtr.Zero);
|
|
825
|
+
Thread.Sleep(4);
|
|
826
|
+
}
|
|
827
|
+
return "{\"ok\":true,\"mode\":\"background\",\"chars\":" + N(text.Length) + "}";
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
public static IntPtr Foreground() { return GetForegroundWindow(); }
|
|
831
|
+
public static string ForegroundInfo() { return WindowInfo(GetForegroundWindow()); }
|
|
832
|
+
public static string CursorPos()
|
|
833
|
+
{
|
|
834
|
+
POINT c; GetCursorPos(out c);
|
|
835
|
+
return "{\"ok\":true,\"x\":" + N(c.X) + ",\"y\":" + N(c.Y) + "}";
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
'@
|
|
839
|
+
|
|
840
|
+
Add-Type -TypeDefinition $csharp -ReferencedAssemblies @(
|
|
841
|
+
'System.Drawing','System.Windows.Forms','UIAutomationClient','UIAutomationTypes','WindowsBase'
|
|
842
|
+
) -ErrorAction Stop
|
|
843
|
+
|
|
844
|
+
# DPI-aware ANTES de cualquier medicion: sin esto Windows reporta la pantalla
|
|
845
|
+
# escalada (p.ej. 2048x1152 en un monitor 2560x1440 al 125%) y TODOS los clics
|
|
846
|
+
# salen desviados ~25% hacia arriba-izquierda. Era la causa raiz de los fallos.
|
|
847
|
+
$dpiMode = [Choco]::SetDpiAware()
|
|
848
|
+
|
|
849
|
+
function Get-Prop($o, $n, $d) {
|
|
850
|
+
if ($null -eq $o) { return $d }
|
|
851
|
+
$p = $o.PSObject.Properties[$n]
|
|
852
|
+
if ($null -eq $p) { return $d }
|
|
853
|
+
if ($null -eq $p.Value) { return $d }
|
|
854
|
+
return $p.Value
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function Resolve-Hwnd($cmd) {
|
|
858
|
+
$h = [int](Get-Prop $cmd 'hwnd' 0)
|
|
859
|
+
if ($h -ne 0) { return [IntPtr]$h }
|
|
860
|
+
$w = [string](Get-Prop $cmd 'window' '')
|
|
861
|
+
if ($w.Trim() -ne '') { return [Choco]::FindWindow($w) }
|
|
862
|
+
return [Choco]::Foreground()
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
Write-Output ("CHOCO#0#{""ok"":true,""ready"":true,""dpi"":""" + $dpiMode + """}")
|
|
866
|
+
|
|
867
|
+
while ($true) {
|
|
868
|
+
$line = [Console]::In.ReadLine()
|
|
869
|
+
if ($null -eq $line) { break }
|
|
870
|
+
if ($line.Trim() -eq '') { continue }
|
|
871
|
+
$id = 0
|
|
872
|
+
try {
|
|
873
|
+
$cmd = $line | ConvertFrom-Json
|
|
874
|
+
$id = [int](Get-Prop $cmd 'id' 0)
|
|
875
|
+
$action = [string](Get-Prop $cmd 'action' '')
|
|
876
|
+
$res = ''
|
|
877
|
+
switch ($action) {
|
|
878
|
+
'ping' { $res = '{"ok":true,"pong":true,"dpi":"' + $dpiMode + '"}' }
|
|
879
|
+
'screen_info' { $res = [Choco]::ScreenInfo() }
|
|
880
|
+
'cursor_position' { $res = [Choco]::CursorPos() }
|
|
881
|
+
'list_windows' { $res = '{"ok":true,"windows":' + [Choco]::ListWindows([string](Get-Prop $cmd 'filter' '')) + '}' }
|
|
882
|
+
'find_window' {
|
|
883
|
+
$h = [Choco]::FindWindow([string](Get-Prop $cmd 'window' ''))
|
|
884
|
+
$info = [Choco]::WindowInfo($h)
|
|
885
|
+
$amb = [Choco]::Ambiguity()
|
|
886
|
+
$res = ($info.TrimEnd('}')) + ',"ambiguity":' + $amb + '}'
|
|
887
|
+
}
|
|
888
|
+
'active_window' { $res = [Choco]::ForegroundInfo() }
|
|
889
|
+
'focus_window' {
|
|
890
|
+
$h = Resolve-Hwnd $cmd
|
|
891
|
+
$r = [Choco]::FocusWindow($h)
|
|
892
|
+
$amb = [Choco]::Ambiguity()
|
|
893
|
+
$res = ($r.TrimEnd('}')) + ',"ambiguity":' + $amb + '}'
|
|
894
|
+
}
|
|
895
|
+
'wait' { Start-Sleep -Milliseconds ([int](Get-Prop $cmd 'ms' 1000)); $res = '{"ok":true}' }
|
|
896
|
+
'capture_screen' {
|
|
897
|
+
$res = [Choco]::CaptureScreen([string](Get-Prop $cmd 'path' ''), [int](Get-Prop $cmd 'maxLongEdge' 1568), [bool](Get-Prop $cmd 'cursor' $true))
|
|
898
|
+
}
|
|
899
|
+
'capture_window' {
|
|
900
|
+
$res = [Choco]::CaptureWindow((Resolve-Hwnd $cmd), [string](Get-Prop $cmd 'path' ''), [int](Get-Prop $cmd 'maxLongEdge' 1568), [bool](Get-Prop $cmd 'clientOnly' $false))
|
|
901
|
+
}
|
|
902
|
+
'ui_snapshot' {
|
|
903
|
+
$res = [Choco]::Snapshot((Resolve-Hwnd $cmd), [int](Get-Prop $cmd 'max' 250), [string](Get-Prop $cmd 'filter' ''), [bool](Get-Prop $cmd 'interactiveOnly' $true))
|
|
904
|
+
}
|
|
905
|
+
'ui_click' { $res = [Choco]::RefInvoke([int](Get-Prop $cmd 'ref' 0)) }
|
|
906
|
+
'ui_set_value' { $res = [Choco]::RefSetValue([int](Get-Prop $cmd 'ref' 0), [string](Get-Prop $cmd 'text' ''), [bool](Get-Prop $cmd 'replace' $false)) }
|
|
907
|
+
'ui_focus' { $res = [Choco]::RefFocus([int](Get-Prop $cmd 'ref' 0)) }
|
|
908
|
+
'ui_rect' { $res = [Choco]::RefRect([int](Get-Prop $cmd 'ref' 0)) }
|
|
909
|
+
'click' { $res = [Choco]::Click([int](Get-Prop $cmd 'x' 0), [int](Get-Prop $cmd 'y' 0), [string](Get-Prop $cmd 'button' 'left'), [int](Get-Prop $cmd 'count' 1)) }
|
|
910
|
+
'move' { $res = [Choco]::MoveMouse([int](Get-Prop $cmd 'x' 0), [int](Get-Prop $cmd 'y' 0)) }
|
|
911
|
+
'drag' { $res = [Choco]::Drag([int](Get-Prop $cmd 'x1' 0), [int](Get-Prop $cmd 'y1' 0), [int](Get-Prop $cmd 'x2' 0), [int](Get-Prop $cmd 'y2' 0)) }
|
|
912
|
+
'scroll' { $res = [Choco]::Scroll([int](Get-Prop $cmd 'x' (-1)), [int](Get-Prop $cmd 'y' (-1)), [string](Get-Prop $cmd 'direction' 'down'), [int](Get-Prop $cmd 'amount' 3)) }
|
|
913
|
+
'type' { $res = [Choco]::TypeText([string](Get-Prop $cmd 'text' ''), [int](Get-Prop $cmd 'delay' 6)) }
|
|
914
|
+
'key' { $res = [Choco]::KeyCombo([string](Get-Prop $cmd 'keys' ''), [int](Get-Prop $cmd 'repeat' 1)) }
|
|
915
|
+
'bg_click' { $res = [Choco]::BgClick((Resolve-Hwnd $cmd), [int](Get-Prop $cmd 'x' 0), [int](Get-Prop $cmd 'y' 0), [string](Get-Prop $cmd 'button' 'left'), [int](Get-Prop $cmd 'count' 1)) }
|
|
916
|
+
'bg_type' { $res = [Choco]::BgType((Resolve-Hwnd $cmd), [string](Get-Prop $cmd 'text' '')) }
|
|
917
|
+
default { $res = '{"ok":false,"error":"accion desconocida: ' + [Choco]::Esc($action) + '"}' }
|
|
918
|
+
}
|
|
919
|
+
Write-Output ("CHOCO#" + $id + "#" + $res)
|
|
920
|
+
} catch {
|
|
921
|
+
$msg = [Choco]::Esc($_.Exception.Message)
|
|
922
|
+
Write-Output ("CHOCO#" + $id + "#" + '{"ok":false,"error":"' + $msg + '"}')
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
`;
|