fix(input): force Genshin foreground via AttachThreadInput (auto-scan no longer aborts)

Auto-scan aborted immediately with "Genshin konnte nicht in den Vordergrund
geholt werden". Root cause: the focus call runs in the background input/capture
helper process, and Windows' foreground lock silently refuses SetForegroundWindow
from a process that is neither foreground nor the last input source. When the user
clicks "Auto-Scan starten" the Electron window is foreground, so the helper's plain
SetForegroundWindow is dropped and focus stays false.

Fix (both the C# sidecar and the PowerShell fallback): before SetForegroundWindow,
attach our thread's input queue to the target (and current-foreground) window
thread with AttachThreadInput and clear SPI_..FOREGROUNDLOCKTIMEOUT, then restore.
This is the same technique Inventory Kamera and other reliable automators use; it
is what our helper was missing after the old ALT-tap workaround was removed on the
wrong assumption that equal integrity level is sufficient (that only covers UIPI
input injection, not foreground changes).

Sidecar recompiled + republished; electron build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
AzuTear
2026-07-06 16:07:30 +02:00
parent 113601f031
commit c8ae0dd7bf
11 changed files with 255 additions and 27 deletions
+3
View File
@@ -51,6 +51,7 @@ export interface AssistantBridge {
publishScannerStatus: (status: ScannerStatusPayload) => Promise<BooleanResult>;
focusMainWindow: () => Promise<BooleanResult>;
focusGenshin: () => Promise<FocusGenshinResult>;
focusGenshinForScanStart: () => Promise<FocusGenshinResult>;
clickScreen: (x: number, y: number) => Promise<ClickResult>;
scrollScreen: (notches: number, anchorX?: number, anchorY?: number) => Promise<ScrollResult>;
onScannerCommand: (callback: (command: "start-auto" | "stop") => void) => () => void;
@@ -66,6 +67,7 @@ export function getAssistantBridge(): AssistantBridge | null {
const apiRecord = api as unknown as Record<string, unknown>;
const canAutoScan = hasFunction(apiRecord, "clickScreen") && hasFunction(apiRecord, "scrollScreen");
const canReviewSamples = hasFunction(apiRecord, "loadReviewSamples") && hasFunction(apiRecord, "saveReviewSample");
const hasFocusGenshinForScanStart = hasFunction(apiRecord, "focusGenshinForScanStart");
return {
isAvailable: true,
@@ -90,6 +92,7 @@ export function getAssistantBridge(): AssistantBridge | null {
publishScannerStatus: (status) => api.publishScannerStatus(status),
focusMainWindow: () => api.focusMainWindow(),
focusGenshin: () => api.focusGenshin(),
focusGenshinForScanStart: () => (hasFocusGenshinForScanStart ? api.focusGenshinForScanStart() : api.focusGenshin()),
clickScreen: (x: number, y: number) => api.clickScreen(x, y),
scrollScreen: (notches: number, anchorX?: number, anchorY?: number) => api.scrollScreen(notches, anchorX, anchorY),
showOverlay: () => api.showOverlay(),