Активация окна WPF вручную работает правильно только при отладке в Visual Studio - PullRequest
0 голосов
/ 03 апреля 2020

Я использую SetWindowsHookEx, чтобы зарегистрировать глобальную горячую клавишу, которая должна активировать мое окно WPF и вывести его на передний план.

Я обнаружил 3 разные функции, которые, похоже, достигают той же цели, Window.Activate, SwitchToThisWindow и SetForegroundWindow. Я заметил, что при отладке в Visual Studio все 3 метода фокусировки на моем окне работали, как и ожидалось, но не смогли правильно активировать его, когда скомпилированная программа запускается вне отладчика VS.

Я создал Небольшой пример проекта для иллюстрации проблемы. Приложение просто отображает события Activated/Deactivated и GotKeyboardFocus/LostKeyboardFocus окна, а также последний захваченный ключ. Он связывает 3 различных метода активации с глобальными горячими клавишами 1-3.

Ниже приведены два рисунка приложения с режимом отладки и без него. Оба раза я переключаюсь из текущего окна Блокнота в свое приложение, используя глобальную горячую клавишу, а затем просто нажимаю некоторые клавиши.

Оба windows получают события Activated и GotKeyboardFocus, но только в режиме отладки приложение действительно получает нажатия клавиш. Строка заголовка также не меняется с серого на черный при работе без режима отладки, что свидетельствует о том, что окно никогда не было активировано.

Запуск внутри VS

With Debug Mode

Бег снаружи VS

Without Debug Mode

1 Ответ

0 голосов
/ 04 апреля 2020

Прежде чем задавать этот вопрос, я забыл специально искать проблемы, связанные с функцией SetForegroundWindow, поэтому пропустил два вопроса, говорящих по существу об одной и той же проблеме.

В первом вопросе есть два вопроса Ответы с опротестованием в основном используют тот же хак, который технически работает, но не является выполнимым решением. Использование keybd_event(0, 0, 0, 0) для имитации нажатия Keys.NONE имеет задержку около половины секунды и по какой-то причине также запускает Nvidia Shadowplay, чтобы начать запись. (Вероятно, это связано с тем, что Nvidia неправильно интерпретирует Keys.NONE как один из их горячих клавиш).

Очень недавний ответ на второй вопрос представляется правильным решением с использованием ассоциаций потоков. Это рабочий C# код, основанный на примере C ++:

[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();

[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo,  bool fAttach);

[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, uint Msg);

[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

public static void ForceForegroundWindow(IntPtr hwnd)
{
    uint windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
    uint currentThreadId = GetCurrentThreadId();
    uint CONST_SW_SHOW = 5;
    AttachThreadInput(windowThreadProcessId, currentThreadId, true);
    BringWindowToTop(hwnd);
    ShowWindow(hwnd, CONST_SW_SHOW);
    AttachThreadInput(windowThreadProcessId, currentThreadId, false);
}
...