Question

I'm writing a little utility to allow me to multiplex input to multiple terminal windows;

enter image description here

Any input received by the form (in the centre) is relayed to all terminal windows it manages.

This works well but a problem occurs if I another window moves in front of the terminals. The input form is Topmost so is always visible, but the terminal sessions are hidden.

To work around this, double-clicking on the Input window iterates through all the terminals and calls SetForegroundWindow (from user32.dll) on each in turn.

For Each Manager In Managers
    SetForegroundWindow(ProcessInfo.MainWindowHandle)
    'Threading.Thread.Sleep(15)
Next

If I make this call without the Thread.Sleep(15), only the first and last window in the list are brought to the foreground. The Sleep works around the problem but is a bit of a hack (and is a little ugly when there are a lot of terminals open).

I assume I'm hitting some internal windows tick frequency where at most one window can be brought to the front per tick.

Am I correct in my assumption? And is there a way around this limitation? Something like a MoveAllToForeground() or a way of queueing the messages?

Edit:

To clarify: The terminal process I'm automating is not part of my application, it's PuTTY

If I swap out the call to SetForegroundWindow for BringWindowToTop, I get a similar issue - although in this case, only the first of the terminals is brought to the front, the rest stay behind any other windows.

Edit 2:

Following on from David's suggestion below. The declarations:

Private Declare Auto Function SetWindowPos Lib "user32.dll" (
    ByVal hWnd As IntPtr,
    ByVal hWndInsertAfter As IntPtr,
    ByVal X As Integer,
    ByVal Y As Integer,
    ByVal cx As Integer,
    ByVal cy As Integer,
    ByVal uFlags As SetWindowPosFlags
    ) As Boolean

Private Shared ReadOnly HWND_TOPMOST As New IntPtr(-1)
Private Shared ReadOnly HWND_NOTOPMOST As New IntPtr(-2)
Private Shared ReadOnly HWND_TOP As New IntPtr(0)
Private Shared ReadOnly HWND_BOTTOM As New IntPtr(1)

Private Enum SetWindowPosFlags
    NOSIZE = &H1
    NOMOVE = &H2
    NOZORDER = &H4
    NOREDRAW = &H8
    NOACTIVATE = &H10
    DRAWFRAME = &H20
    FRAMECHANGED = &H20
    SHOWWINDOW = &H40
    HIDEWINDOW = &H80
    NOCOPYBITS = &H100
    NOOWNERZORDER = &H200
    NOREPOSITION = &H200
    NOSENDCHANGING = &H400
    DEFERERASE = &H2000
    ASYNCWINDOWPOS = &H4000
End Enum

And the call...

SetWindowPos(ProcessInfo.MainWindowHandle,
             HWND_TOP,
             0, 0, 0, 0,
             SetWindowPosFlags.NOMOVE Or
             SetWindowPosFlags.NOSIZE Or
             SetWindowPosFlags.NOACTIVATE)
Was it helpful?

Solution

Apparently you want to bring these windows to the front rather than make them be the foreground window. The way to do that is to call SetWindowPos passing HWND_TOP. You'll need to pass SWP_NOMOVE | SWP_NOSIZE as the uFlags parameter since you only want to change the z-order, and not the position and size.

After each call to SetWindowPos, call SetForegroundWindow passing your application's main window handle.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top