TopMost problems

TopMost problems

Post by E Gofort » Fri, 17 Oct 2003 04:54:08


Hello,

I have a VB6 app that calls a VB.NET app via named pipes and a third
party component. Inside the VB.NET app I'm trying to force a form to
the top, at least temporarily. Inside a form's public sub I'm doing:

Public Sub ThisIsMySub(TheseAreMyArguments)
Me.Show
Me.WindowState = FormWindowState.Normal
Me.TopMost = True
Me.TopMost = False
End Sub

However, my form doesn't pop on top like I'd expect it to. BTW the form
is an MDIParent.

I wrote a simple proof-of-concept VB.NET application with a timer
control on an MDI Parent form. I do the same steps as above it works as
I'd expect. When the timer_tick event fires my MDIParent form pops on
top. I can then move another app on top of it or minimize my MDIParent
form. The next time the timer_tick event fires it pops back on top.

The only thing that I could figure out is that another form in my app
had it's TopMost property set to True. I have a StatusBar form that
does this, but I did some checking and its unloaded by the time I do my
"Me.TopMost = True
" Any idea what could be going on?

Thanks,
Eric


*** Sent via Developersdex http://www.yqcomputer.com/ ***
Don't just participate in USENET...get rewarded for it!
 
 
 

TopMost problems

Post by Tom Shelto » Fri, 17 Oct 2003 05:45:37


Since windows 98 you can't force a window to the foreground if another
application window has focus. The only way, I know of to get around
this behavior on all os's is to use P/Invoke to find the threadid of the
current foreground window and then call AttachThreadInput to attach to
that threads input queue - you can then call SetForegroundWindow to move
your application to the front, andt then call AttachThreadInput again to
detach from the thread input queue...

I know it sounds complex, but it goes something like (air code):

Dim currentForeground As IntPtr = GetForegroundWindow()

If currentForeground = Me.Handle Then
SetForegroundWindow(Me.Handle)
Else
Dim processId As IntPtr
Dim threadId As IntPtr = GetWindowThreadProcessId( _
currentforeground, processId)
Dim currenThread as IntPtr = GetCurrentThread()

AttachTheadInput(currentThread, threadId, True)
SetForegroundWindow(currentForeground)
AttachThreadInput(currentThread, threadId, False)
End If

In Windows ME and above, you can call AllowSetForegroundWindow to give
the other process specific permission to steal focus - but it won't work
on windows 98, so I prefere the above method. I hope I haven't forgot
anythign :) I do have a working example of this - though it is in C#,
so if you can't get it working I can try and dig it out.

--
Tom Shelton
MVP [Visual Basic]

 
 
 

TopMost problems

Post by Tom Shelto » Fri, 17 Oct 2003 06:01:30


I should clarify, that it is a window of another process. If the window
is in your own process, then all is ok :)

<snip>

--
Tom Shelton
MVP [Visual Basic]
 
 
 

TopMost problems

Post by E Gofort » Fri, 17 Oct 2003 06:58:37

Hello,

I'm doing my development/testing on an XP box, which is what the users
will be running. Maybe some Win2k, but certainly no Win9x. If you
can't force a window to the foreground if another application window has
focus, how come my proof-of-concept app VB.NET app works so nicely. I
minimize my VB.NET form open up several applications and it pops on top
when the timer fires. I then shift focus back to my other apps or
minimize my VB.NET form and it pops back on top when the timer fires
again, ad infinitum

-Eric




form
as
on
MDIParent
my

I should clarify, that it is a window of another process. If the window
is in your own process, then all is ok :)

<snip>

--
Tom Shelton
MVP [Visual Basic]
.








*** Sent via Developersdex http://www.yqcomputer.com/ ***
Don't just participate in USENET...get rewarded for it!
 
 
 

TopMost problems

Post by Tom Shelto » Fri, 17 Oct 2003 07:26:41


It seems I misread what your trying to do... I thought you were trying
to take the input focus. That is accomplished via the
SetForegroundWindow API and that does not work accross processes. What
you are describing is the SetWindowPos function - moving a window to the
top of the z-order. That does work, but it does not steal the input
focus - in other words, the window pops forward, but the window that
previously had focus still does...

Simple test, open notepad and start typing. Even after the window pops,
you will still be typing in notepad (this is the behavior that I get on
my XP box). The only way to gain input focus is to use the method I
described (or you can use the AllowSetForegroundWindow API since your
on 2K and XP)

Making a topmost window with setwindowpos (which is what is happing with
the TopMost property), is only valid until another window is made
topmost. I presume that there is another window that is stealing the
topmost position.

Is there any way you can post some code that recreates the problem? It
would be much easier to diagnose if there was a small example.

--
Tom Shelton
MVP [Visual Basic]
 
 
 

TopMost problems

Post by E Gofort » Fri, 17 Oct 2003 08:42:00

Hello,

Is there a good way to tell which window is getting stealing TopMost
away from my Window? I put some trace statements in my code and they
indicated that the only likely culprit I could think of was closed and
set to Nothing by the time that my form was set TopMost.

Is it possible that the Garbage Collector hasn't cleaned up the suspect
form by the time my form is set TopMost? Does TopMost care if there's
another TopMost form in the garbage?


-------------------------------------

has
top

It seems I misread what your trying to do... I thought you were trying
to take the input focus. That is accomplished via the
SetForegroundWindow API and that does not work accross processes. What
you are describing is the SetWindowPos function - moving a window to the
top of the z-order. That does work, but it does not steal the input
focus - in other words, the window pops forward, but the window that
previously had focus still does...

Simple test, open notepad and start typing. Even after the window pops,
you will still be typing in notepad (this is the behavior that I get on
my XP box). The only way to gain input focus is to use the method I
described (or you can use the AllowSetForegroundWindow API since your
on 2K and XP)

Making a topmost window with setwindowpos (which is what is happing with
the TopMost property), is only valid until another window is made
topmost. I presume that there is another window that is stealing the
topmost position.

Is there any way you can post some code that recreates the problem? It
would be much easier to diagnose if there was a small example.

-------------------------------------
Eric Goforth
Raleigh, NC

*** Sent via Developersdex http://www.yqcomputer.com/ ***
Don't just participate in USENET...get rewarded for it!
 
 
 

TopMost problems

Post by Tom Shelto » Fri, 17 Oct 2003 09:27:48


Well, you may try to call Dispose on the form when your done with it -
but I don't think that will solve the problem. If you set a new TopMost
window, then that should automatically invalidate any other window as
the topmost....

What you may try is to use the GetTopWindow function, do see which is
the topmost window...

Declare Function GetTopWindow Lib "user32.dll" (ByVal hWnd As IntPtr) As IntPtr

You can pass in your main forms handle, and it should return the handle
of the window at the top of the zorder... This may serve as a check
anyway...

HTH
--
Tom Shelton
MVP [Visual Basic]
 
 
 

TopMost problems

Post by Fergus Coo » Fri, 17 Oct 2003 09:52:28

Hi E, Tom,

I may be wrong on this but it makes sense that FormX.Close calls
FormX.Dispose. And this should release all unmanaged resources immediately as
it knows that FormX has been finshed with. Then FormX can sit in the GCs pile
until Doomsday. My understanding is that the cleaning up that's done in
Finalize should be the just-in-case-they-didn't-call-Dispose type of stuff.

Regards,
Fergus
 
 
 

TopMost problems

Post by Tom Shelto » Fri, 17 Oct 2003 12:01:11


You maybe right on the Close calling dispose... I'll have to check that
out. Either way, I don't think that's his problem. I think it is going
to boil down to his communication method myself, but just trying to
cover all the bases :)

--
Tom Shelton
MVP [Visual Basic]
 
 
 

TopMost problems

Post by E Gofort » Fri, 17 Oct 2003 23:16:58

Hello,

Do the AllowSetForegroundWindow and SetForegroundWindow APIs make the
window in the foreground permanently? I just want it to pop to the top
of the z-order, but then allow the user to put other windows on top, if
they want to.

Eric Goforth
Raleigh, NC

*** Sent via Developersdex http://www.yqcomputer.com/ ***
Don't just participate in USENET...get rewarded for it!
 
 
 

TopMost problems

Post by Tom Shelto » Sat, 18 Oct 2003 00:32:18


No they don't make them on top permanently. All they do is let you
steal the focus. Of course, using the
AllowSetForegroundWindow/SetForegroundWindow - you are setting it up so
that your second app can steal focus from your first, but it still will
not be able to steal focus from any other app. To make it universal,
you have to use the AttachThreadInput method...

I think though that you have another issue going on. Can you remind me
of how your doing the communication - was it with pipes?

--
Tom Shelton
MVP [Visual Basic]
 
 
 

TopMost problems

Post by E Gofort » Sat, 18 Oct 2003 03:17:00

ello,

I added the following declarations to my MDIParent form:

---- Begin code ------------------

Private Declare Function GetTopWindow Lib "user32.dll" (ByVal hWnd
As IntPtr) As IntPtr
Private Declare Function SetForegroundWindow Lib "user32" (ByVal
hwnd As IntPtr) As IntPtr
Private Declare Function GetWindowText Lib "user32" Alias
"GetWindowTextA" (ByVal hwnd As IntPtr, ByVal lpString As String, ByVal
cch As Long) As Long
Private Declare Function SetWindowPos Lib "user32" Alias
"SetWindowPos" (ByVal hwnd As IntPtr, ByVal hWndInsertAfter As Long,
ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long,
ByVal wFlags As Long) As Long

Private Const HWND_TOP As Integer = 0
Private Const HWND_BOTTOM As Integer = 1
Private Const HWND_TOPMOST As Integer = -1
Private Const HWND_NOTOPMOST As Integer = -2
Private Const SWP_NOSIZE As Long = &H1
Private Const SWP_NOMOVE As Long = &H2

Inside my public sub I have:

Me.Show()
Me.WindowState = FormWindowState.Normal

Trace("frmMain.LoadMyForm", "+++++++++++++++++++++++++ (2) - Me.Handle =
" & Me.Handle.ToString & ", TopWindow = " &
GetTopWindow(Me.Handle).ToString)

'Me.TopMost = True
Dim strTest As String
GetWindowText(GetTopWindow(Me.Handle), strTest, 100)

Trace("frmMain.LoadMyForm", "(2) Prior to doing the SetForegroundWindow,
TopWindow Name = " & strTest)

'SetForegroundWindow(Me.Handle)
SetWindowPos(Me.Handle, HWND_TOPMOST, Me.Left, Me.Top, Me.Width,
Me.Height, SWP_NOMOVE Or SWP_NOSIZE)

Trace("frmMain.LoadMyForm", "(2) After doing the SetForegroundWindow
Me.Handle = " & Me.Handle.ToString & ", TopWindow = " &
GetTopWindow(Me.Handle).ToString)

Trace("frmMain.LoadMyForm", "(2) - Me.TopMost = " & Me.TopMost.ToString)
Trace("frmMain.LoadMyForm", "(2) - Me.Visible = " & Me.Visible.ToString)
Trace("frmMain.LoadMyForm", "(2) - Me.WindowState = " &
Me.WindowState.ToString)
Trace("frmMain.LoadMyForm", "(2) - Me.Position = " & Me.Left.ToString &
", " & Me.Top.ToString)

'Me.TopMost = False
SetWindowPos(Me.Handle, HWND_NOTOPMOST, Me.Left, Me.Top, Me.Width,
Me.Height, SWP_NOMOVE Or SWP_NOSIZE)

Trace("frmMain.LoadMyForm", "(3) - Me.TopMost = " & Me.TopMost.ToString)

---- End code ------------------

It shrinks my window and puts it all the way in the upper left hand
corner of the screen. I thought that the coordinates system in .NET and
Windows was different, but would have expected the "SWP_NOMOVE Or
SWP_NOSIZE" to prevent that.

Other than that, it works the same as doing the Me.TopMost = True
shortly followed by a Me.TopMost = False. My form doesn't pop to the
top of the z-order that I can see.

My tracefile shows:

10/16/2003 1:46:59 PM : frmMain.LoadMyForm :
+++++++++++++++++++++++++ (2) - Me.Handle = 8390128, TopWindow
= 11142560
10/16/2003 1:46:59 PM : frmMain.LoadMyForm :
(2) Prior to doing the SetForegroundWindow, TopWindow Name =
10/16/2003 1:46:59 PM : frmMain.LoadMyForm :
(2) After doing the SetForegroundWindow Me.Handle = 8390128,
TopWindow = 11142560
10/16/2003 1:46:59 PM : frmMain.LoadMyForm :
(2) - Me.TopMost = False
10/16/2003 1:46:59 PM : frmMain.LoadMyForm :
(2) - Me.Visible = True
10/16/2003 1:46:59 PM : frmMain.LoadMyForm :
(2) - Me.WindowState = Normal
10/16/2003 1:46:59 PM : frmMain.LoadMyForm :
(2) - Me.Position = -1, 103
10/16/2003 1:46:59 PM : frmMain.Lo
 
 
 

TopMost problems

Post by Tom Shelto » Sat, 18 Oct 2003 04:28:15

n article <O$ XXXX@XXXXX.COM >, E Goforth wrote:

<snip>


Some of your declares are wrong... Any way, here is an example of using
SetForegroundWindow...

Option Strict On
Option Explicit On

Public Class Form1
Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()
MyBase.New()

'This call is required by the Windows Form Designer.
InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As
Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form
Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents Timer1 As System.Windows.Forms.Timer
<System.Diagnostics.DebuggerStepThrough()> Private Sub
InitializeComponent()
Me.components = New System.ComponentModel.Container
Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
'
'Timer1
'
Me.Timer1.Enabled = True
Me.Timer1.Interval = 10000
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(608, 453)
Me.IsMdiContainer = True
Me.Name = "Form1"
Me.Text = "Form1"

End Sub

#End Region

Private Declare Function GetCurrentThreadId Lib "kernel32" () As
IntPtr

Private Declare Function AttachThreadInput Lib "user32" _
(ByVal idAttach As IntPtr, _
ByVal idAttachTo As IntPtr, _
ByVal fAttach As Boolean) As Boolean

Private Declare Function GetWindowThreadProcessId Lib "user32" _
(ByVal hWnd As IntPtr, _
ByVal lpdwProcessId As IntPtr) As IntPtr

Private Declare Function SetForegroundWindow Lib "user32" (ByVal
hWnd As IntPtr) As Boolean
Private Declare Function GetForegroundWindow Lib "user32" () As
IntPtr

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Timer1.Tick
If Me.WindowState = FormWindowState.Minimized Then
Me.WindowState = FormWindowState.Normal
End If

Dim foregroundThread As IntPtr =
GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero)
Dim currentThread As IntPtr = GetCurrentThreadId()

If Not foregroundThread.Equals(currentThread) Then
AttachThreadInput(foregroundThread, currentThread, True)
End If

SetForegroundWindow(Me.Handle)

If Not foregroundThread.Equals(currentThread) Then
AttachThreadInput(foregroundThread, currentThread, False)
End If

End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim f As New Form2
f.MdiParent = Me
f.Visible = True
End Sub
End Class

HTH
--
Tom Shelton
MVP [Visual Basic]