Visual Basic FAQ

SetWindowLong: Create a Floating Window
     
Posted:   Thursday December 26, 1996
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6, and VB3, VB4-16 with appropriate declarations
     
Related:   SetWindowPos: Create a Topmost Window
SetWindowLong: Create a Floating Window
Pure VB: Simulating Owned Windows for MDI Children
 Prerequisites
None.

Another popular question!  This code, like the SetWindowPos method it the Topmost routine, creates a child form that is topmost to the application.  However, unlike the Topmost method, the form set with this method will not float overtop of other applications. In addition, the floating window constructed here will minimize and restore automatically with the parent window, making real floating toolbar windows a snap.

But note .. this code changes the parent relationship of forms. Improper use of this API can cause GPF in VB and possibly in Windows. Please see the Comments following the example. During the design stage, you must ensure that on the floating form you have a command button to either end the application correctly, or to unload the floating window. And when using this method, never stop the app using the VB 'Stop' button.

* Note: VB6 introduced a change (second parameter) to the Show command that will achieve this same result:

    frmChild.Show vbModeless, frmMain

 BAS Module Code
Add the following code to a BAS module:

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Const GWL_HWNDPARENT = (-8)

Public Declare Function SetWindowLong Lib "user32" _
    Alias "SetWindowLongA"_
(ByVal hwnd As Long, ByVal nIndex As Long, _
    ByVal wNewLong As Long) As Long
 Form Code
In the general declarations area of the floating form (not the parent form!) place all of the following code:

Option Explicit

Private OriginalParenthWnd As Long


Private Sub Form_Load()

   OriginalParenthWnd = SetWindowLong(Me.hwnd, _
                                      GWL_HWNDPARENT, _
                                      parent.hwnd)

End Sub


Private Sub Form_Unload()

  'Restore the original parent before unloading
   Call SetWindowLong(Me.hwnd, GWL_HWNDPARENT, OriginalParenthWnd)

End Sub


Private Sub Command1_Click()

  'force the Unload sub to execute to end, 
  'preventing a crash
   Unload Me

  'In addition, make sure that the parent form 
  'implicitly unloads the floating form before it 
  'unloads itself.

End Sub
 Comments
The code above creates a window which appears (while the application has focus) as a topmost window, but which will 'follow' the parent window if it is minimized or restored, or fall behind any other app that is brought to the top (gains focus). This is similar to the behaviour of the property and tool windows in the SDI-style VB design environments, and many other popular programs like PageMaker and PhotoShop.

Unlike a simple topmost window (see SetWindowPos: SetWindowPos: Create a Topmost Window) the SetWindowLong API actually changes the window parent/child relationship via the API call. Because of this, should the application be terminated by closing or ending the application with the VB toolbar Stop button, or the parent form's window close command (or pressing the X), the floating windows Unload sub will not execute. Since the window relationship was changed during execution (via the API call), and was not reset on ending, a VB GPF will occur, causing VB to crash.

It is therefore imperative that you:

  • a) save your work before running the app in the design environment, and
  • b) never use the VB toolbar's Stop command or end the program or close the parent window without first ensuring that the floating window's Unload sub has executed.

A window which has been made 'floating' with the above code does not require to be set to topmost as well .. the above takes care of that as well.

Once the application has been developed, you may want to remove the 'unload' or 'end' command button from the floating form, and use either the normal File/Exit method of ending your application, or do so through some other command or code sequence. To ensure that I don't cause a GPF, my personal practice has been to declare a global (public) variable in a bas module as a 'loaded' flag, and set this to true on the load of the floating form. Then, when the application ends, in the main form's unload sub I check to see if this variable is still true. If it is, I call the floating form's unload routine by issuing an Unload command, assuring that the unload sub has fired. In the floating form's unload sub, after the API, I set the flag variable to false. The app can then be ended safely.

Note that under 16-bit Windows, the name of the API is SetWindowWord, not SetWindowLong. The name was changed for the 32-bit operating systems (Win95/98/NT4) to properly reflect the data being altered (a 4 byte Long in a 32-bit OS vs. a 16 bit Word in a 16-bit OS).


 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter