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