I'm writing a little utility to allow me to multiplex input to multiple terminal windows;
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)
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.