Skip to content

Commit 8960843

Browse files
committed
fix: fix attaching to wrong application window
1 parent f25b000 commit 8960843

File tree

1 file changed

+31
-33
lines changed

1 file changed

+31
-33
lines changed

lib/commands/app.ts

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,14 @@ export async function changeRootElement(this: NovaWindowsDriver, pathOrNativeWin
179179
const result = await this.sendPowerShellCommand(/* ps1 */ `(Get-Process -Name 'ApplicationFrameHost').Id`);
180180
const processIds = result.split('\n').map((pid) => pid.trim()).filter(Boolean).map(Number);
181181

182-
this.log.debug('Process IDs of ApplicationFrameHost processes: ' + processIds.join(', '));
182+
this.log.debug(`Process IDs of ApplicationFrameHost processes (${processIds.length}): ` + processIds.join(', '));
183183
try {
184184
await this.attachToApplicationWindow(processIds);
185185
return;
186-
} catch {
187-
// noop
186+
} catch (err) {
187+
if (err instanceof Error) {
188+
this.log.debug(`attachToApplicationWindow failed: ${err.message}`);
189+
}
188190
}
189191

190192
this.log.info(`Failed to locate window of the app. Sleeping for ${SLEEP_INTERVAL_MS} milliseconds and retrying... (${i}/20)`); // TODO: make a setting for the number of retries or timeout
@@ -282,45 +284,41 @@ export async function setWindowRect(
282284
return await this.getWindowRect();
283285
}
284286

285-
export async function waitForNewWindow(this: NovaWindowsDriver, pid: number, timeout: number): Promise<number> {
287+
288+
export async function attachToApplicationWindow(this: NovaWindowsDriver, processIds: number[]): Promise<void> {
289+
this.log.debug(`Attaching to application window. Process IDs: [${processIds.join(', ')}]`);
290+
const timeout = (this.caps['ms:waitForAppLaunch'] ?? 0) * 1000 || SLEEP_INTERVAL_MS * 20;
286291
const start = Date.now();
287292
let attempts = 0;
288293

289294
while (Date.now() - start < timeout) {
290-
const handles = getWindowAllHandlesForProcessIds([pid]);
295+
const handles = getWindowAllHandlesForProcessIds(processIds);
291296

292297
if (handles.length > 0) {
293-
return handles[handles.length - 1];
298+
this.log.debug(`Found ${handles.length} window handle(s) for PIDs [${processIds.join(', ')}]: ${handles.map((h) => `0x${h.toString(16).padStart(8, '0')}`).join(', ')}`);
299+
300+
for (const handle of handles) {
301+
const elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN, new PropertyCondition(Property.NATIVE_WINDOW_HANDLE, new PSInt32(handle))).buildCommand());
302+
303+
if (elementId.trim()) {
304+
await this.sendPowerShellCommand(/* ps1 */ `$rootElement = ${new FoundAutomationElement(elementId).buildCommand()}`);
305+
if ((await this.sendPowerShellCommand(/* ps1 */ `$null -ne $rootElement`)).toLowerCase() === 'true') {
306+
const confirmedHandle = Number(await this.sendPowerShellCommand(AutomationElement.automationRoot.buildGetPropertyCommand(Property.NATIVE_WINDOW_HANDLE)));
307+
this.log.info(`Successfully attached to window. Native window handle: 0x${confirmedHandle.toString(16).padStart(8, '0')}`);
308+
if (!trySetForegroundWindow(confirmedHandle)) {
309+
await this.focusElement({
310+
[W3C_ELEMENT_KEY]: elementId,
311+
} satisfies Element);
312+
}
313+
return;
314+
}
315+
}
316+
}
294317
}
295318

296-
this.log.debug(`Waiting for the process window to appear... (${++attempts}/${Math.floor(timeout / SLEEP_INTERVAL_MS)})`);
319+
this.log.debug(`No attachable window found yet. Sleeping for ${SLEEP_INTERVAL_MS} milliseconds and retrying... (${++attempts}/${Math.floor(timeout / SLEEP_INTERVAL_MS)})`);
297320
await sleep(SLEEP_INTERVAL_MS);
298321
}
299322

300-
throw new Error('Timed out waiting for window.');
301-
}
302-
303-
export async function attachToApplicationWindow(this: NovaWindowsDriver, processIds: number[]): Promise<void> {
304-
const nativeWindowHandle = await waitForNewWindow.call(this, processIds[0], this.caps['ms:waitForAppLaunch'] ?? SLEEP_INTERVAL_MS * 20);
305-
306-
let elementId = '';
307-
for (let i = 1; i <= 20; i++) {
308-
elementId = await this.sendPowerShellCommand(AutomationElement.rootElement.findFirst(TreeScope.CHILDREN, new PropertyCondition(Property.NATIVE_WINDOW_HANDLE, new PSInt32(nativeWindowHandle))).buildCommand());
309-
if (elementId) {
310-
break;
311-
}
312-
this.log.info(`The window with handle 0x${nativeWindowHandle.toString(16).padStart(8, '0')} is not yet available in the UI Automation tree. Sleeping for ${SLEEP_INTERVAL_MS} milliseconds and retrying... (${i}/20)`); // TODO: make a setting for the number of retries or timeout
313-
await sleep(SLEEP_INTERVAL_MS); // TODO: make a setting for the sleep timeout
314-
}
315-
316-
await this.sendPowerShellCommand(/* ps1 */ `$rootElement = ${new FoundAutomationElement(elementId).buildCommand()}`);
317-
if ((await this.sendPowerShellCommand(/* ps1 */ `$null -ne $rootElement`)).toLowerCase() === 'true') {
318-
const nativeWindowHandle = Number(await this.sendPowerShellCommand(AutomationElement.automationRoot.buildGetPropertyCommand(Property.NATIVE_WINDOW_HANDLE)));
319-
if (!trySetForegroundWindow(nativeWindowHandle)) {
320-
await this.focusElement({
321-
[W3C_ELEMENT_KEY]: elementId,
322-
} satisfies Element);
323-
};
324-
return;
325-
}
323+
throw new Error('Timed out waiting to attach to application window.');
326324
}

0 commit comments

Comments
 (0)