babalae/better-genshin-impact · error · TimeoutException

动作 {snapshot.Description} 执行 {attempts} 次后,等待 {snapshot.Targ

Error message

动作 {snapshot.Description} 执行 {attempts} 次后,等待 {snapshot.TargetDescription} 超时({snapshot.Timeout}ms)

What it means

Thrown when ChildSessionNativeMethods.TryFocusRdpInputWindow(Handle) returns false, meaning the Win32 SetForegroundWindow or equivalent call failed to give keyboard focus to the RDP input child window. Without focus, SendKeys would deliver keystrokes to the wrong target, so the code refuses to send.

Source

Thrown at BetterGenshinImpact/Core/BgiVision/BvFlow.cs:349

            return Task.CompletedTask;
        });
    }

    private async Task ExecuteActionStep(BvFlowActionSnapshot snapshot, BvFlowExecutionContext context)
    {
        var startedAt = _services.GetTimestamp();
        var attempts = 0;

        while (true)
        {
            _services.ThrowIfCancellationRequested();
            attempts++;
            await snapshot.Action(context);

            var remainingMilliseconds = snapshot.Timeout - _services.GetElapsedMilliseconds(startedAt);
            if (remainingMilliseconds <= 0)
            {
                throw new TimeoutException(
                    $"动作 {snapshot.Description} 执行 {attempts} 次后,等待 {snapshot.TargetDescription} 超时({snapshot.Timeout}ms)");
            }

            await _services.Delay(GetDelayMilliseconds(snapshot.RetryInterval, remainingMilliseconds));

            if (_services.GetElapsedMilliseconds(startedAt) >= snapshot.Timeout)
            {
                throw new TimeoutException(
                    $"动作 {snapshot.Description} 执行 {attempts} 次后,等待 {snapshot.TargetDescription} 超时({snapshot.Timeout}ms)");
            }

            var result = FindTargets(snapshot.Targets, snapshot.Condition);
            _services.ThrowIfCancellationRequested();
            if (_services.GetElapsedMilliseconds(startedAt) >= snapshot.Timeout)
            {
                throw new TimeoutException(
                    $"动作 {snapshot.Description} 执行 {attempts} 次后,等待 {snapshot.TargetDescription} 超时({snapshot.Timeout}ms)");
            }

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Ensure the BetterGI main window is foreground before invoking shortcuts (bring it to front first).
  2. Call ChildSessionNativeMethods.ClearRdpInputWindowCache(Handle) to invalidate stale handles then retry.
  3. Retry the focus attempt a few times with short delays; if still failing, notify the user to click the child session area.
  4. Allow the OperatingSystem focus-stealing workaround (AttachThreadInput) if available in TryFocusRdpInputWindow.

Example fix

// before
if (!ChildSessionNativeMethods.TryFocusRdpInputWindow(Handle))
    throw new InvalidOperationException("无法将键盘焦点切换到桌面分身。");
RunComStep($"向 Child Session 发送 {displayName}", () => SendKeyStrokes(strokes));

// after — retry focus, then degrade gracefully
ChildSessionNativeMethods.ClearRdpInputWindowCache(Handle);
var focused = false;
for (var i = 0; i < 3; i++)
{
    if (ChildSessionNativeMethods.TryFocusRdpInputWindow(Handle)) { focused = true; break; }
    await Task.Delay(100, ct);
}
if (!focused)
    throw new InvalidOperationException("无法将键盘焦点切换到桌面分身,请点击桌面分身窗口后重试。");
Defensive patterns

Strategy: retry

Validate before calling

// Bring main window to foreground before focusing RDP
ChildSessionNativeMethods.ClearRdpInputWindowCache(Handle);
var focused = ChildSessionNativeMethods.TryFocusRdpInputWindow(Handle);

Type guard

bool CanFocusRdpWindow() =>
    ConnectedState == 1 && ChildSessionNativeMethods.TryFocusRdpInputWindow(Handle);

Try / catch

for (var i = 0; i < 3; i++)
{
    if (ChildSessionNativeMethods.TryFocusRdpInputWindow(Handle)) break;
    await Task.Delay(100, ct);
}
if (!focused) throw new InvalidOperationException("无法将键盘焦点切换到桌面分身。");

Prevention

When it happens

Trigger: Called inside SendShortcut after ConnectedState is 1. TryFocusRdpInputWindow fails when the RDP window handle is not in the foreground-capable state — e.g. the BetterGI window is minimized, another application grabbed focus, or the RDP child window handle cache is stale after a reconnection.

Common situations: User clicked away to another app between triggering the shortcut and delivery, the main window is minimized/background (Windows restricts SetForegroundWindow for background processes), or the cached RDP input window handle pointed to a destroyed window after reconnect.

Understand the failure class

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/8b13467406497a96. Report an issue: GitHub.