Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding AttachThreadInput - detaching lose focus

i got a little problem fully understanding AttachThreadInput.

I know it's "connecting" the message queue of 2 threads, which (what i want to do) allows me for example to force my window (winforms) in foreground.

Which I can do with this method:

private void SetForegroundWindowEx(IntPtr hWnd)
{
    uint SW_SHOW = 5;
    uint appThread = GetCurrentThreadId();     
    uint foregroundThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);         

    if (foregroundThread != appThread)
    {
        AttachThreadInput(foregroundThread, appThread, true);

        BringWindowToTop(hWnd);
        ShowWindow(hWnd, SW_SHOW);       
        AttachThreadInput(foregroundThread, appThread, false);

    }
    else
    {
        BringWindowToTop(hWnd);
        ShowWindow(hWnd, SW_SHOW);
    }
}

However both of the windows lose focus as soon as the threads detach.

If I wait for the message queue to empty (Application.DoEvents()) and activate my window (which is now in foreground but not focused) it will regain the focus and keep it.

If I do it before the message queue is empty it will lose focus again.

So I would guess something of the detaching takes the focus from my windows but i have no idea what that is or how to prevent it.

This is the first thing I do not quite understand.

The second thing I do not get is, if I do not set my window to foreground:

AttachThreadInput(foregroundThread, appThread, true);

AttachThreadInput(foregroundThread, appThread, false);
Application.DoEvents();
this.Activate();

wait for the message queue to empty, and activate my window (this time it is not in foreground and the other window still has the focus), it actually gets activated, even though the threads are not attached anymore.

perhaps someone with a better understanding of AttachThreadInput can answer me these 2 questions.

INFO:
I need to steal the focus in this case, because my app gets called via an API. The other app that calls mine, waits for feedback from my app and in most times freezes until it gets the info.

In case the other app is Fullscreen many user do not notice the blinking in the taskbar and think the other app crashed and kill it with the Taskmamanger. Since i do not necessarily have control over the other app, i cannot tell it to set the focus to my window.

This method won't get called if it is not absolutelly necessary, in this case this behavior, which is hostile, i know that, is as well wanted by myself as by the user.

like image 269
Visions Avatar asked Jul 26 '13 11:07

Visions


1 Answers

Here's a piece of c# code I use for the same purpose. I'd like to note there are legitimate cases when this may be required. In our situation, it was MS Word automation. Whenever user clicks a toolbar button inside our app, we should immediately bring the Word window up to the user's attention.

public static void ForceWindowIntoForeground(IntPtr window)
{
    uint currentThread = Win32.GetCurrentThreadId();

    IntPtr activeWindow = Win32.GetForegroundWindow();
    uint activeProcess;
    uint activeThread = Win32.GetWindowThreadProcessId(activeWindow, out activeProcess);

    uint windowProcess;
    uint windowThread = Win32.GetWindowThreadProcessId(window, out windowProcess);

    if (currentThread != activeThread)
        Win32.AttachThreadInput(currentThread, activeThread, true);
    if (windowThread != currentThread)
        Win32.AttachThreadInput(windowThread, currentThread, true);

    uint oldTimeout = 0, newTimeout = 0;
    Win32.SystemParametersInfo(Win32.SPI_GETFOREGROUNDLOCKTIMEOUT, 0, ref oldTimeout, 0);
    Win32.SystemParametersInfo(Win32.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref newTimeout, 0);
    Win32.LockSetForegroundWindow(LSFW_UNLOCK);
    Win32.AllowSetForegroundWindow(Win32.ASFW_ANY);

    Win32.SetForegroundWindow(window);
    Win32.ShowWindow(window, Win32.SW_RESTORE);

    Win32.SystemParametersInfo(Win32.SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ref oldTimeout, 0);

    if (currentThread != activeThread)
        Win32.AttachThreadInput(currentThread, activeThread, false);
    if (windowThread != currentThread)
        Win32.AttachThreadInput(windowThread, currentThread, false);
}
like image 131
noseratio Avatar answered Nov 16 '22 22:11

noseratio