How to Control the Placement of Desktop Windows

ID: Q113702


The information in this article applies to:


SUMMARY

There are situations where you'd like your program to have control over windows that do not belong to the program itself. For example, when a Visual Basic program performs OLE automation with Microsoft Word for Windows, you might want to place the Word window next to the current position of the program's window -- so that the two windows are tiled side by side.

This article demonstrates how to use the Windows API SetWindowPlacement function in a tiling algorithm.


MORE INFORMATION

The example code in this article obtains the handles of three standard applications (CALC.EXE, WRITE.EXE, and NOTEPAD.EXE) by using the FindWindow function. Then it tiles the application windows horizontally or vertically, along with the Visual Basic program's form. The handles are obtained after using the Visual Basic Shell function to launch the applications.

After obtaining the handles, the example code calls the Windows API SetWindowPlacement function to size and position the top-level window. For illustration, an algorithm that tiles the windows either horizontally or vertically is called using an array of window handles.

Possible Enhancement

You could enhance the example in this article by applying a technique described in another Microsoft Knowledge Base article to obtain the handles of already running applications. For information about this technique please see the following article in the Microsoft Knowledge Base:
Q78001 : How to Get Windows Master List (Task List) Using Visual Basic

Step-by-Step Example

Here are the steps necessary to construct the example:
  1. Start a new project in Visual Basic. Form1 is created by default.


  2. Add a command button (Command1) to the form, and change its Caption property to Tile.


  3. Add a code module (Module1) to the project by choosing New Module from the File menu (ALT, F, M).


  4. Add the following code to the general declarations section of Module1:
    
       Type POINTAPI
          x As Integer
          y As Integer
       End Type
    
       Type RECT
          left As Integer
          top As Integer
          right As Integer
          bottom As Integer
       End Type
    
       Type WINDOWPLACEMENT
          length As Integer
          flags As Integer
          showCmd As Integer
          ptMinPosition As POINTAPI
          ptMaxPosition As POINTAPI
          rcNormalPosition As RECT
       End Type
    
       Global lpwndplOld As WINDOWPLACEMENT
       Global lpwndplNew As WINDOWPLACEMENT
    
       ' Enter each of the following Declare statements as one, single line:
    
       Declare Function FindWindow Lib "User" (ByVal lpClassName As Any,
          ByVal lpWindowName As Any) As Integer
       Declare Function GetWindowPlacement Lib "User" (ByVal hwnd As
          Integer, lpwndpl As WINDOWPLACEMENT) As Integer
       Declare Function SetWindowPlacement Lib "User" (ByVal hwnd As
          Integer, lpwndpl As WINDOWPLACEMENT) As Integer
    
       Global Const SW_SHOWNORMAL = 1
       Global hWnds() As Integer
     


  5. Add the following code to Module1:
    
       ' Enter the following Function statement as one, single line:
    
       Function Tile (hWndArr() As Integer, cardinal As Integer,
          HorV As Integer)
    
          Dim ordinal As Integer
          On Error GoTo handler
    
          Select Case HorV
    
          Case 1
    
          For ordinal = 1 To cardinal
    
          ' Optionally, check old placement of window:
          ' ****************************************************
          ' Note that the debug statements induce a slight delay
          ' Enter each Debug.Print statement as one, single line:
          '
          ' lpwndplOld.length = Len(lpwndplOld)
          ' ret% = GetWindowPlacement(hWndArr(ordinal), lpwndplOld)
          ' Debug.Print
          ' Debug.Print "ordinal= "; ordinal
          ' Enter the following two lines as one, single line:
          ' Debug.Print " Old window placement values for handle = ";
          '    Hex$(hWndArr(ordinal))
          ' Debug.Print "lpwndplOld.length= "; lpwndplOld.length
          ' Debug.Print "lpwndplOld.flags= "; lpwndplOld.flags
          ' Debug.Print "lpwndplOld.showCmd= "; lpwndplOld.showCmd
          '
          ' Debug.Print "lpwndplOld.ptMinPosition.x= ";
          '    lpwndplOld.ptMinPosition.x
          ' Debug.Print "lpwndplOld.ptMinPosition.y= ";
          '    lpwndplOld.ptMinPosition.y
          '
          ' Debug.Print "lpwndplOld.ptMaxPosition.x= ";
          '    lpwndplOld.ptMaxPosition.x
          ' Debug.Print "lpwndplOld.ptMaxPosition.y= ";
          '    lpwndplOld.ptMaxPosition.y
          '
          ' Debug.Print "lpwndplOld.rcNormalPosition.left= ";
          '    lpwndplOld.rcNormalPosition.left
          ' Debug.Print "lpwndplOld.rcNormalPosition.top= ";
          '    lpwndplOld.rcNormalPosition.top
          ' Debug.Print "lpwndplOld.rcNormalPosition.right= ";
          '    lpwndplOld.rcNormalPosition.right
          ' Debug.Print "lpwndplOld.rcNormalPosition.bottom= ";
          '    lpwndplOld.rcNormalPosition.bottom
          '**************************************************************
    
             lpwndplNew.length = Len(lpwndplNew)
             lpwndplNew.flags = 0
             lpwndplNew.showCmd = SW_SHOWNORMAL
    
             l = 0
    
             ' Enter the following two lines as one, single line:
             t = (ordinal - 1) *
                (screen.Height / screen.TwipsPerPixelY) \ cardinal
    
             r = (screen.Width / screen.TwipsPerPixelX)
             b = ordinal * (screen.Height / screen.TwipsPerPixelY) \ cardinal
    
             lpwndplNew.rcNormalPosition.left = l
             lpwndplNew.rcNormalPosition.top = t
             lpwndplNew.rcNormalPosition.right = r
             lpwndplNew.rcNormalPosition.bottom = b
    
             ret% = SetWindowPlacement(hWndArr(ordinal), lpwndplNew)
    
          Next ordinal
    
          Case 2
    
          For ordinal = 1 To cardinal
    
             lpwndplNew.length = Len(lpwndplNew)
             lpwndplNew.flags = 0
             lpwndplNew.showCmd = SW_SHOWNORMAL
    
             ' Enter the following two lines as one, single line:
             l = (ordinal - 1) *
                (screen.Width / screen.TwipsPerPixelX) \ cardinal
    
             t = 0
             r = ordinal * (screen.Width / screen.TwipsPerPixelX) \ cardinal
             b = (screen.Height / screen.TwipsPerPixelY)
             lpwndplNew.rcNormalPosition.left = l
             lpwndplNew.rcNormalPosition.top = t
             lpwndplNew.rcNormalPosition.right = r
             lpwndplNew.rcNormalPosition.bottom = b
    
             ret% = SetWindowPlacement(hWndArr(ordinal), lpwndplNew)
    
          Next ordinal
          End Select
          Tile = True
          Exit Function
    
          handler:
          MsgBox Error$
          Tile = False
          Exit Function
    
       End Function
     


  6. Add the following code to Command1_Click event procedure:
    
       Sub Command1_Click ()
          Dim orientation As Integer, handlecount As Integer, i As Integer
    
          ret% = Shell("notepad", 7)
          If FindWindow("NOTEPAD", 0&) Then
             handlecount = handlecount + 1
             ReDim Preserve hWnds(1 To handlecount)
             hWnds(handlecount) = FindWindow("NOTEPAD", 0&)
          End If
    
          ret% = Shell("write", 7)
          If FindWindow("MSWRITE_MENU", 0&) Then
             handlecount = handlecount + 1
             ReDim Preserve hWnds(1 To handlecount)
             hWnds(handlecount) = FindWindow("MSWRITE_MENU", 0&)
          End If
    
          handlecount = handlecount + 1
          ReDim Preserve hWnds(1 To handlecount)
          hWnds(handlecount) = Me.hWnd ' the handle of Form1
    
          ret% = Shell("calc", 7)
          If FindWindow("SCICALC", 0&) Then
             handlecount = handlecount + 1
             ReDim Preserve hWnds(1 To handlecount)
             hWnds(handlecount) = FindWindow("SCICALC", 0&)
          End If
    
          'orientation = 1 ' horizontal
          orientation = 2 ' vertical
    
          ret% = Tile(hWnds(), handlecount, orientation)
    
       End Sub
     


  7. Save the project and run the program by pressing the F5 key.


  8. Click the Tile button, and observe the vertical tiling of the windows. Then stop the program, and close the three applications (CALC.EXE, WRITE.EXE, and NOTEPAD.EXE). Remove the single quotation mark from "orientation = 1" line. Then add a single quotation mark to the "orientation = 2" line. Run the program again, and observe the horizontal tiling.


Additional query words: 3.00


Keywords          : 
Version           : 
Platform          : 
Issue type        : 

Last Reviewed: June 7, 1999