appium-novawindows2-driver 1.1.21 → 1.1.22
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/LICENSE +201 -201
- package/README.md +716 -716
- package/build/lib/commands/app.js +14 -14
- package/build/lib/commands/extension.js +14 -14
- package/build/lib/commands/file.js +26 -26
- package/build/lib/commands/functions.js +380 -380
- package/build/lib/commands/index.d.ts +3 -3
- package/build/lib/commands/index.d.ts.map +1 -1
- package/build/lib/driver.js +9 -9
- package/build/lib/powershell/elements.js +544 -544
- package/build/lib/powershell/win32.js +807 -807
- package/package.json +86 -85
|
@@ -7,812 +7,812 @@ exports.WIN32_HELPER_SCRIPT = void 0;
|
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
const dllPath = node_path_1.default.resolve(__dirname, '..', 'dll', 'Win32Helper.dll').replace(/\\/g, '\\\\');
|
|
9
9
|
const dllDir = node_path_1.default.resolve(__dirname, '..', 'dll').replace(/\\/g, '\\\\');
|
|
10
|
-
exports.WIN32_HELPER_SCRIPT = `
|
|
11
|
-
# Win32Helper - Native Win32 helpers compiled as C# DLL
|
|
12
|
-
# Provides: window management, MSAA property retrieval, process validation
|
|
13
|
-
|
|
14
|
-
$dllPath = '${dllPath}'
|
|
15
|
-
$dllDir = '${dllDir}'
|
|
16
|
-
|
|
17
|
-
# Check if Win32Helper is already loaded
|
|
18
|
-
if (-not ([System.Management.Automation.PSTypeName]'Win32Helper').Type) {
|
|
19
|
-
|
|
20
|
-
# Check if DLL exists
|
|
21
|
-
if (Test-Path $dllPath) {
|
|
22
|
-
Write-Output "Loading Win32Helper from existing DLL: $dllPath"
|
|
23
|
-
Add-Type -Path $dllPath -ErrorAction Stop
|
|
24
|
-
} else {
|
|
25
|
-
Write-Output "Compiling Win32Helper.dll..."
|
|
26
|
-
|
|
27
|
-
# Create DLL directory if it doesn't exist
|
|
28
|
-
if (-not (Test-Path $dllDir)) {
|
|
29
|
-
New-Item -ItemType Directory -Path $dllDir -Force | Out-Null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
# C# source code
|
|
33
|
-
$code = @'
|
|
34
|
-
using System;
|
|
35
|
-
using System.Runtime.InteropServices;
|
|
36
|
-
using System.Collections;
|
|
37
|
-
using System.Collections.Generic;
|
|
38
|
-
using System.Diagnostics;
|
|
39
|
-
using System.Reflection;
|
|
40
|
-
using System.Runtime.ExceptionServices;
|
|
41
|
-
using System.Security;
|
|
42
|
-
using System.Threading;
|
|
43
|
-
|
|
44
|
-
/// <summary>
|
|
45
|
-
/// Native Win32 helper for Appium Windows driver.
|
|
46
|
-
///
|
|
47
|
-
/// Window management:
|
|
48
|
-
/// BringToForeground, SetTopMost, ClearTopMost, MinimizeWindow, RestoreWindow,
|
|
49
|
-
/// IsMinimized, IsVisible, GetWindowText, GetWindowRect, GetWindowProcessId
|
|
50
|
-
///
|
|
51
|
-
/// MSAA property retrieval (with built-in safety):
|
|
52
|
-
/// SetExpectedPid - set expected PID + optional BringToForeground
|
|
53
|
-
/// GetLegacyProperty - single property via hwnd (AccessibleObjectFromWindow)
|
|
54
|
-
/// GetLegacyPropertyWithFallback - hwnd -> PID check -> point fallback
|
|
55
|
-
/// GetLegacyPropsWithFallback - batch: all 9 MSAA properties
|
|
56
|
-
/// </summary>
|
|
57
|
-
public static class Win32Helper {
|
|
58
|
-
|
|
59
|
-
// ==================================================================
|
|
60
|
-
// P/Invoke - MSAA (oleacc.dll)
|
|
61
|
-
// ==================================================================
|
|
62
|
-
|
|
63
|
-
[DllImport("oleacc.dll")]
|
|
64
|
-
private static extern int AccessibleObjectFromWindow(
|
|
65
|
-
IntPtr hwnd, uint dwId, ref Guid riid,
|
|
66
|
-
[MarshalAs(UnmanagedType.Interface)] out object ppvObject);
|
|
67
|
-
|
|
68
|
-
[DllImport("oleacc.dll")]
|
|
69
|
-
private static extern int AccessibleObjectFromPoint(
|
|
70
|
-
POINT pt,
|
|
71
|
-
[MarshalAs(UnmanagedType.Interface)] out object ppacc,
|
|
72
|
-
[MarshalAs(UnmanagedType.Struct)] out object pvarChild);
|
|
73
|
-
|
|
74
|
-
// ==================================================================
|
|
75
|
-
// P/Invoke - Window management (user32.dll)
|
|
76
|
-
// ==================================================================
|
|
77
|
-
|
|
78
|
-
[DllImport("user32.dll")]
|
|
79
|
-
private static extern IntPtr GetForegroundWindow();
|
|
80
|
-
|
|
81
|
-
[DllImport("user32.dll")]
|
|
82
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
83
|
-
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
84
|
-
|
|
85
|
-
[DllImport("user32.dll")]
|
|
86
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
87
|
-
private static extern bool BringWindowToTop(IntPtr hWnd);
|
|
88
|
-
|
|
89
|
-
[DllImport("user32.dll")]
|
|
90
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
91
|
-
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
92
|
-
|
|
93
|
-
[DllImport("user32.dll")]
|
|
94
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
95
|
-
private static extern bool IsIconic(IntPtr hWnd);
|
|
96
|
-
|
|
97
|
-
[DllImport("user32.dll")]
|
|
98
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
99
|
-
private static extern bool IsWindowVisible(IntPtr hWnd);
|
|
100
|
-
|
|
101
|
-
[DllImport("user32.dll")]
|
|
102
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
103
|
-
private static extern bool IsWindow(IntPtr hWnd);
|
|
104
|
-
|
|
105
|
-
[DllImport("user32.dll")]
|
|
106
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
107
|
-
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
|
108
|
-
|
|
109
|
-
[DllImport("user32.dll")]
|
|
110
|
-
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
111
|
-
|
|
112
|
-
[DllImport("user32.dll")]
|
|
113
|
-
private static extern IntPtr WindowFromPoint(POINT pt);
|
|
114
|
-
|
|
115
|
-
[DllImport("user32.dll")]
|
|
116
|
-
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
|
117
|
-
|
|
118
|
-
[DllImport("user32.dll")]
|
|
119
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
120
|
-
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
|
|
121
|
-
int X, int Y, int cx, int cy, uint uFlags);
|
|
122
|
-
|
|
123
|
-
[DllImport("user32.dll")]
|
|
124
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
125
|
-
private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref uint pvParam, uint fWinIni);
|
|
126
|
-
|
|
127
|
-
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
128
|
-
private static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
|
|
129
|
-
|
|
130
|
-
[DllImport("user32.dll")]
|
|
131
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
132
|
-
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
133
|
-
|
|
134
|
-
[DllImport("kernel32.dll")]
|
|
135
|
-
private static extern uint GetCurrentThreadId();
|
|
136
|
-
|
|
137
|
-
// ==================================================================
|
|
138
|
-
// Structs and constants
|
|
139
|
-
// ==================================================================
|
|
140
|
-
|
|
141
|
-
[StructLayout(LayoutKind.Sequential)]
|
|
142
|
-
public struct POINT { public int x; public int y; }
|
|
143
|
-
|
|
144
|
-
[StructLayout(LayoutKind.Sequential)]
|
|
145
|
-
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
|
|
146
|
-
|
|
147
|
-
[StructLayout(LayoutKind.Sequential)]
|
|
148
|
-
private struct INPUT {
|
|
149
|
-
public uint type;
|
|
150
|
-
public INPUTUNION u;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
[StructLayout(LayoutKind.Explicit)]
|
|
154
|
-
private struct INPUTUNION {
|
|
155
|
-
[FieldOffset(0)] public KEYBDINPUT ki;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
[StructLayout(LayoutKind.Sequential)]
|
|
159
|
-
private struct KEYBDINPUT {
|
|
160
|
-
public ushort wVk;
|
|
161
|
-
public ushort wScan;
|
|
162
|
-
public uint dwFlags;
|
|
163
|
-
public uint time;
|
|
164
|
-
public IntPtr dwExtraInfo;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private const int SW_MINIMIZE = 6;
|
|
168
|
-
private const int SW_RESTORE = 9;
|
|
169
|
-
private const int SW_SHOW = 5;
|
|
170
|
-
private const ushort VK_MENU = 0xA4;
|
|
171
|
-
private const uint INPUT_KEYBOARD = 1;
|
|
172
|
-
private const uint KEYEVENTF_KEYUP = 0x0002;
|
|
173
|
-
private const uint SWP_NOMOVE = 0x0002;
|
|
174
|
-
private const uint SWP_NOSIZE = 0x0001;
|
|
175
|
-
private const uint SWP_SHOWWINDOW = 0x0040;
|
|
176
|
-
private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
|
|
177
|
-
private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
|
|
178
|
-
private const uint SPIF_SENDWININICHANGE = 0x02;
|
|
179
|
-
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
|
180
|
-
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
|
181
|
-
|
|
182
|
-
// Context for MSAA queries - set via SetExpectedPid() before calling GetLegacy* methods
|
|
183
|
-
private static uint _expectedPid = 0;
|
|
184
|
-
|
|
185
|
-
// ==================================================================
|
|
186
|
-
// Context setup - call before MSAA queries
|
|
187
|
-
// ==================================================================
|
|
188
|
-
|
|
189
|
-
/// <summary>
|
|
190
|
-
/// Set expected process ID for MSAA point-based queries.
|
|
191
|
-
/// AccessibleObjectFromPoint will be skipped if the window at the target
|
|
192
|
-
/// coordinates belongs to a different process.
|
|
193
|
-
/// </summary>
|
|
194
|
-
public static void SetExpectedPid(uint expectedPid) {
|
|
195
|
-
_expectedPid = expectedPid;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/// <summary>
|
|
199
|
-
/// Set expected PID and bring the ancestor window to foreground.
|
|
200
|
-
/// Call before GetLegacyPropsWithFallback for getProperty("all").
|
|
201
|
-
/// </summary>
|
|
202
|
-
public static void SetExpectedPidAndForeground(uint expectedPid, IntPtr ancestorHwnd) {
|
|
203
|
-
_expectedPid = expectedPid;
|
|
204
|
-
if (ancestorHwnd != IntPtr.Zero) {
|
|
205
|
-
BringToForeground(ancestorHwnd);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// ==================================================================
|
|
210
|
-
// Window management
|
|
211
|
-
// ==================================================================
|
|
212
|
-
|
|
213
|
-
/// <summary>
|
|
214
|
-
/// Force a window to foreground even from a background process.
|
|
215
|
-
/// Uses escalating strategies modeled after AutoHotkey WinActivate:
|
|
216
|
-
/// 1. Restore if minimized
|
|
217
|
-
/// 2. AttachThreadInput to foreground + target threads
|
|
218
|
-
/// 3. Retry loop: SetForegroundWindow + BringWindowToTop
|
|
219
|
-
/// 4. SendInput ALT key (satisfies "last input" requirement)
|
|
220
|
-
/// 5. SPI_SETFOREGROUNDLOCKTIMEOUT = 0 (temporarily disable lock)
|
|
221
|
-
/// 6. HWND_TOPMOST toggle (last resort Z-order force)
|
|
222
|
-
/// </summary>
|
|
223
|
-
public static bool BringToForeground(IntPtr hwnd) {
|
|
224
|
-
if (hwnd == IntPtr.Zero || !IsWindow(hwnd)) return false;
|
|
225
|
-
if (GetForegroundWindow() == hwnd) return true;
|
|
226
|
-
if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE);
|
|
227
|
-
|
|
228
|
-
uint unusedPid = 0;
|
|
229
|
-
uint curThread = GetCurrentThreadId();
|
|
230
|
-
IntPtr foreWnd = GetForegroundWindow();
|
|
231
|
-
uint foreThread = GetWindowThreadProcessId(foreWnd, out unusedPid);
|
|
232
|
-
uint targetThread = GetWindowThreadProcessId(hwnd, out unusedPid);
|
|
233
|
-
|
|
234
|
-
bool attachedCurFore = false;
|
|
235
|
-
bool attachedForeTarget = false;
|
|
236
|
-
|
|
237
|
-
try {
|
|
238
|
-
if (curThread != foreThread)
|
|
239
|
-
attachedCurFore = AttachThreadInput(curThread, foreThread, true);
|
|
240
|
-
if (foreThread != targetThread)
|
|
241
|
-
attachedForeTarget = AttachThreadInput(foreThread, targetThread, true);
|
|
242
|
-
|
|
243
|
-
// Retry with escalation
|
|
244
|
-
for (int attempt = 0; attempt < 5; attempt++) {
|
|
245
|
-
BringWindowToTop(hwnd);
|
|
246
|
-
ShowWindow(hwnd, SW_SHOW);
|
|
247
|
-
SetForegroundWindow(hwnd);
|
|
248
|
-
Thread.Sleep(10);
|
|
249
|
-
if (GetForegroundWindow() == hwnd) return true;
|
|
250
|
-
|
|
251
|
-
// ALT key trick via SendInput
|
|
252
|
-
SendAltKey(false);
|
|
253
|
-
SendAltKey(true);
|
|
254
|
-
SetForegroundWindow(hwnd);
|
|
255
|
-
Thread.Sleep(10);
|
|
256
|
-
if (GetForegroundWindow() == hwnd) return true;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Temporarily disable foreground lock timeout
|
|
260
|
-
uint lockTimeout = 0;
|
|
261
|
-
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, ref lockTimeout, 0);
|
|
262
|
-
if (lockTimeout > 0) {
|
|
263
|
-
uint zero = 0;
|
|
264
|
-
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref zero, SPIF_SENDWININICHANGE);
|
|
265
|
-
SetForegroundWindow(hwnd);
|
|
266
|
-
BringWindowToTop(hwnd);
|
|
267
|
-
ShowWindow(hwnd, SW_SHOW);
|
|
268
|
-
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref lockTimeout, SPIF_SENDWININICHANGE);
|
|
269
|
-
Thread.Sleep(10);
|
|
270
|
-
if (GetForegroundWindow() == hwnd) return true;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Last resort: TOPMOST toggle
|
|
274
|
-
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
275
|
-
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
276
|
-
return GetForegroundWindow() == hwnd;
|
|
277
|
-
} catch {
|
|
278
|
-
return false;
|
|
279
|
-
} finally {
|
|
280
|
-
if (attachedCurFore) AttachThreadInput(curThread, foreThread, false);
|
|
281
|
-
if (attachedForeTarget) AttachThreadInput(foreThread, targetThread, false);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/// <summary>Set window as always-on-top.</summary>
|
|
286
|
-
public static bool SetTopMost(IntPtr hwnd) {
|
|
287
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
288
|
-
return SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/// <summary>Remove always-on-top from window.</summary>
|
|
292
|
-
public static bool ClearTopMost(IntPtr hwnd) {
|
|
293
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
294
|
-
return SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
public static bool MinimizeWindow(IntPtr hwnd) {
|
|
298
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
299
|
-
return ShowWindow(hwnd, SW_MINIMIZE);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
public static bool RestoreWindow(IntPtr hwnd) {
|
|
303
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
304
|
-
return ShowWindow(hwnd, SW_RESTORE);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
public static bool IsMinimized(IntPtr hwnd) {
|
|
308
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
309
|
-
return IsIconic(hwnd);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
public static bool IsVisible(IntPtr hwnd) {
|
|
313
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
314
|
-
return IsWindowVisible(hwnd);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
public static string GetText(IntPtr hwnd) {
|
|
318
|
-
if (hwnd == IntPtr.Zero) return null;
|
|
319
|
-
System.Text.StringBuilder sb = new System.Text.StringBuilder(512);
|
|
320
|
-
GetWindowText(hwnd, sb, sb.Capacity);
|
|
321
|
-
string text = sb.ToString();
|
|
322
|
-
return text.Length > 0 ? text : null;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
public static RECT GetRect(IntPtr hwnd) {
|
|
326
|
-
RECT rect;
|
|
327
|
-
rect.Left = 0; rect.Top = 0; rect.Right = 0; rect.Bottom = 0;
|
|
328
|
-
if (hwnd != IntPtr.Zero) GetWindowRect(hwnd, out rect);
|
|
329
|
-
return rect;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
public static uint GetProcessId(IntPtr hwnd) {
|
|
333
|
-
if (hwnd == IntPtr.Zero) return 0;
|
|
334
|
-
uint pid = 0;
|
|
335
|
-
GetWindowThreadProcessId(hwnd, out pid);
|
|
336
|
-
return pid;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// ==================================================================
|
|
340
|
-
// Window enumeration (for scope-session)
|
|
341
|
-
// ==================================================================
|
|
342
|
-
|
|
343
|
-
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
344
|
-
|
|
345
|
-
[DllImport("user32.dll")]
|
|
346
|
-
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
347
|
-
|
|
348
|
-
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
349
|
-
private static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);
|
|
350
|
-
|
|
351
|
-
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
352
|
-
private static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, System.Text.StringBuilder lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult);
|
|
353
|
-
|
|
354
|
-
private const uint WM_GETTEXT = 0x000D;
|
|
355
|
-
private const uint SMTO_ABORTIFHUNG = 0x0002;
|
|
356
|
-
private const uint SMTO_BLOCK = 0x0001;
|
|
357
|
-
|
|
358
|
-
/// <summary>
|
|
359
|
-
/// Find a top-level window by exact Name and Win32 ClassName.
|
|
360
|
-
/// Uses EnumWindows + SendMessageTimeout(WM_GETTEXT) — avoids UIA FindFirst
|
|
361
|
-
/// which blocks for 20+ s against SecureAge's slow UIA provider.
|
|
362
|
-
/// Polls for up to timeoutMs milliseconds (pollMs interval). Returns IntPtr.Zero on timeout.
|
|
363
|
-
/// </summary>
|
|
364
|
-
public static IntPtr FindWindowByName(string targetName, string targetClass, int timeoutMs, int pollMs) {
|
|
365
|
-
var sw = Stopwatch.StartNew();
|
|
366
|
-
do {
|
|
367
|
-
var candidates = new List<IntPtr>();
|
|
368
|
-
EnumWindowsProc cb = (h, l) => {
|
|
369
|
-
if (!IsWindowVisible(h)) return true;
|
|
370
|
-
if (!string.IsNullOrEmpty(targetClass)) {
|
|
371
|
-
var cls = new System.Text.StringBuilder(256);
|
|
372
|
-
GetClassName(h, cls, 256);
|
|
373
|
-
if (cls.ToString() != targetClass) return true;
|
|
374
|
-
}
|
|
375
|
-
candidates.Add(h);
|
|
376
|
-
return true;
|
|
377
|
-
};
|
|
378
|
-
EnumWindows(cb, IntPtr.Zero);
|
|
379
|
-
foreach (var h in candidates) {
|
|
380
|
-
var tb = new System.Text.StringBuilder(512);
|
|
381
|
-
IntPtr res = IntPtr.Zero;
|
|
382
|
-
IntPtr rc = SendMessageTimeout(h, WM_GETTEXT, (IntPtr)512, tb,
|
|
383
|
-
SMTO_ABORTIFHUNG | SMTO_BLOCK, 500, out res);
|
|
384
|
-
if (rc != IntPtr.Zero && tb.ToString() == targetName) return h;
|
|
385
|
-
}
|
|
386
|
-
long elapsed = sw.ElapsedMilliseconds;
|
|
387
|
-
if (elapsed >= timeoutMs) break;
|
|
388
|
-
if (pollMs > 0) Thread.Sleep(Math.Min(pollMs, (int)(timeoutMs - elapsed)));
|
|
389
|
-
} while (true);
|
|
390
|
-
return IntPtr.Zero;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
private static void SendAltKey(bool keyUp) {
|
|
394
|
-
INPUT input = new INPUT();
|
|
395
|
-
input.type = INPUT_KEYBOARD;
|
|
396
|
-
input.u.ki.wVk = VK_MENU;
|
|
397
|
-
input.u.ki.dwFlags = keyUp ? KEYEVENTF_KEYUP : (uint)0;
|
|
398
|
-
input.u.ki.dwExtraInfo = IntPtr.Zero;
|
|
399
|
-
SendInput(1, new INPUT[] { input }, Marshal.SizeOf(typeof(INPUT)));
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ==================================================================
|
|
403
|
-
// User32 extended API
|
|
404
|
-
// Direct wrappers for common user32.dll functions, exposed to PS scripts
|
|
405
|
-
// so test code can drive Win32 without writing Add-Type boilerplate.
|
|
406
|
-
// All methods are null-safe (return IntPtr.Zero / false / empty on hwnd=0)
|
|
407
|
-
// and read-only by default (control methods are named explicitly).
|
|
408
|
-
// ==================================================================
|
|
409
|
-
|
|
410
|
-
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
411
|
-
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
|
412
|
-
|
|
413
|
-
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
414
|
-
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
|
|
415
|
-
|
|
416
|
-
[DllImport("user32.dll")]
|
|
417
|
-
private static extern IntPtr GetDesktopWindow();
|
|
418
|
-
|
|
419
|
-
[DllImport("user32.dll")]
|
|
420
|
-
private static extern IntPtr GetParent(IntPtr hWnd);
|
|
421
|
-
|
|
422
|
-
[DllImport("user32.dll")]
|
|
423
|
-
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
|
|
424
|
-
|
|
425
|
-
[DllImport("user32.dll")]
|
|
426
|
-
private static extern IntPtr GetTopWindow(IntPtr hWnd);
|
|
427
|
-
|
|
428
|
-
[DllImport("user32.dll")]
|
|
429
|
-
private static extern IntPtr GetActiveWindow();
|
|
430
|
-
|
|
431
|
-
[DllImport("user32.dll")]
|
|
432
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
433
|
-
private static extern bool IsWindowEnabled(IntPtr hWnd);
|
|
434
|
-
|
|
435
|
-
[DllImport("user32.dll")]
|
|
436
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
437
|
-
private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
|
|
438
|
-
|
|
439
|
-
[DllImport("user32.dll")]
|
|
440
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
441
|
-
private static extern bool CloseWindow(IntPtr hWnd);
|
|
442
|
-
|
|
443
|
-
[DllImport("user32.dll")]
|
|
444
|
-
private static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
445
|
-
|
|
446
|
-
[DllImport("user32.dll")]
|
|
447
|
-
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
448
|
-
|
|
449
|
-
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
450
|
-
private static extern int GetWindowTextLength(IntPtr hWnd);
|
|
451
|
-
|
|
452
|
-
[DllImport("user32.dll")]
|
|
453
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
454
|
-
private static extern bool SetCursorPos(int X, int Y);
|
|
455
|
-
|
|
456
|
-
[DllImport("user32.dll")]
|
|
457
|
-
[return: MarshalAs(UnmanagedType.Bool)]
|
|
458
|
-
private static extern bool GetCursorPos(out POINT lpPoint);
|
|
459
|
-
|
|
460
|
-
[DllImport("user32.dll")]
|
|
461
|
-
private static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
462
|
-
|
|
463
|
-
// ---- Window lookup ----
|
|
464
|
-
|
|
465
|
-
/// <summary>FindWindow(class, name) — instant lookup, no polling. Either arg may be null.</summary>
|
|
466
|
-
public static IntPtr FindWindowHandle(string className, string windowName) {
|
|
467
|
-
return FindWindow(className, windowName);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/// <summary>
|
|
471
|
-
/// FindWindow by class name only. Use this from PowerShell because passing
|
|
472
|
-
/// $null to the two-arg overload's windowName parameter gets marshaled as
|
|
473
|
-
/// an empty string, not a null pointer, causing FindWindow to return 0.
|
|
474
|
-
/// This overload calls FindWindow(className, null) in C#, where null is
|
|
475
|
-
/// real null and matches any window of the class.
|
|
476
|
-
/// </summary>
|
|
477
|
-
public static IntPtr FindWindowHandle(string className) {
|
|
478
|
-
return FindWindow(className, null);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/// <summary>FindWindowEx — find a child window. hwndParent and hwndChildAfter may be IntPtr.Zero.</summary>
|
|
482
|
-
public static IntPtr FindChildWindow(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string windowName) {
|
|
483
|
-
return FindWindowEx(hwndParent, hwndChildAfter, className, windowName);
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/// <summary>Desktop window handle (root of the window tree).</summary>
|
|
487
|
-
public static IntPtr GetDesktop() {
|
|
488
|
-
return GetDesktopWindow();
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/// <summary>Window currently in the foreground (with focus).</summary>
|
|
492
|
-
public static IntPtr GetForeground() {
|
|
493
|
-
return GetForegroundWindow();
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/// <summary>Active window of the calling thread.</summary>
|
|
497
|
-
public static IntPtr GetActive() {
|
|
498
|
-
return GetActiveWindow();
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/// <summary>Top-level window at screen coordinates (x, y).</summary>
|
|
502
|
-
public static IntPtr WindowAtPoint(int x, int y) {
|
|
503
|
-
POINT p; p.x = x; p.y = y;
|
|
504
|
-
return WindowFromPoint(p);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// ---- Window traversal ----
|
|
508
|
-
|
|
509
|
-
/// <summary>Parent of hwnd, or IntPtr.Zero if it has none / hwnd is invalid.</summary>
|
|
510
|
-
public static IntPtr GetParentWindow(IntPtr hwnd) {
|
|
511
|
-
if (hwnd == IntPtr.Zero) return IntPtr.Zero;
|
|
512
|
-
return GetParent(hwnd);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/// <summary>
|
|
516
|
-
/// GetWindow relation — cmd values:
|
|
517
|
-
/// 0 GW_HWNDFIRST, 1 GW_HWNDLAST, 2 GW_HWNDNEXT, 3 GW_HWNDPREV,
|
|
518
|
-
/// 4 GW_OWNER, 5 GW_CHILD, 6 GW_ENABLEDPOPUP
|
|
519
|
-
/// </summary>
|
|
520
|
-
public static IntPtr GetRelatedWindow(IntPtr hwnd, uint cmd) {
|
|
521
|
-
if (hwnd == IntPtr.Zero) return IntPtr.Zero;
|
|
522
|
-
return GetWindow(hwnd, cmd);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/// <summary>Top-most child of parent in Z-order (parent=IntPtr.Zero for desktop).</summary>
|
|
526
|
-
public static IntPtr GetTopChild(IntPtr parent) {
|
|
527
|
-
return GetTopWindow(parent);
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
/// <summary>All visible+invisible top-level windows in Z-order (front first).</summary>
|
|
531
|
-
public static IntPtr[] EnumTopLevelWindows() {
|
|
532
|
-
var list = new List<IntPtr>();
|
|
533
|
-
EnumWindowsProc cb = (h, l) => { list.Add(h); return true; };
|
|
534
|
-
EnumWindows(cb, IntPtr.Zero);
|
|
535
|
-
return list.ToArray();
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/// <summary>All direct + descendant children of parent.</summary>
|
|
539
|
-
public static IntPtr[] EnumChildren(IntPtr parent) {
|
|
540
|
-
if (parent == IntPtr.Zero) return new IntPtr[0];
|
|
541
|
-
var list = new List<IntPtr>();
|
|
542
|
-
EnumWindowsProc cb = (h, l) => { list.Add(h); return true; };
|
|
543
|
-
EnumChildWindows(parent, cb, IntPtr.Zero);
|
|
544
|
-
return list.ToArray();
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// ---- Window state ----
|
|
548
|
-
|
|
549
|
-
/// <summary>True if hwnd identifies an existing window.</summary>
|
|
550
|
-
public static bool IsValidWindow(IntPtr hwnd) {
|
|
551
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
552
|
-
return IsWindow(hwnd);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/// <summary>True if the window accepts mouse and keyboard input.</summary>
|
|
556
|
-
public static bool IsEnabled(IntPtr hwnd) {
|
|
557
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
558
|
-
return IsWindowEnabled(hwnd);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/// <summary>Win32 class name of a window (the ClassName property in UIA).</summary>
|
|
562
|
-
public static string GetClassNameOf(IntPtr hwnd) {
|
|
563
|
-
if (hwnd == IntPtr.Zero) return null;
|
|
564
|
-
var sb = new System.Text.StringBuilder(256);
|
|
565
|
-
GetClassName(hwnd, sb, 256);
|
|
566
|
-
return sb.ToString();
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/// <summary>Length of the window title in characters (does not block).</summary>
|
|
570
|
-
public static int GetTextLength(IntPtr hwnd) {
|
|
571
|
-
if (hwnd == IntPtr.Zero) return 0;
|
|
572
|
-
return GetWindowTextLength(hwnd);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// ---- Window control ----
|
|
576
|
-
|
|
577
|
-
/// <summary>
|
|
578
|
-
/// ShowWindow — cmd values:
|
|
579
|
-
/// 0 SW_HIDE, 1 SW_SHOWNORMAL, 2 SW_SHOWMINIMIZED, 3 SW_MAXIMIZE,
|
|
580
|
-
/// 4 SW_SHOWNOACTIVATE, 5 SW_SHOW, 6 SW_MINIMIZE, 7 SW_SHOWMINNOACTIVE,
|
|
581
|
-
/// 8 SW_SHOWNA, 9 SW_RESTORE, 10 SW_SHOWDEFAULT, 11 SW_FORCEMINIMIZE
|
|
582
|
-
/// </summary>
|
|
583
|
-
public static bool Show(IntPtr hwnd, int cmd) {
|
|
584
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
585
|
-
return ShowWindow(hwnd, cmd);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/// <summary>Maximize a window (SW_MAXIMIZE).</summary>
|
|
589
|
-
public static bool MaximizeWindow(IntPtr hwnd) {
|
|
590
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
591
|
-
return ShowWindow(hwnd, 3);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/// <summary>Hide a window (SW_HIDE — process keeps running, window disappears).</summary>
|
|
595
|
-
public static bool HideWindow(IntPtr hwnd) {
|
|
596
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
597
|
-
return ShowWindow(hwnd, 0);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/// <summary>Move + resize a window in screen coordinates.</summary>
|
|
601
|
-
public static bool Move(IntPtr hwnd, int x, int y, int width, int height, bool repaint) {
|
|
602
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
603
|
-
return MoveWindow(hwnd, x, y, width, height, repaint);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/// <summary>SetWindowPos — flexible reposition / Z-order change. See MSDN for hwndInsertAfter and flags.</summary>
|
|
607
|
-
public static bool SetPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int cx, int cy, uint flags) {
|
|
608
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
609
|
-
return SetWindowPos(hwnd, hwndInsertAfter, x, y, cx, cy, flags);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/// <summary>Win32 CloseWindow — confusingly, this MINIMIZES the window (it does not destroy it).</summary>
|
|
613
|
-
public static bool MinimizeViaCloseWindow(IntPtr hwnd) {
|
|
614
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
615
|
-
return CloseWindow(hwnd);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
// ---- Messages ----
|
|
619
|
-
|
|
620
|
-
/// <summary>PostMessage — fire-and-forget. Returns true if posted (non-zero return).</summary>
|
|
621
|
-
public static bool Post(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam) {
|
|
622
|
-
if (hwnd == IntPtr.Zero) return false;
|
|
623
|
-
return PostMessage(hwnd, msg, wParam, lParam) != IntPtr.Zero;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/// <summary>SendMessage — blocks until the receiving window's message procedure returns.</summary>
|
|
627
|
-
public static IntPtr Send(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam) {
|
|
628
|
-
if (hwnd == IntPtr.Zero) return IntPtr.Zero;
|
|
629
|
-
return SendMessage(hwnd, msg, wParam, lParam);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// ---- Cursor ----
|
|
633
|
-
|
|
634
|
-
/// <summary>Move OS cursor to screen coordinates.</summary>
|
|
635
|
-
public static bool MoveCursor(int x, int y) {
|
|
636
|
-
return SetCursorPos(x, y);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/// <summary>Current OS cursor position in screen coordinates.</summary>
|
|
640
|
-
public static POINT GetCursor() {
|
|
641
|
-
POINT p; p.x = 0; p.y = 0;
|
|
642
|
-
GetCursorPos(out p);
|
|
643
|
-
return p;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// ==================================================================
|
|
647
|
-
// MSAA property retrieval
|
|
648
|
-
// ==================================================================
|
|
649
|
-
|
|
650
|
-
private static bool PointBelongsToExpectedProcess(int x, int y) {
|
|
651
|
-
if (_expectedPid == 0 || (x == 0 && y == 0)) return true;
|
|
652
|
-
try {
|
|
653
|
-
POINT pt; pt.x = x; pt.y = y;
|
|
654
|
-
IntPtr hWnd = WindowFromPoint(pt);
|
|
655
|
-
if (hWnd == IntPtr.Zero) return true;
|
|
656
|
-
uint pidAtPoint = 0;
|
|
657
|
-
GetWindowThreadProcessId(hWnd, out pidAtPoint);
|
|
658
|
-
return pidAtPoint == 0 || pidAtPoint == _expectedPid;
|
|
659
|
-
} catch { return true; }
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
private static string ResolveMemberName(string propertyName, out object[] args) {
|
|
663
|
-
args = new object[] { (int)0 };
|
|
664
|
-
switch(propertyName.ToLower()) {
|
|
665
|
-
case "name": return "accName";
|
|
666
|
-
case "value": return "accValue";
|
|
667
|
-
case "description": return "accDescription";
|
|
668
|
-
case "role": return "accRole";
|
|
669
|
-
case "state": return "accState";
|
|
670
|
-
case "help": return "accHelp";
|
|
671
|
-
case "keyboardshortcut": return "accKeyboardShortcut";
|
|
672
|
-
case "defaultaction": return "accDefaultAction";
|
|
673
|
-
case "focus": args = null; return "accFocus";
|
|
674
|
-
case "selection": args = null; return "accSelection";
|
|
675
|
-
default: return null;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/// <summary>
|
|
680
|
-
/// Get MSAA property via AccessibleObjectFromWindow (hwnd-based, no screen coords).
|
|
681
|
-
/// </summary>
|
|
682
|
-
[HandleProcessCorruptedStateExceptions]
|
|
683
|
-
[SecurityCritical]
|
|
684
|
-
public static object GetLegacyProperty(IntPtr hwnd, string propertyName) {
|
|
685
|
-
if (hwnd == IntPtr.Zero) return null;
|
|
686
|
-
Guid IID_IAccessible = new Guid("618736E0-3C3D-11CF-810C-00AA00389B71");
|
|
687
|
-
object accObj = null;
|
|
688
|
-
try {
|
|
689
|
-
int res = AccessibleObjectFromWindow(hwnd, 0xFFFFFFFC, ref IID_IAccessible, out accObj);
|
|
690
|
-
if (res == 0 && accObj != null) {
|
|
691
|
-
object[] args;
|
|
692
|
-
string memberName = ResolveMemberName(propertyName, out args);
|
|
693
|
-
if (memberName == null) return null;
|
|
694
|
-
return accObj.GetType().InvokeMember(memberName,
|
|
695
|
-
BindingFlags.GetProperty, null, accObj, args);
|
|
696
|
-
}
|
|
697
|
-
} catch { }
|
|
698
|
-
finally {
|
|
699
|
-
if (accObj != null) try { Marshal.ReleaseComObject(accObj); } catch { }
|
|
700
|
-
}
|
|
701
|
-
return null;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
/// <summary>
|
|
705
|
-
/// Get MSAA property with cascading fallback:
|
|
706
|
-
/// 1. hwnd-based (AccessibleObjectFromWindow)
|
|
707
|
-
/// 2. PID validation (WindowFromPoint check)
|
|
708
|
-
/// 3. Point-based (AccessibleObjectFromPoint)
|
|
709
|
-
/// Call SetExpectedPid() first to set expected PID.
|
|
710
|
-
/// </summary>
|
|
711
|
-
[HandleProcessCorruptedStateExceptions]
|
|
712
|
-
[SecurityCritical]
|
|
713
|
-
public static object GetLegacyPropertyWithFallback(IntPtr hwnd, int x, int y, string propertyName) {
|
|
714
|
-
if (hwnd != IntPtr.Zero) {
|
|
715
|
-
object val = GetLegacyProperty(hwnd, propertyName);
|
|
716
|
-
if (val != null) return val;
|
|
717
|
-
}
|
|
718
|
-
if (!PointBelongsToExpectedProcess(x, y)) return null;
|
|
719
|
-
if (propertyName.ToLower() == "childid") { /* handled below */ }
|
|
720
|
-
|
|
721
|
-
POINT pt; pt.x = x; pt.y = y;
|
|
722
|
-
object accObj = null;
|
|
723
|
-
object childIdObj = null;
|
|
724
|
-
try {
|
|
725
|
-
int res = AccessibleObjectFromPoint(pt, out accObj, out childIdObj);
|
|
726
|
-
if (res == 0 && accObj != null) {
|
|
727
|
-
if (propertyName.ToLower() == "childid") return childIdObj;
|
|
728
|
-
object[] args;
|
|
729
|
-
string memberName = ResolveMemberName(propertyName, out args);
|
|
730
|
-
if (memberName == null) return null;
|
|
731
|
-
if (args != null) args = new object[] { childIdObj };
|
|
732
|
-
return accObj.GetType().InvokeMember(memberName,
|
|
733
|
-
BindingFlags.GetProperty, null, accObj, args);
|
|
734
|
-
}
|
|
735
|
-
} catch { }
|
|
736
|
-
finally {
|
|
737
|
-
if (accObj != null) try { Marshal.ReleaseComObject(accObj); } catch { }
|
|
738
|
-
}
|
|
739
|
-
return null;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/// <summary>
|
|
743
|
-
/// Get all MSAA properties at once.
|
|
744
|
-
/// Call SetExpectedPid() first to set expected PID.
|
|
745
|
-
/// </summary>
|
|
746
|
-
[HandleProcessCorruptedStateExceptions]
|
|
747
|
-
[SecurityCritical]
|
|
748
|
-
public static Hashtable GetLegacyPropsWithFallback(IntPtr hwnd, int x, int y) {
|
|
749
|
-
Hashtable props = new Hashtable();
|
|
750
|
-
try {
|
|
751
|
-
string[] names = { "Name", "Value", "Description", "Role", "State",
|
|
752
|
-
"Help", "KeyboardShortcut", "DefaultAction", "ChildId" };
|
|
753
|
-
foreach (string name in names) {
|
|
754
|
-
object val = GetLegacyPropertyWithFallback(hwnd, x, y, name);
|
|
755
|
-
if (val != null) props.Add(name, val);
|
|
756
|
-
}
|
|
757
|
-
} catch { }
|
|
758
|
-
return props;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
public static class ConsoleHelper {
|
|
763
|
-
[DllImport("kernel32.dll", SetLastError = true)]
|
|
764
|
-
public static extern IntPtr GetStdHandle(int nStdHandle);
|
|
765
|
-
[DllImport("kernel32.dll", SetLastError = true)]
|
|
766
|
-
public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
|
|
767
|
-
[DllImport("kernel32.dll", SetLastError = true)]
|
|
768
|
-
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
|
|
769
|
-
|
|
770
|
-
public const int STD_INPUT_HANDLE = -10;
|
|
771
|
-
public const uint ENABLE_MOUSE_INPUT = 0x0010;
|
|
772
|
-
public const uint ENABLE_INSERT_MODE = 0x0020;
|
|
773
|
-
public const uint ENABLE_QUICK_EDIT_MODE = 0x0040;
|
|
774
|
-
public const uint ENABLE_EXTENDED_FLAGS = 0x0080;
|
|
775
|
-
|
|
776
|
-
public static void DisableConsoleInteractions() {
|
|
777
|
-
IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
|
|
778
|
-
uint mode;
|
|
779
|
-
if (GetConsoleMode(consoleHandle, out mode)) {
|
|
780
|
-
mode &= ~ENABLE_QUICK_EDIT_MODE;
|
|
781
|
-
mode &= ~ENABLE_INSERT_MODE;
|
|
782
|
-
mode &= ~ENABLE_MOUSE_INPUT;
|
|
783
|
-
mode |= ENABLE_EXTENDED_FLAGS;
|
|
784
|
-
SetConsoleMode(consoleHandle, mode);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
'@
|
|
789
|
-
|
|
790
|
-
# Set TEMP and TMP to DLL directory to avoid permission issues
|
|
791
|
-
$originalTemp = $env:TEMP
|
|
792
|
-
$originalTmp = $env:TMP
|
|
793
|
-
$env:TEMP = $dllDir
|
|
794
|
-
$env:TMP = $dllDir
|
|
795
|
-
|
|
796
|
-
try {
|
|
797
|
-
# Compile the C# code to DLL
|
|
798
|
-
Add-Type -TypeDefinition $code -Language CSharp -ReferencedAssemblies @('System') -OutputAssembly $dllPath -ErrorAction Stop
|
|
799
|
-
Write-Output "Win32Helper.dll compiled successfully"
|
|
800
|
-
|
|
801
|
-
# Load the newly compiled DLL
|
|
802
|
-
Add-Type -Path $dllPath -ErrorAction Stop
|
|
803
|
-
Write-Output "Win32Helper.dll loaded successfully"
|
|
804
|
-
} catch {
|
|
805
|
-
Write-Output "Compilation failed, falling back to in-memory loading"
|
|
806
|
-
# Fallback: load in-memory if compilation fails
|
|
807
|
-
Add-Type -TypeDefinition $code -Language CSharp -ReferencedAssemblies @('System') -ErrorAction Stop
|
|
808
|
-
} finally {
|
|
809
|
-
# Restore original TEMP/TMP
|
|
810
|
-
$env:TEMP = $originalTemp
|
|
811
|
-
$env:TMP = $originalTmp
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
Write-Output "Win32Helper ready"
|
|
10
|
+
exports.WIN32_HELPER_SCRIPT = `
|
|
11
|
+
# Win32Helper - Native Win32 helpers compiled as C# DLL
|
|
12
|
+
# Provides: window management, MSAA property retrieval, process validation
|
|
13
|
+
|
|
14
|
+
$dllPath = '${dllPath}'
|
|
15
|
+
$dllDir = '${dllDir}'
|
|
16
|
+
|
|
17
|
+
# Check if Win32Helper is already loaded
|
|
18
|
+
if (-not ([System.Management.Automation.PSTypeName]'Win32Helper').Type) {
|
|
19
|
+
|
|
20
|
+
# Check if DLL exists
|
|
21
|
+
if (Test-Path $dllPath) {
|
|
22
|
+
Write-Output "Loading Win32Helper from existing DLL: $dllPath"
|
|
23
|
+
Add-Type -Path $dllPath -ErrorAction Stop
|
|
24
|
+
} else {
|
|
25
|
+
Write-Output "Compiling Win32Helper.dll..."
|
|
26
|
+
|
|
27
|
+
# Create DLL directory if it doesn't exist
|
|
28
|
+
if (-not (Test-Path $dllDir)) {
|
|
29
|
+
New-Item -ItemType Directory -Path $dllDir -Force | Out-Null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# C# source code
|
|
33
|
+
$code = @'
|
|
34
|
+
using System;
|
|
35
|
+
using System.Runtime.InteropServices;
|
|
36
|
+
using System.Collections;
|
|
37
|
+
using System.Collections.Generic;
|
|
38
|
+
using System.Diagnostics;
|
|
39
|
+
using System.Reflection;
|
|
40
|
+
using System.Runtime.ExceptionServices;
|
|
41
|
+
using System.Security;
|
|
42
|
+
using System.Threading;
|
|
43
|
+
|
|
44
|
+
/// <summary>
|
|
45
|
+
/// Native Win32 helper for Appium Windows driver.
|
|
46
|
+
///
|
|
47
|
+
/// Window management:
|
|
48
|
+
/// BringToForeground, SetTopMost, ClearTopMost, MinimizeWindow, RestoreWindow,
|
|
49
|
+
/// IsMinimized, IsVisible, GetWindowText, GetWindowRect, GetWindowProcessId
|
|
50
|
+
///
|
|
51
|
+
/// MSAA property retrieval (with built-in safety):
|
|
52
|
+
/// SetExpectedPid - set expected PID + optional BringToForeground
|
|
53
|
+
/// GetLegacyProperty - single property via hwnd (AccessibleObjectFromWindow)
|
|
54
|
+
/// GetLegacyPropertyWithFallback - hwnd -> PID check -> point fallback
|
|
55
|
+
/// GetLegacyPropsWithFallback - batch: all 9 MSAA properties
|
|
56
|
+
/// </summary>
|
|
57
|
+
public static class Win32Helper {
|
|
58
|
+
|
|
59
|
+
// ==================================================================
|
|
60
|
+
// P/Invoke - MSAA (oleacc.dll)
|
|
61
|
+
// ==================================================================
|
|
62
|
+
|
|
63
|
+
[DllImport("oleacc.dll")]
|
|
64
|
+
private static extern int AccessibleObjectFromWindow(
|
|
65
|
+
IntPtr hwnd, uint dwId, ref Guid riid,
|
|
66
|
+
[MarshalAs(UnmanagedType.Interface)] out object ppvObject);
|
|
67
|
+
|
|
68
|
+
[DllImport("oleacc.dll")]
|
|
69
|
+
private static extern int AccessibleObjectFromPoint(
|
|
70
|
+
POINT pt,
|
|
71
|
+
[MarshalAs(UnmanagedType.Interface)] out object ppacc,
|
|
72
|
+
[MarshalAs(UnmanagedType.Struct)] out object pvarChild);
|
|
73
|
+
|
|
74
|
+
// ==================================================================
|
|
75
|
+
// P/Invoke - Window management (user32.dll)
|
|
76
|
+
// ==================================================================
|
|
77
|
+
|
|
78
|
+
[DllImport("user32.dll")]
|
|
79
|
+
private static extern IntPtr GetForegroundWindow();
|
|
80
|
+
|
|
81
|
+
[DllImport("user32.dll")]
|
|
82
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
83
|
+
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
84
|
+
|
|
85
|
+
[DllImport("user32.dll")]
|
|
86
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
87
|
+
private static extern bool BringWindowToTop(IntPtr hWnd);
|
|
88
|
+
|
|
89
|
+
[DllImport("user32.dll")]
|
|
90
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
91
|
+
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
92
|
+
|
|
93
|
+
[DllImport("user32.dll")]
|
|
94
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
95
|
+
private static extern bool IsIconic(IntPtr hWnd);
|
|
96
|
+
|
|
97
|
+
[DllImport("user32.dll")]
|
|
98
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
99
|
+
private static extern bool IsWindowVisible(IntPtr hWnd);
|
|
100
|
+
|
|
101
|
+
[DllImport("user32.dll")]
|
|
102
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
103
|
+
private static extern bool IsWindow(IntPtr hWnd);
|
|
104
|
+
|
|
105
|
+
[DllImport("user32.dll")]
|
|
106
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
107
|
+
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
|
108
|
+
|
|
109
|
+
[DllImport("user32.dll")]
|
|
110
|
+
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
|
111
|
+
|
|
112
|
+
[DllImport("user32.dll")]
|
|
113
|
+
private static extern IntPtr WindowFromPoint(POINT pt);
|
|
114
|
+
|
|
115
|
+
[DllImport("user32.dll")]
|
|
116
|
+
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
|
117
|
+
|
|
118
|
+
[DllImport("user32.dll")]
|
|
119
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
120
|
+
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
|
|
121
|
+
int X, int Y, int cx, int cy, uint uFlags);
|
|
122
|
+
|
|
123
|
+
[DllImport("user32.dll")]
|
|
124
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
125
|
+
private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref uint pvParam, uint fWinIni);
|
|
126
|
+
|
|
127
|
+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
128
|
+
private static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
|
|
129
|
+
|
|
130
|
+
[DllImport("user32.dll")]
|
|
131
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
132
|
+
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
133
|
+
|
|
134
|
+
[DllImport("kernel32.dll")]
|
|
135
|
+
private static extern uint GetCurrentThreadId();
|
|
136
|
+
|
|
137
|
+
// ==================================================================
|
|
138
|
+
// Structs and constants
|
|
139
|
+
// ==================================================================
|
|
140
|
+
|
|
141
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
142
|
+
public struct POINT { public int x; public int y; }
|
|
143
|
+
|
|
144
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
145
|
+
public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
|
|
146
|
+
|
|
147
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
148
|
+
private struct INPUT {
|
|
149
|
+
public uint type;
|
|
150
|
+
public INPUTUNION u;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
[StructLayout(LayoutKind.Explicit)]
|
|
154
|
+
private struct INPUTUNION {
|
|
155
|
+
[FieldOffset(0)] public KEYBDINPUT ki;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
159
|
+
private struct KEYBDINPUT {
|
|
160
|
+
public ushort wVk;
|
|
161
|
+
public ushort wScan;
|
|
162
|
+
public uint dwFlags;
|
|
163
|
+
public uint time;
|
|
164
|
+
public IntPtr dwExtraInfo;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private const int SW_MINIMIZE = 6;
|
|
168
|
+
private const int SW_RESTORE = 9;
|
|
169
|
+
private const int SW_SHOW = 5;
|
|
170
|
+
private const ushort VK_MENU = 0xA4;
|
|
171
|
+
private const uint INPUT_KEYBOARD = 1;
|
|
172
|
+
private const uint KEYEVENTF_KEYUP = 0x0002;
|
|
173
|
+
private const uint SWP_NOMOVE = 0x0002;
|
|
174
|
+
private const uint SWP_NOSIZE = 0x0001;
|
|
175
|
+
private const uint SWP_SHOWWINDOW = 0x0040;
|
|
176
|
+
private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
|
|
177
|
+
private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
|
|
178
|
+
private const uint SPIF_SENDWININICHANGE = 0x02;
|
|
179
|
+
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
|
180
|
+
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
|
181
|
+
|
|
182
|
+
// Context for MSAA queries - set via SetExpectedPid() before calling GetLegacy* methods
|
|
183
|
+
private static uint _expectedPid = 0;
|
|
184
|
+
|
|
185
|
+
// ==================================================================
|
|
186
|
+
// Context setup - call before MSAA queries
|
|
187
|
+
// ==================================================================
|
|
188
|
+
|
|
189
|
+
/// <summary>
|
|
190
|
+
/// Set expected process ID for MSAA point-based queries.
|
|
191
|
+
/// AccessibleObjectFromPoint will be skipped if the window at the target
|
|
192
|
+
/// coordinates belongs to a different process.
|
|
193
|
+
/// </summary>
|
|
194
|
+
public static void SetExpectedPid(uint expectedPid) {
|
|
195
|
+
_expectedPid = expectedPid;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/// <summary>
|
|
199
|
+
/// Set expected PID and bring the ancestor window to foreground.
|
|
200
|
+
/// Call before GetLegacyPropsWithFallback for getProperty("all").
|
|
201
|
+
/// </summary>
|
|
202
|
+
public static void SetExpectedPidAndForeground(uint expectedPid, IntPtr ancestorHwnd) {
|
|
203
|
+
_expectedPid = expectedPid;
|
|
204
|
+
if (ancestorHwnd != IntPtr.Zero) {
|
|
205
|
+
BringToForeground(ancestorHwnd);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ==================================================================
|
|
210
|
+
// Window management
|
|
211
|
+
// ==================================================================
|
|
212
|
+
|
|
213
|
+
/// <summary>
|
|
214
|
+
/// Force a window to foreground even from a background process.
|
|
215
|
+
/// Uses escalating strategies modeled after AutoHotkey WinActivate:
|
|
216
|
+
/// 1. Restore if minimized
|
|
217
|
+
/// 2. AttachThreadInput to foreground + target threads
|
|
218
|
+
/// 3. Retry loop: SetForegroundWindow + BringWindowToTop
|
|
219
|
+
/// 4. SendInput ALT key (satisfies "last input" requirement)
|
|
220
|
+
/// 5. SPI_SETFOREGROUNDLOCKTIMEOUT = 0 (temporarily disable lock)
|
|
221
|
+
/// 6. HWND_TOPMOST toggle (last resort Z-order force)
|
|
222
|
+
/// </summary>
|
|
223
|
+
public static bool BringToForeground(IntPtr hwnd) {
|
|
224
|
+
if (hwnd == IntPtr.Zero || !IsWindow(hwnd)) return false;
|
|
225
|
+
if (GetForegroundWindow() == hwnd) return true;
|
|
226
|
+
if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE);
|
|
227
|
+
|
|
228
|
+
uint unusedPid = 0;
|
|
229
|
+
uint curThread = GetCurrentThreadId();
|
|
230
|
+
IntPtr foreWnd = GetForegroundWindow();
|
|
231
|
+
uint foreThread = GetWindowThreadProcessId(foreWnd, out unusedPid);
|
|
232
|
+
uint targetThread = GetWindowThreadProcessId(hwnd, out unusedPid);
|
|
233
|
+
|
|
234
|
+
bool attachedCurFore = false;
|
|
235
|
+
bool attachedForeTarget = false;
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
if (curThread != foreThread)
|
|
239
|
+
attachedCurFore = AttachThreadInput(curThread, foreThread, true);
|
|
240
|
+
if (foreThread != targetThread)
|
|
241
|
+
attachedForeTarget = AttachThreadInput(foreThread, targetThread, true);
|
|
242
|
+
|
|
243
|
+
// Retry with escalation
|
|
244
|
+
for (int attempt = 0; attempt < 5; attempt++) {
|
|
245
|
+
BringWindowToTop(hwnd);
|
|
246
|
+
ShowWindow(hwnd, SW_SHOW);
|
|
247
|
+
SetForegroundWindow(hwnd);
|
|
248
|
+
Thread.Sleep(10);
|
|
249
|
+
if (GetForegroundWindow() == hwnd) return true;
|
|
250
|
+
|
|
251
|
+
// ALT key trick via SendInput
|
|
252
|
+
SendAltKey(false);
|
|
253
|
+
SendAltKey(true);
|
|
254
|
+
SetForegroundWindow(hwnd);
|
|
255
|
+
Thread.Sleep(10);
|
|
256
|
+
if (GetForegroundWindow() == hwnd) return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Temporarily disable foreground lock timeout
|
|
260
|
+
uint lockTimeout = 0;
|
|
261
|
+
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, ref lockTimeout, 0);
|
|
262
|
+
if (lockTimeout > 0) {
|
|
263
|
+
uint zero = 0;
|
|
264
|
+
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref zero, SPIF_SENDWININICHANGE);
|
|
265
|
+
SetForegroundWindow(hwnd);
|
|
266
|
+
BringWindowToTop(hwnd);
|
|
267
|
+
ShowWindow(hwnd, SW_SHOW);
|
|
268
|
+
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref lockTimeout, SPIF_SENDWININICHANGE);
|
|
269
|
+
Thread.Sleep(10);
|
|
270
|
+
if (GetForegroundWindow() == hwnd) return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Last resort: TOPMOST toggle
|
|
274
|
+
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
275
|
+
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
276
|
+
return GetForegroundWindow() == hwnd;
|
|
277
|
+
} catch {
|
|
278
|
+
return false;
|
|
279
|
+
} finally {
|
|
280
|
+
if (attachedCurFore) AttachThreadInput(curThread, foreThread, false);
|
|
281
|
+
if (attachedForeTarget) AttachThreadInput(foreThread, targetThread, false);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/// <summary>Set window as always-on-top.</summary>
|
|
286
|
+
public static bool SetTopMost(IntPtr hwnd) {
|
|
287
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
288
|
+
return SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/// <summary>Remove always-on-top from window.</summary>
|
|
292
|
+
public static bool ClearTopMost(IntPtr hwnd) {
|
|
293
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
294
|
+
return SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
public static bool MinimizeWindow(IntPtr hwnd) {
|
|
298
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
299
|
+
return ShowWindow(hwnd, SW_MINIMIZE);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public static bool RestoreWindow(IntPtr hwnd) {
|
|
303
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
304
|
+
return ShowWindow(hwnd, SW_RESTORE);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
public static bool IsMinimized(IntPtr hwnd) {
|
|
308
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
309
|
+
return IsIconic(hwnd);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
public static bool IsVisible(IntPtr hwnd) {
|
|
313
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
314
|
+
return IsWindowVisible(hwnd);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
public static string GetText(IntPtr hwnd) {
|
|
318
|
+
if (hwnd == IntPtr.Zero) return null;
|
|
319
|
+
System.Text.StringBuilder sb = new System.Text.StringBuilder(512);
|
|
320
|
+
GetWindowText(hwnd, sb, sb.Capacity);
|
|
321
|
+
string text = sb.ToString();
|
|
322
|
+
return text.Length > 0 ? text : null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
public static RECT GetRect(IntPtr hwnd) {
|
|
326
|
+
RECT rect;
|
|
327
|
+
rect.Left = 0; rect.Top = 0; rect.Right = 0; rect.Bottom = 0;
|
|
328
|
+
if (hwnd != IntPtr.Zero) GetWindowRect(hwnd, out rect);
|
|
329
|
+
return rect;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
public static uint GetProcessId(IntPtr hwnd) {
|
|
333
|
+
if (hwnd == IntPtr.Zero) return 0;
|
|
334
|
+
uint pid = 0;
|
|
335
|
+
GetWindowThreadProcessId(hwnd, out pid);
|
|
336
|
+
return pid;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ==================================================================
|
|
340
|
+
// Window enumeration (for scope-session)
|
|
341
|
+
// ==================================================================
|
|
342
|
+
|
|
343
|
+
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
344
|
+
|
|
345
|
+
[DllImport("user32.dll")]
|
|
346
|
+
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
347
|
+
|
|
348
|
+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
349
|
+
private static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);
|
|
350
|
+
|
|
351
|
+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
352
|
+
private static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, System.Text.StringBuilder lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult);
|
|
353
|
+
|
|
354
|
+
private const uint WM_GETTEXT = 0x000D;
|
|
355
|
+
private const uint SMTO_ABORTIFHUNG = 0x0002;
|
|
356
|
+
private const uint SMTO_BLOCK = 0x0001;
|
|
357
|
+
|
|
358
|
+
/// <summary>
|
|
359
|
+
/// Find a top-level window by exact Name and Win32 ClassName.
|
|
360
|
+
/// Uses EnumWindows + SendMessageTimeout(WM_GETTEXT) — avoids UIA FindFirst
|
|
361
|
+
/// which blocks for 20+ s against SecureAge's slow UIA provider.
|
|
362
|
+
/// Polls for up to timeoutMs milliseconds (pollMs interval). Returns IntPtr.Zero on timeout.
|
|
363
|
+
/// </summary>
|
|
364
|
+
public static IntPtr FindWindowByName(string targetName, string targetClass, int timeoutMs, int pollMs) {
|
|
365
|
+
var sw = Stopwatch.StartNew();
|
|
366
|
+
do {
|
|
367
|
+
var candidates = new List<IntPtr>();
|
|
368
|
+
EnumWindowsProc cb = (h, l) => {
|
|
369
|
+
if (!IsWindowVisible(h)) return true;
|
|
370
|
+
if (!string.IsNullOrEmpty(targetClass)) {
|
|
371
|
+
var cls = new System.Text.StringBuilder(256);
|
|
372
|
+
GetClassName(h, cls, 256);
|
|
373
|
+
if (cls.ToString() != targetClass) return true;
|
|
374
|
+
}
|
|
375
|
+
candidates.Add(h);
|
|
376
|
+
return true;
|
|
377
|
+
};
|
|
378
|
+
EnumWindows(cb, IntPtr.Zero);
|
|
379
|
+
foreach (var h in candidates) {
|
|
380
|
+
var tb = new System.Text.StringBuilder(512);
|
|
381
|
+
IntPtr res = IntPtr.Zero;
|
|
382
|
+
IntPtr rc = SendMessageTimeout(h, WM_GETTEXT, (IntPtr)512, tb,
|
|
383
|
+
SMTO_ABORTIFHUNG | SMTO_BLOCK, 500, out res);
|
|
384
|
+
if (rc != IntPtr.Zero && tb.ToString() == targetName) return h;
|
|
385
|
+
}
|
|
386
|
+
long elapsed = sw.ElapsedMilliseconds;
|
|
387
|
+
if (elapsed >= timeoutMs) break;
|
|
388
|
+
if (pollMs > 0) Thread.Sleep(Math.Min(pollMs, (int)(timeoutMs - elapsed)));
|
|
389
|
+
} while (true);
|
|
390
|
+
return IntPtr.Zero;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private static void SendAltKey(bool keyUp) {
|
|
394
|
+
INPUT input = new INPUT();
|
|
395
|
+
input.type = INPUT_KEYBOARD;
|
|
396
|
+
input.u.ki.wVk = VK_MENU;
|
|
397
|
+
input.u.ki.dwFlags = keyUp ? KEYEVENTF_KEYUP : (uint)0;
|
|
398
|
+
input.u.ki.dwExtraInfo = IntPtr.Zero;
|
|
399
|
+
SendInput(1, new INPUT[] { input }, Marshal.SizeOf(typeof(INPUT)));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ==================================================================
|
|
403
|
+
// User32 extended API
|
|
404
|
+
// Direct wrappers for common user32.dll functions, exposed to PS scripts
|
|
405
|
+
// so test code can drive Win32 without writing Add-Type boilerplate.
|
|
406
|
+
// All methods are null-safe (return IntPtr.Zero / false / empty on hwnd=0)
|
|
407
|
+
// and read-only by default (control methods are named explicitly).
|
|
408
|
+
// ==================================================================
|
|
409
|
+
|
|
410
|
+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
411
|
+
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
|
412
|
+
|
|
413
|
+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
414
|
+
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
|
|
415
|
+
|
|
416
|
+
[DllImport("user32.dll")]
|
|
417
|
+
private static extern IntPtr GetDesktopWindow();
|
|
418
|
+
|
|
419
|
+
[DllImport("user32.dll")]
|
|
420
|
+
private static extern IntPtr GetParent(IntPtr hWnd);
|
|
421
|
+
|
|
422
|
+
[DllImport("user32.dll")]
|
|
423
|
+
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
|
|
424
|
+
|
|
425
|
+
[DllImport("user32.dll")]
|
|
426
|
+
private static extern IntPtr GetTopWindow(IntPtr hWnd);
|
|
427
|
+
|
|
428
|
+
[DllImport("user32.dll")]
|
|
429
|
+
private static extern IntPtr GetActiveWindow();
|
|
430
|
+
|
|
431
|
+
[DllImport("user32.dll")]
|
|
432
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
433
|
+
private static extern bool IsWindowEnabled(IntPtr hWnd);
|
|
434
|
+
|
|
435
|
+
[DllImport("user32.dll")]
|
|
436
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
437
|
+
private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
|
|
438
|
+
|
|
439
|
+
[DllImport("user32.dll")]
|
|
440
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
441
|
+
private static extern bool CloseWindow(IntPtr hWnd);
|
|
442
|
+
|
|
443
|
+
[DllImport("user32.dll")]
|
|
444
|
+
private static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
445
|
+
|
|
446
|
+
[DllImport("user32.dll")]
|
|
447
|
+
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
448
|
+
|
|
449
|
+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
450
|
+
private static extern int GetWindowTextLength(IntPtr hWnd);
|
|
451
|
+
|
|
452
|
+
[DllImport("user32.dll")]
|
|
453
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
454
|
+
private static extern bool SetCursorPos(int X, int Y);
|
|
455
|
+
|
|
456
|
+
[DllImport("user32.dll")]
|
|
457
|
+
[return: MarshalAs(UnmanagedType.Bool)]
|
|
458
|
+
private static extern bool GetCursorPos(out POINT lpPoint);
|
|
459
|
+
|
|
460
|
+
[DllImport("user32.dll")]
|
|
461
|
+
private static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
462
|
+
|
|
463
|
+
// ---- Window lookup ----
|
|
464
|
+
|
|
465
|
+
/// <summary>FindWindow(class, name) — instant lookup, no polling. Either arg may be null.</summary>
|
|
466
|
+
public static IntPtr FindWindowHandle(string className, string windowName) {
|
|
467
|
+
return FindWindow(className, windowName);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/// <summary>
|
|
471
|
+
/// FindWindow by class name only. Use this from PowerShell because passing
|
|
472
|
+
/// $null to the two-arg overload's windowName parameter gets marshaled as
|
|
473
|
+
/// an empty string, not a null pointer, causing FindWindow to return 0.
|
|
474
|
+
/// This overload calls FindWindow(className, null) in C#, where null is
|
|
475
|
+
/// real null and matches any window of the class.
|
|
476
|
+
/// </summary>
|
|
477
|
+
public static IntPtr FindWindowHandle(string className) {
|
|
478
|
+
return FindWindow(className, null);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/// <summary>FindWindowEx — find a child window. hwndParent and hwndChildAfter may be IntPtr.Zero.</summary>
|
|
482
|
+
public static IntPtr FindChildWindow(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string windowName) {
|
|
483
|
+
return FindWindowEx(hwndParent, hwndChildAfter, className, windowName);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/// <summary>Desktop window handle (root of the window tree).</summary>
|
|
487
|
+
public static IntPtr GetDesktop() {
|
|
488
|
+
return GetDesktopWindow();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/// <summary>Window currently in the foreground (with focus).</summary>
|
|
492
|
+
public static IntPtr GetForeground() {
|
|
493
|
+
return GetForegroundWindow();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/// <summary>Active window of the calling thread.</summary>
|
|
497
|
+
public static IntPtr GetActive() {
|
|
498
|
+
return GetActiveWindow();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/// <summary>Top-level window at screen coordinates (x, y).</summary>
|
|
502
|
+
public static IntPtr WindowAtPoint(int x, int y) {
|
|
503
|
+
POINT p; p.x = x; p.y = y;
|
|
504
|
+
return WindowFromPoint(p);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ---- Window traversal ----
|
|
508
|
+
|
|
509
|
+
/// <summary>Parent of hwnd, or IntPtr.Zero if it has none / hwnd is invalid.</summary>
|
|
510
|
+
public static IntPtr GetParentWindow(IntPtr hwnd) {
|
|
511
|
+
if (hwnd == IntPtr.Zero) return IntPtr.Zero;
|
|
512
|
+
return GetParent(hwnd);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/// <summary>
|
|
516
|
+
/// GetWindow relation — cmd values:
|
|
517
|
+
/// 0 GW_HWNDFIRST, 1 GW_HWNDLAST, 2 GW_HWNDNEXT, 3 GW_HWNDPREV,
|
|
518
|
+
/// 4 GW_OWNER, 5 GW_CHILD, 6 GW_ENABLEDPOPUP
|
|
519
|
+
/// </summary>
|
|
520
|
+
public static IntPtr GetRelatedWindow(IntPtr hwnd, uint cmd) {
|
|
521
|
+
if (hwnd == IntPtr.Zero) return IntPtr.Zero;
|
|
522
|
+
return GetWindow(hwnd, cmd);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/// <summary>Top-most child of parent in Z-order (parent=IntPtr.Zero for desktop).</summary>
|
|
526
|
+
public static IntPtr GetTopChild(IntPtr parent) {
|
|
527
|
+
return GetTopWindow(parent);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/// <summary>All visible+invisible top-level windows in Z-order (front first).</summary>
|
|
531
|
+
public static IntPtr[] EnumTopLevelWindows() {
|
|
532
|
+
var list = new List<IntPtr>();
|
|
533
|
+
EnumWindowsProc cb = (h, l) => { list.Add(h); return true; };
|
|
534
|
+
EnumWindows(cb, IntPtr.Zero);
|
|
535
|
+
return list.ToArray();
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/// <summary>All direct + descendant children of parent.</summary>
|
|
539
|
+
public static IntPtr[] EnumChildren(IntPtr parent) {
|
|
540
|
+
if (parent == IntPtr.Zero) return new IntPtr[0];
|
|
541
|
+
var list = new List<IntPtr>();
|
|
542
|
+
EnumWindowsProc cb = (h, l) => { list.Add(h); return true; };
|
|
543
|
+
EnumChildWindows(parent, cb, IntPtr.Zero);
|
|
544
|
+
return list.ToArray();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// ---- Window state ----
|
|
548
|
+
|
|
549
|
+
/// <summary>True if hwnd identifies an existing window.</summary>
|
|
550
|
+
public static bool IsValidWindow(IntPtr hwnd) {
|
|
551
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
552
|
+
return IsWindow(hwnd);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/// <summary>True if the window accepts mouse and keyboard input.</summary>
|
|
556
|
+
public static bool IsEnabled(IntPtr hwnd) {
|
|
557
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
558
|
+
return IsWindowEnabled(hwnd);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/// <summary>Win32 class name of a window (the ClassName property in UIA).</summary>
|
|
562
|
+
public static string GetClassNameOf(IntPtr hwnd) {
|
|
563
|
+
if (hwnd == IntPtr.Zero) return null;
|
|
564
|
+
var sb = new System.Text.StringBuilder(256);
|
|
565
|
+
GetClassName(hwnd, sb, 256);
|
|
566
|
+
return sb.ToString();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/// <summary>Length of the window title in characters (does not block).</summary>
|
|
570
|
+
public static int GetTextLength(IntPtr hwnd) {
|
|
571
|
+
if (hwnd == IntPtr.Zero) return 0;
|
|
572
|
+
return GetWindowTextLength(hwnd);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ---- Window control ----
|
|
576
|
+
|
|
577
|
+
/// <summary>
|
|
578
|
+
/// ShowWindow — cmd values:
|
|
579
|
+
/// 0 SW_HIDE, 1 SW_SHOWNORMAL, 2 SW_SHOWMINIMIZED, 3 SW_MAXIMIZE,
|
|
580
|
+
/// 4 SW_SHOWNOACTIVATE, 5 SW_SHOW, 6 SW_MINIMIZE, 7 SW_SHOWMINNOACTIVE,
|
|
581
|
+
/// 8 SW_SHOWNA, 9 SW_RESTORE, 10 SW_SHOWDEFAULT, 11 SW_FORCEMINIMIZE
|
|
582
|
+
/// </summary>
|
|
583
|
+
public static bool Show(IntPtr hwnd, int cmd) {
|
|
584
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
585
|
+
return ShowWindow(hwnd, cmd);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/// <summary>Maximize a window (SW_MAXIMIZE).</summary>
|
|
589
|
+
public static bool MaximizeWindow(IntPtr hwnd) {
|
|
590
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
591
|
+
return ShowWindow(hwnd, 3);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/// <summary>Hide a window (SW_HIDE — process keeps running, window disappears).</summary>
|
|
595
|
+
public static bool HideWindow(IntPtr hwnd) {
|
|
596
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
597
|
+
return ShowWindow(hwnd, 0);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/// <summary>Move + resize a window in screen coordinates.</summary>
|
|
601
|
+
public static bool Move(IntPtr hwnd, int x, int y, int width, int height, bool repaint) {
|
|
602
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
603
|
+
return MoveWindow(hwnd, x, y, width, height, repaint);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/// <summary>SetWindowPos — flexible reposition / Z-order change. See MSDN for hwndInsertAfter and flags.</summary>
|
|
607
|
+
public static bool SetPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int cx, int cy, uint flags) {
|
|
608
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
609
|
+
return SetWindowPos(hwnd, hwndInsertAfter, x, y, cx, cy, flags);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/// <summary>Win32 CloseWindow — confusingly, this MINIMIZES the window (it does not destroy it).</summary>
|
|
613
|
+
public static bool MinimizeViaCloseWindow(IntPtr hwnd) {
|
|
614
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
615
|
+
return CloseWindow(hwnd);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// ---- Messages ----
|
|
619
|
+
|
|
620
|
+
/// <summary>PostMessage — fire-and-forget. Returns true if posted (non-zero return).</summary>
|
|
621
|
+
public static bool Post(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam) {
|
|
622
|
+
if (hwnd == IntPtr.Zero) return false;
|
|
623
|
+
return PostMessage(hwnd, msg, wParam, lParam) != IntPtr.Zero;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/// <summary>SendMessage — blocks until the receiving window's message procedure returns.</summary>
|
|
627
|
+
public static IntPtr Send(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam) {
|
|
628
|
+
if (hwnd == IntPtr.Zero) return IntPtr.Zero;
|
|
629
|
+
return SendMessage(hwnd, msg, wParam, lParam);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// ---- Cursor ----
|
|
633
|
+
|
|
634
|
+
/// <summary>Move OS cursor to screen coordinates.</summary>
|
|
635
|
+
public static bool MoveCursor(int x, int y) {
|
|
636
|
+
return SetCursorPos(x, y);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/// <summary>Current OS cursor position in screen coordinates.</summary>
|
|
640
|
+
public static POINT GetCursor() {
|
|
641
|
+
POINT p; p.x = 0; p.y = 0;
|
|
642
|
+
GetCursorPos(out p);
|
|
643
|
+
return p;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// ==================================================================
|
|
647
|
+
// MSAA property retrieval
|
|
648
|
+
// ==================================================================
|
|
649
|
+
|
|
650
|
+
private static bool PointBelongsToExpectedProcess(int x, int y) {
|
|
651
|
+
if (_expectedPid == 0 || (x == 0 && y == 0)) return true;
|
|
652
|
+
try {
|
|
653
|
+
POINT pt; pt.x = x; pt.y = y;
|
|
654
|
+
IntPtr hWnd = WindowFromPoint(pt);
|
|
655
|
+
if (hWnd == IntPtr.Zero) return true;
|
|
656
|
+
uint pidAtPoint = 0;
|
|
657
|
+
GetWindowThreadProcessId(hWnd, out pidAtPoint);
|
|
658
|
+
return pidAtPoint == 0 || pidAtPoint == _expectedPid;
|
|
659
|
+
} catch { return true; }
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private static string ResolveMemberName(string propertyName, out object[] args) {
|
|
663
|
+
args = new object[] { (int)0 };
|
|
664
|
+
switch(propertyName.ToLower()) {
|
|
665
|
+
case "name": return "accName";
|
|
666
|
+
case "value": return "accValue";
|
|
667
|
+
case "description": return "accDescription";
|
|
668
|
+
case "role": return "accRole";
|
|
669
|
+
case "state": return "accState";
|
|
670
|
+
case "help": return "accHelp";
|
|
671
|
+
case "keyboardshortcut": return "accKeyboardShortcut";
|
|
672
|
+
case "defaultaction": return "accDefaultAction";
|
|
673
|
+
case "focus": args = null; return "accFocus";
|
|
674
|
+
case "selection": args = null; return "accSelection";
|
|
675
|
+
default: return null;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/// <summary>
|
|
680
|
+
/// Get MSAA property via AccessibleObjectFromWindow (hwnd-based, no screen coords).
|
|
681
|
+
/// </summary>
|
|
682
|
+
[HandleProcessCorruptedStateExceptions]
|
|
683
|
+
[SecurityCritical]
|
|
684
|
+
public static object GetLegacyProperty(IntPtr hwnd, string propertyName) {
|
|
685
|
+
if (hwnd == IntPtr.Zero) return null;
|
|
686
|
+
Guid IID_IAccessible = new Guid("618736E0-3C3D-11CF-810C-00AA00389B71");
|
|
687
|
+
object accObj = null;
|
|
688
|
+
try {
|
|
689
|
+
int res = AccessibleObjectFromWindow(hwnd, 0xFFFFFFFC, ref IID_IAccessible, out accObj);
|
|
690
|
+
if (res == 0 && accObj != null) {
|
|
691
|
+
object[] args;
|
|
692
|
+
string memberName = ResolveMemberName(propertyName, out args);
|
|
693
|
+
if (memberName == null) return null;
|
|
694
|
+
return accObj.GetType().InvokeMember(memberName,
|
|
695
|
+
BindingFlags.GetProperty, null, accObj, args);
|
|
696
|
+
}
|
|
697
|
+
} catch { }
|
|
698
|
+
finally {
|
|
699
|
+
if (accObj != null) try { Marshal.ReleaseComObject(accObj); } catch { }
|
|
700
|
+
}
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/// <summary>
|
|
705
|
+
/// Get MSAA property with cascading fallback:
|
|
706
|
+
/// 1. hwnd-based (AccessibleObjectFromWindow)
|
|
707
|
+
/// 2. PID validation (WindowFromPoint check)
|
|
708
|
+
/// 3. Point-based (AccessibleObjectFromPoint)
|
|
709
|
+
/// Call SetExpectedPid() first to set expected PID.
|
|
710
|
+
/// </summary>
|
|
711
|
+
[HandleProcessCorruptedStateExceptions]
|
|
712
|
+
[SecurityCritical]
|
|
713
|
+
public static object GetLegacyPropertyWithFallback(IntPtr hwnd, int x, int y, string propertyName) {
|
|
714
|
+
if (hwnd != IntPtr.Zero) {
|
|
715
|
+
object val = GetLegacyProperty(hwnd, propertyName);
|
|
716
|
+
if (val != null) return val;
|
|
717
|
+
}
|
|
718
|
+
if (!PointBelongsToExpectedProcess(x, y)) return null;
|
|
719
|
+
if (propertyName.ToLower() == "childid") { /* handled below */ }
|
|
720
|
+
|
|
721
|
+
POINT pt; pt.x = x; pt.y = y;
|
|
722
|
+
object accObj = null;
|
|
723
|
+
object childIdObj = null;
|
|
724
|
+
try {
|
|
725
|
+
int res = AccessibleObjectFromPoint(pt, out accObj, out childIdObj);
|
|
726
|
+
if (res == 0 && accObj != null) {
|
|
727
|
+
if (propertyName.ToLower() == "childid") return childIdObj;
|
|
728
|
+
object[] args;
|
|
729
|
+
string memberName = ResolveMemberName(propertyName, out args);
|
|
730
|
+
if (memberName == null) return null;
|
|
731
|
+
if (args != null) args = new object[] { childIdObj };
|
|
732
|
+
return accObj.GetType().InvokeMember(memberName,
|
|
733
|
+
BindingFlags.GetProperty, null, accObj, args);
|
|
734
|
+
}
|
|
735
|
+
} catch { }
|
|
736
|
+
finally {
|
|
737
|
+
if (accObj != null) try { Marshal.ReleaseComObject(accObj); } catch { }
|
|
738
|
+
}
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/// <summary>
|
|
743
|
+
/// Get all MSAA properties at once.
|
|
744
|
+
/// Call SetExpectedPid() first to set expected PID.
|
|
745
|
+
/// </summary>
|
|
746
|
+
[HandleProcessCorruptedStateExceptions]
|
|
747
|
+
[SecurityCritical]
|
|
748
|
+
public static Hashtable GetLegacyPropsWithFallback(IntPtr hwnd, int x, int y) {
|
|
749
|
+
Hashtable props = new Hashtable();
|
|
750
|
+
try {
|
|
751
|
+
string[] names = { "Name", "Value", "Description", "Role", "State",
|
|
752
|
+
"Help", "KeyboardShortcut", "DefaultAction", "ChildId" };
|
|
753
|
+
foreach (string name in names) {
|
|
754
|
+
object val = GetLegacyPropertyWithFallback(hwnd, x, y, name);
|
|
755
|
+
if (val != null) props.Add(name, val);
|
|
756
|
+
}
|
|
757
|
+
} catch { }
|
|
758
|
+
return props;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
public static class ConsoleHelper {
|
|
763
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
764
|
+
public static extern IntPtr GetStdHandle(int nStdHandle);
|
|
765
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
766
|
+
public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
|
|
767
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
768
|
+
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
|
|
769
|
+
|
|
770
|
+
public const int STD_INPUT_HANDLE = -10;
|
|
771
|
+
public const uint ENABLE_MOUSE_INPUT = 0x0010;
|
|
772
|
+
public const uint ENABLE_INSERT_MODE = 0x0020;
|
|
773
|
+
public const uint ENABLE_QUICK_EDIT_MODE = 0x0040;
|
|
774
|
+
public const uint ENABLE_EXTENDED_FLAGS = 0x0080;
|
|
775
|
+
|
|
776
|
+
public static void DisableConsoleInteractions() {
|
|
777
|
+
IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
|
|
778
|
+
uint mode;
|
|
779
|
+
if (GetConsoleMode(consoleHandle, out mode)) {
|
|
780
|
+
mode &= ~ENABLE_QUICK_EDIT_MODE;
|
|
781
|
+
mode &= ~ENABLE_INSERT_MODE;
|
|
782
|
+
mode &= ~ENABLE_MOUSE_INPUT;
|
|
783
|
+
mode |= ENABLE_EXTENDED_FLAGS;
|
|
784
|
+
SetConsoleMode(consoleHandle, mode);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
'@
|
|
789
|
+
|
|
790
|
+
# Set TEMP and TMP to DLL directory to avoid permission issues
|
|
791
|
+
$originalTemp = $env:TEMP
|
|
792
|
+
$originalTmp = $env:TMP
|
|
793
|
+
$env:TEMP = $dllDir
|
|
794
|
+
$env:TMP = $dllDir
|
|
795
|
+
|
|
796
|
+
try {
|
|
797
|
+
# Compile the C# code to DLL
|
|
798
|
+
Add-Type -TypeDefinition $code -Language CSharp -ReferencedAssemblies @('System') -OutputAssembly $dllPath -ErrorAction Stop
|
|
799
|
+
Write-Output "Win32Helper.dll compiled successfully"
|
|
800
|
+
|
|
801
|
+
# Load the newly compiled DLL
|
|
802
|
+
Add-Type -Path $dllPath -ErrorAction Stop
|
|
803
|
+
Write-Output "Win32Helper.dll loaded successfully"
|
|
804
|
+
} catch {
|
|
805
|
+
Write-Output "Compilation failed, falling back to in-memory loading"
|
|
806
|
+
# Fallback: load in-memory if compilation fails
|
|
807
|
+
Add-Type -TypeDefinition $code -Language CSharp -ReferencedAssemblies @('System') -ErrorAction Stop
|
|
808
|
+
} finally {
|
|
809
|
+
# Restore original TEMP/TMP
|
|
810
|
+
$env:TEMP = $originalTemp
|
|
811
|
+
$env:TMP = $originalTmp
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
Write-Output "Win32Helper ready"
|
|
817
817
|
`;
|
|
818
818
|
//# sourceMappingURL=win32.js.map
|