1

I'm using SetWindowsHookEx to register a global hotkey that is supposed to activate my WPF window and bring it to the front.

I found 3 different functions that all seem to achieve that same goal, Window.Activate, SwitchToThisWindow and SetForegroundWindow. I noticed that all 3 methods of bringing my window in focus behaved as expected when debugging in Visual Studio, but failed to properly activate it when the compiled program is run outside the VS debugger.

I created a small example project to illustrate the issue. The application just displays the Activated/Deactivated and GotKeyboardFocus/LostKeyboardFocus events of the window, as well as the last key it captured. It binds the 3 different activation methods to the global hotkeys 1-3.

Below are two gifs of the application with and without debug mode. Both times I'm switching from a currently focused Notepad window to my application using the global hotkey, and then just pressing some keys.

Both windows receive the Activated and GotKeyboardFocus events, but only in debug mode does the application actually receive key presses. The title bar also doesn't change from grey to black when running without debug mode, suggesting the window was never really activated.

Running inside VS

With Debug Mode

Running outside VS

Without Debug Mode

guyyst
  • 388
  • 1
  • 4
  • 16

1 Answers1

1

Before asking this I forgot to specifically search for issues regarding the SetForegroundWindow function, so I missed two question talking about essentially the same problem.

The first question has two highly upvoted answers essentially using the same hack, which technically works but isn't a feasable solution. Using keybd_event(0, 0, 0, 0) to simulate a Keys.NONE press has about a half second delay and for some reason also triggers Nvidia Shadowplay to start a recoridng. (That's probably on Nvidia incorrectly interpreting Keys.NONE as one of their hotkey though).

A very recent answer on the second question seems to be a proper solution using thread associations. This is the working C# code based on the C++ example:

[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);
}
guyyst
  • 388
  • 1
  • 4
  • 16