Child Windows

We now look at the third and last type of window the child window. When you create a child window you tell Windows which window will be its parent window. Windows proceeds by displaying the child window "embedded" in its parent window. Whenever the parent window is minimized or made invisible the child window becomes invisible. When the parent window is then restored or maximized the child window reappears. A child window can also be the parent of another child window.
    The program winchild.asm shows key features of the parent/child relationship. The program creates four child windows three of which are direct children of the main window. The fourth window is the child of a child window.
    Because Child1 Child2 and Child3 are at the same level any one of them can appear "between" the other two. Because Child1 is the parent of Child4 the other children (Child2 and Child3) cannot be placed between them.
    Minimize a parent window (main window or Child1) and its children will disappear. After minimizing a parent restoring or maximizing it will cause its children to reappear and be restored.
    As you can see this program is very similar to the winowner.asm program. When you run this program and play with the child windows you will see other behaviors that are slightly different from the other two window types. [ Back to Win32 ASM Page ]
 

The window classes

We register two window classes one for the main parent window and one for the child windows. To prevent the child windows from totally disappearing (we don't yet have a way of recreating them) we add CS_NOCLOSE to the child class style. Otherwise we aren't doing anything special with the child windows so we make their window procedures the default DefWindowProc.
    The main window height and width is set to display all child windows.
.386
.model  flat

; If using TLINK32
don't include vclib.inc
include vclib.inc    ; Microsoft VC++ .lib link names

include win32hst.inc ; constants
structures
and entry names

.data
align   4
wcx     dd      size WNDCLASSEX           ; cbSize
dd      CS_VREDRAW or CS_HREDRAW  ; style
dd      WndProc                   ; lpfnWndProc
dd      0
0                       ; cbClsExtra
cbWndExtra
dd      0                         ; hInstance
dd      0                         ; hIcon
dd      0                         ; hCursor
dd      COLOR_WINDOW+1            ; hbrBackground
dd      0                         ; lpszMenuName
dd      wndclsname                ; lpszClassName
dd      0                         ; hIconSm

wndclsname db 'winmain'
0

align 4
child_class     dd      size WNDCLASSEX           ; cbSize
dd      CS_VREDRAW or CS_HREDRAW or CS_NOCLOSE ; style
dd      DefWindowProc             ; lpfnWndProc
dd      0
0                       ; cbClsExtra
cbWndExtra
dd      0                         ; hInstance
dd      0                         ; hIcon
dd      0                         ; hCursor
dd      COLOR_WINDOW+1            ; hbrBackground
dd      0                         ; lpszMenuName
dd      child_class_name          ; lpszClassName
dd      0                         ; hIconSm

child_class_name db 'childclass'
0

.code

public _start
extrn   GetModuleHandle:near
extrn   LoadIcon:near
LoadCursor:near
extrn   RegisterClassEx:near

_start:
push    large 0         ; NULL string pointer means
call    GetModuleHandle ; get HINSTANCE/HMODULE of EXE file
mov     [wcx.wcx_hInstance]
eax
mov     [child_class.wcx_hInstance]
eax

push    large IDI_WINLOGO
push    large 0         ; hInstance
0 = stock icon
call    LoadIcon
mov     [wcx.wcx_hIcon]
eax
mov     [child_class.wcx_hIcon]
eax

push    large IDC_ARROW
push    large 0         ; hInstance
0 = stock cursor
call    LoadCursor
mov     [wcx.wcx_hCursor]
eax
mov     [child_class.wcx_hCursor]
eax

push    offset wcx
call    RegisterClassEx

push    offset child_class
call    RegisterClassEx

Creating the main window

Here we create a main window. After it is created we save its handle and call a subroutine to create the popup windows.
.data
align   4

hMainWnd dd   0                  ; handle of main window

cwargs   dd   0                  ; dwExStyle
dd   wndclsname         ; lpszClass
dd   wnd_title          ; lpszName
dd   WS_VISIBLE or WS_OVERLAPPED or WS_SYSMENU or WS_THICKFRAME \
or WS_MINIMIZEBOX or WS_MAXIMIZEBOX  ; style
dd   100                ; x
dd   100                ; y
dd   765                ; cx (width)
dd   240                ; cy (height)
dd   0                  ; hwndParent
dd   0                  ; hMenu
dd   0                  ; hInstance
dd   0                  ; lpCreateParams

msgbuf MSG      <>

wnd_title db 'Parent of child windows'
0

.code

extrn   CreateWindowEx:near
extrn   GetMessage:near
DispatchMessage:near
extrn   ExitProcess:near

sub     esp
48            ; allocate argument list
mov     esi
offset cwargs ; set block move source
mov     edi
esp           ; set block move destination
mov     ecx
12            ; number of arguments
rep movsd
mov     eax
[wcx.wcx_hInstance]
mov     [esp+40]
eax      ; set hInstance argument in stack
call    CreateWindowEx
mov     [hMainWnd]
eax
call    create_children   ; call our special procedure

The message loop and program termination

Here is the minimal message loop and program termination code.
msg_loop:
push    large 0         ; uMsgFilterMax
push    large 0         ; uMsgFilterMin
push    large 0         ; hWnd (filter)
0 = all windows
push    offset msgbuf   ; lpMsg
call    GetMessage      ; returns FALSE if WM_QUIT
or      eax
eax
jz      end_loop

push    offset msgbuf
call    DispatchMessage

jmp     msg_loop

end_loop:
push    large 0 ; (error) return code
call    ExitProcess

Main window procedure

Here is the minimal window procedure used by the main window.
.code

extrn   DefWindowProc:near
PostQuitMessage:near

WndProc:
mov     eax
[esp+4+4]           ; message ID
cmp     eax
WM_DESTROY          ; about to start window destruction
je      on_destroy
jmp     DefWindowProc           ; delegate other message processing

on_destroy:
push    large 0
call    PostQuitMessage

xor     eax
eax
ret     16

Creating the child windows

Child windows are created by calling CreateWindowEx with WS_CHILD included in the style argument.
    When CreateWindowEx is used to create a child window the x-y coordinates you supply are client coordinates where (0 0) is the upper left corner of the framed area the client area. Overlapped and popup windows are created with screen coordinates where (0 0) is the upper left corner of the desktop.
    The hwndParent argument is obviously the parent window. When the window type (set in the style argument) is set to WS_CHILD this argument is treated as a parent.
    Notice that WS_CHILD replaces WS_POPUP. We have included WS_CAPTION because child windows do not automatically have a caption (title bar).
    The WS_CLIPSIBLINGS is required if movable child windows are to act rationally when they are overlapped. Remove this style option and see how crazy the child windows can behave when they overlap!
    The following code creates the large CreateWindowEx argument list by building it in a directly addressable area and then copying it onto the stack.
.data
align   4
cwp_args      dd   0                  ; dwExStyle
dd   child_class_name   ; lpszClass
child_caption dd   0                  ; lpszName
dd   WS_VISIBLE or WS_CHILD or WS_SYSMENU or WS_THICKFRAME \
or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_CAPTION \
or WS_CLIPSIBLINGS ; style
child_x       dd   0                  ; x
child_y       dd   0                  ; y
child_width   dd   0                  ; cx (width)
child_height  dd   0                  ; cy (height)
hParent       dd   0                  ; hwndParent
dd   0                  ; hMenu (control ID)
hInstance     dd   0                  ; hInstance
dd   0                  ; lpCreateParams

child_title1 db   'Child1'
0
child_title2 db   'Child2'
0
child_title3 db   'Child3'
0
child_title4 db   'Child4'
0

.code

create_children:
mov     eax
[wcx.wcx_hInstance]
mov     [hInstance]
eax    ; set hInstance argument

mov     eax
[hMainWnd]
mov     [hParent]
eax      ; set parent to main window
mov     [child_x]
10
mov     [child_y]
10
mov     [child_width]
240
mov     [child_height]
180
mov     [child_caption]
offset child_title1
call    create_one_child

mov     [hParent]
eax      ; set parent to previous window
mov     [child_x]
10
mov     [child_y]
10
mov     [child_width]
200
mov     [child_height]
120
mov     [child_caption]
offset child_title2
call    create_one_child

mov     eax
[hMainWnd]
mov     [hParent]
eax      ; set parent to main window
mov     [child_x]
260
mov     [child_y]
10
mov     [child_width]
240
mov     [child_height]
180
mov     [child_caption]
offset child_title3
call    create_one_child

mov     eax
[hMainWnd]
mov     [hParent]
eax      ; set parent to main window
mov     [child_x]
510
mov     [child_y]
10
mov     [child_width]
240
mov     [child_height]
180
mov     [child_caption]
offset child_title4
call    create_one_child

ret

create_one_child:
sub     esp
48            ; allocate argument list
mov     esi
offset cwp_args ; set block move source
mov     edi
esp           ; set block move destination
mov     ecx
12            ; number of arguments
rep movsd
call    CreateWindowEx
ret

end     _start