Make Form stay on top until application is deactivated

  • Thread starter Thread starter TC
  • Start date Start date
T

TC

Hello All,

I am working on an addin for Outlook and I am displaying a form as modeless.

I am able to make that form stay on top via the SetWindowPos API.

However, when the user deactivates Outlook by switching to another
application, I want the form to be released from being on top.

One would assume that I could use the same API and set the form to
HWND_NOTOPMOST but this is not so easy.

The top level Application object from within Outlook does not have Activate
and Deactivate events like other Office Suite application. The Explorer
object does but these events fire off wildly while still within Outlook
itself depending upon what the user's selection is.

I tried something like the code below:

' Test to see if the top window is indeed Outlook
hWindowHandle = FindWindow(g_constrOUTLOOK, vbNullString)
hWindowHandle2 = GetForegroundWindow()

If hWindowHandle = hWindowHandle2 Then
lngResult = SetWindowPos(hHandle, HWND_TOPMOST, 0, 0, 0, 0, FLAGS)
Else
lngResult = SetWindowPos(hHandle, HWND_BOTTOM, 0, 0, 0, 0, FLAGS)
End If

The idea being that if the top level window is not Outlook, release the form
from the top. I would call the above function from the Activate /
Deactivate events caused by the Explorer object. From within the VB IDE, it
works quite nicely. Once compiled, the events fire off in such a way that
it locks my machine up. I've tried various changes to the logic but none
work.

The above said, I was wondering if there is a way to attach the .dll's form
to the Outlook application as a child form?

Any ideas?

Thanks & Regards,

TC
 
I am working on an addin for Outlook and I am displaying a form as modeless.

I am able to make that form stay on top via the SetWindowPos API.

However, when the user deactivates Outlook by switching to another
application, I want the form to be released from being on top.

One would assume that I could use the same API and set the form to
HWND_NOTOPMOST but this is not so easy.

The top level Application object from within Outlook does not have Activate
and Deactivate events like other Office Suite application. The Explorer
object does but these events fire off wildly while still within Outlook
itself depending upon what the user's selection is.

I tried something like the code below:

' Test to see if the top window is indeed Outlook
hWindowHandle = FindWindow(g_constrOUTLOOK, vbNullString)
hWindowHandle2 = GetForegroundWindow()

If hWindowHandle = hWindowHandle2 Then
lngResult = SetWindowPos(hHandle, HWND_TOPMOST, 0, 0, 0, 0, FLAGS)
Else
lngResult = SetWindowPos(hHandle, HWND_BOTTOM, 0, 0, 0, 0, FLAGS)
End If

The idea being that if the top level window is not Outlook, release the form
from the top. I would call the above function from the Activate /
Deactivate events caused by the Explorer object. From within the VB IDE, it
works quite nicely. Once compiled, the events fire off in such a way that
it locks my machine up. I've tried various changes to the logic but none
work.

The above said, I was wondering if there is a way to attach the .dll's form
to the Outlook application as a child form?

A better approach might be to make your app's form "owned" by the OL
main window. That way the OS will maintain it's zorder above OL and
also hide it when the OL window is minimized. You can do this by using
SetWindowLong. (Note GWL_HWNDPARENT is a misnomer as it actually sets
the owner, not the parent.)

Ex:

'declares...
Private Const GWL_HWNDPARENT = (-8)
Private Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

'usage - form_load or wherever...
Dim hwndOwnr As Long

hwndOwnr = FindWindow(g_constrOUTLOOK, vbNullString)
If hwndOwnr <> 0 Then
SetWindowLong Me.hwnd, GWL_HWNDPARENT, hwndOwnr
End If


-Tom
MVP - Visual Basic
(please post replies to the newsgroup)
 
Hey Tom,

Yes! Yes! Yes!

Thanks so much.

This is EXACTLY what I needed. I had been looking to try to do this but was
using the wrong API.

Regards,

Todd
 
Setting the owner of an existing window sounds a little dangerous to me.

Can't you just set the parent/owner param of CreateWindow or whatever the VB
equivalent is?
 
Setting the owner of an existing window sounds a little dangerous to me.

Hi Ken,

Never had a problem with it. AFIK the owner / owned relationship (not
to be confused with parent / child BTW) effects only the zorder and
visibility behavior. I've found the SDK docs to be sometimes confusing
with regard to owner/owned vs parent/child relationships since they
often use the terms interchangably (and incorrectly). For example the
the defined name of the GWL_HWNDPARENT constant used with
Get/SetWindowlong would imply that it relates to parent/child, when in
fact it does not. (A more appropriate name choice would have been
GWL_HWNDOWNER.) Using SetParent OTOH is a different story and caution
would definitely be advisable :-)
Can't you just set the parent/owner param of CreateWindow or whatever the VB
equivalent is?
CreateWindow's hwndParent arg is entirely different as it set's the
parent (not the owner). If you're referring to the Ownerform argument
to VB's show statement, I can't see how it's useful in this case
because it only accepts a Form object. My guess is behind the scenes
it's calling SetWindowLong with GWL_HWNDPARENT anyway, which AFIK is
the only way the API provides to set the owner.


-Tom
MVP - Visual Basic
(please post replies to the newsgroup)
 
Tom said:
[snip]
A better approach might be to make your app's form "owned" by the OL
main window. That way the OS will maintain it's zorder above OL and
also hide it when the OL window is minimized. You can do this by
using
SetWindowLong. (Note GWL_HWNDPARENT is a misnomer as it actually
sets
the owner, not the parent.)

Ex:

'declares...
Private Const GWL_HWNDPARENT = (-8)
Private Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA"
_
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

'usage - form_load or wherever...
Dim hwndOwnr As Long

hwndOwnr = FindWindow(g_constrOUTLOOK, vbNullString)
If hwndOwnr <> 0 Then
SetWindowLong Me.hwnd, GWL_HWNDPARENT, hwndOwnr
End If


-Tom
MVP - Visual Basic
(please post replies to the newsgroup)


This sounds like a possible road for a solution to a problem
I posted, but couldn't get resolved. The application for which
I'm writing an addin, and which needs to "own" my form
actually opens two, seemingly separate windows. My form
needs to stay on top as long as EITHER of the app's
windows are on top, but be able to sink when BOTH of the
app's window sink.

The application's windows come to the front and sink
independantly of each other.

Would the best way for my addin to address this be to have
a timer control that uses your method to change to owner?

TIA for any additional help or suggestions.
 
CreateWindow hwndParent arg will set the owner for a top-level window. See
this from MSDN

"Handle to the parent or owner window of the window being created. To create
a child window or an owned window, supply a valid window handle. This
parameter is optional for pop-up windows. "

I agree it is confusing but the param and the GetWindowLong field are
overloaded. They do mean parent for child windows and owner for top-level
windows.
 
That sounds no different that specifying the Owner parameter in VB's Show
statement ...

form2.show vbmodeless, form1

This will make it topmost to the app - albeit not modal - and allow other
apps to appear over it.

--


Randy Birch
MS MVP Visual Basic
http://vbnet.mvps.org/


: CreateWindow hwndParent arg will set the owner for a top-level window.
See
: this from MSDN
:
: "Handle to the parent or owner window of the window being created. To
create
: a child window or an owned window, supply a valid window handle. This
: parameter is optional for pop-up windows. "
:
: I agree it is confusing but the param and the GetWindowLong field are
: overloaded. They do mean parent for child windows and owner for top-level
: windows.
:
:
: --
:
: Ken Wickes [MSFT]
: This posting is provided "AS IS" with no warranties, and confers no
rights.
:
:
: : > On Sun, 16 Jan 2005 12:43:36 -0800, "Ken Wickes [MSFT]"
: >
: >>Setting the owner of an existing window sounds a little dangerous to me.
: >
: > Hi Ken,
: >
: > Never had a problem with it. AFIK the owner / owned relationship (not
: > to be confused with parent / child BTW) effects only the zorder and
: > visibility behavior. I've found the SDK docs to be sometimes confusing
: > with regard to owner/owned vs parent/child relationships since they
: > often use the terms interchangably (and incorrectly). For example the
: > the defined name of the GWL_HWNDPARENT constant used with
: > Get/SetWindowlong would imply that it relates to parent/child, when in
: > fact it does not. (A more appropriate name choice would have been
: > GWL_HWNDOWNER.) Using SetParent OTOH is a different story and caution
: > would definitely be advisable :-)
: >
: >>Can't you just set the parent/owner param of CreateWindow or whatever
the
: >>VB
: >>equivalent is?
: > CreateWindow's hwndParent arg is entirely different as it set's the
: > parent (not the owner). If you're referring to the Ownerform argument
: > to VB's show statement, I can't see how it's useful in this case
: > because it only accepts a Form object. My guess is behind the scenes
: > it's calling SetWindowLong with GWL_HWNDPARENT anyway, which AFIK is
: > the only way the API provides to set the owner.
: >
: >
: > -Tom
: > MVP - Visual Basic
: > (please post replies to the newsgroup)
:
:
 
CreateWindow hwndParent arg will set the owner for a top-level window. See
this from MSDN

"Handle to the parent or owner window of the window being created. To create
a child window or an owned window, supply a valid window handle. This
parameter is optional for pop-up windows. "

So it really ~does~ mean "or"! And here I thought it was just
terminology obfuscation. said:
I agree it is confusing but the param and the GetWindowLong field are
overloaded. They do mean parent for child windows and owner for top-level
windows.

Makes sense now, given the way CreateWindow works.

Getting back to your original post, are you saying there's a possible
danger in re-assigning the owner for a top-level window? I've used it
for years without a problem. For example it's sometimes handy for
changing the zorder behavior of multiple VB forms. Normally they're
all owned by the non-visible "thundermain" which causes their zorders
to be in effect "slaved" to each other. Setting the owner to 0 breaks
the linkage and provides independent zordering. I've never encountered
any side effects with it, though maybe I'm just not looking in the
right place.


-Tom
MVP - Visual Basic
(please post replies to the newsgroup)
 
...
Would the best way for my addin to address this be to have
a timer control that uses your method to change to owner?

That would be my choice, unless Ken points out some heretofore hidden
danger.

-Tom
MVP - Visual Basic
(please post replies to the newsgroup)
 
Tom said:
Getting back to your original post, are you saying there's a possible
danger in re-assigning the owner for a top-level window? I've used it
for years without a problem.

I recall there being teardown issues, if you don't reset the owner before the owner
is destroyed. Been a decade since I really tested it, though. But if you clean up
after yourself, no, I've never seen a problem either.
 
It may very well work today, but it worries me because it is not documented.

If CreateWindow provides a viable solution than the original poster should
stick with it.
 
Ken said:
It may very well work today, but it worries me because it is not
documented.

It "works" fine if you clean up after yourself (do an orderly teardown). Has since
Windows 3.0, at least.
If CreateWindow provides a viable solution than the original poster
should stick with it.

CreateWindow isn't something most VB users encounter on a very regular basis. Never
in relation to a "form" in the sense being used by the OP. It is possible to hook
the window creation, and alter its properties at that time:

http://www.ftponline.com/archives/premier/mgznarch/vbpj/1999/07jul99/bb0799.pdf

But you can't use CreateWindow directly to create a standard VB form.

Later... Karl
 
I recall there being teardown issues, if you don't reset the owner before the owner
is destroyed. Been a decade since I really tested it, though. But if you clean up
after yourself, no, I've never seen a problem either.

Yeah, I do seem to recall doing that a few times myself way back when,
but I don't think it was ever because of a problem with it. Can't hurt
in any case :-)


-Tom
MVP - Visual Basic
(please post replies to the newsgroup)
 
Yeah, I do seem to recall doing that a few times myself way back when,
but I don't think it was ever because of a problem with it. Can't hurt
in any case :-)

Yep, I've used the technique for years and never had a problem unless
I forgot to reset the original parent (only takes forgetting once or
twice to get the point across. ;-) ).

Bryan
____________________________________________________________
New Vision Software "When the going gets weird,"
Bryan Stafford "the weird turn pro."
alpine_don'(e-mail address removed) Hunter S. Thompson -
Microsoft MVP-Visual Basic Fear and Loathing in LasVegas
 
Karl E. Peterson said:
It "works" fine if you clean up after yourself (do an orderly teardown). Has since
Windows 3.0, at least.

I remember waaaay back in the day some VB forms would cause GPFs when
parented to the desktop hWnd. Can't recall specifics though... ok, I'll be
quiet now <g>
 
Klaus H. Probst said:
I remember waaaay back in the day some VB forms would cause GPFs when
parented to the desktop hWnd. Can't recall specifics though... ok, I'll be
quiet now <g>

Owning things to the desktop is a bad idea. It used to be that modal
dialogs owned by the desktop would disable the desktop with interesting
effects. Today the dialog manager checks for that case, but still, dont
have your window owned by the desktop.
 
Back
Top