0

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)
Basic
  • 26,321
  • 24
  • 115
  • 201
  • Why do you want to make these windows be the foreground window? You just want to bring them to the front surely? And why would sleep help? You surely don't want to do that. – David Heffernan Mar 17 '14 at 16:10
  • I definitely don't want to sleep - it's just the only way I've found to make it work so far. This Q is about a way to avoid Sleeping. And no, I don't particularly care if they actually become the foremost for any period of time - I just want them in front of all windows (except my Topmost one). In case it's not clear, those Terminal windows are an application which I haven't written, I'm merely automating it – Basic Mar 17 '14 at 16:11

1 Answers1

2

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Looks promising but what should I be passing into `hWndInsertAfter`? If I give it a `HWND_TOPMOST`, It places them _above_ my topmost window. The other 3 options (`NOTOPMOST`/`TOP`/`BOTTOM`) don't seem to move terminals in front of the window covering them (a browser in this instance) (I'll edit my Q to show the call I'm making) – Basic Mar 17 '14 at 16:32
  • Use `HWND_TOP` which is what I said in the answer. – David Heffernan Mar 17 '14 at 16:34
  • And as I said in my comment, The only one with an obvious impact is TOPMOST - TOP has no visible impact. I understand fully that the docs linked say that it should work that way, so perhaps I'm missing something else? – Basic Mar 17 '14 at 16:36
  • Sorry, you need to remove `SWP_NOACTIVATE`. Mea culpa. – David Heffernan Mar 17 '14 at 16:38
  • No worries, I'm just grateful for the help. Unfortunately, this seems a little circular... using `HWND_TOP` with `NOMOVE | NORESIZE` results in just the first of the terminals being shown. I _must_ be missing something else as if I do two calls (TOPMOST followed by NOTOPMOST) it has the desired outcome. Of course, this still isn't "right" and I'd like to get it working properly. In any case, thanks, you've pointed me in the right direction so I'll go off and see what I can come up with – Basic Mar 17 '14 at 17:23
  • OK, I can repro that. Fix it by calling `SetForegroundWindow` passing your main form's window handle (not the putty window handles) after each call to `SetWindowPos`. So you will call `SetForegroundWindow` four times in total, each time passing the same window handle, the handle of the main window of your application. – David Heffernan Mar 17 '14 at 17:40
  • That did it, thanks very much for your help! Incidentally, do you happen to know why that call is required? – Basic Mar 17 '14 at 18:23