Popup Windows and Ownership
The first window we created was an overlapped
window. A second type of window is the popup
window, or popup, for short. There doesn't appear to be
any real difference between overlapped windows and popups, except
for initialization. The overlapped window is always
created with a border and a caption (title bar), but a
popup window can be created without either. You can let
Windows figure out where and how big to make your
overlapped window by using CW_USEDEFAULT as the x, y, cx,
and cy arguments of CreateWindowEx, but a popup window
requires explicit location and size arguments.
To make one window always appear in front of another window,
we can make the "background" window the owner of
the other. When the owner is minimized, the owned window will
disappear. Restoring the minimized window will cause the owned
window to reappear. Only overlapped and popup windows can be
owners or be owned. The popup is often a dialog or a message
window, so it's sensible to make a main window the owner of
popups.
The program winowner.asm shows key features of the
owner/owned relationship. The program creates four popup windows,
three of which are owned directly by the main window. The fourth
window is indirectly owned by the main window.
None of the owned windows will appear on the taskbar.
Because Popup1, Popup2, and Popup3 are owned at the same
level, any one of them can appear "between" the other two.
Because Popup1 owns Popup4, the other popups (Popup2 and Popup3)
cannot be placed between them. And none of the popups can appear
"behind" the "top owner", the main window.
If any of the windows are partially covered, clicking on any
of the program windows will bring them all to the front. Minimize
an owner window (main window or Popup1), and the popups it owns
will disappear. After minimizing an owner, restoring or
maximizing it will cause its owned popups to reappear and be
restored.
[ Back to Win32 ASM Page ]
The window classes
We register two window classes, one for the main parent window,
and one for the popup windows. To prevent the owned popup windows
from totally disappearing (we don't yet have a way of recreating
them), we add CS_NOCLOSE to the popup class style. Otherwise, we
aren't doing anything special with the popup windows, so we make
their window procedures the default, DefWindowProc.
.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
popup_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 popup_class_name ; lpszClassName
dd 0 ; hIconSm
popup_class_name db 'popupclass',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 [popup_class.wcx_hInstance],eax
push large IDI_WINLOGO
push large 0 ; hInstance, 0 = stock icon
call LoadIcon
mov [wcx.wcx_hIcon],eax
mov [popup_class.wcx_hIcon],eax
push large IDC_ARROW
push large 0 ; hInstance, 0 = stock cursor
call LoadCursor
mov [wcx.wcx_hCursor],eax
mov [popup_class.wcx_hCursor],eax
push offset wcx
call RegisterClassEx
push offset popup_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 400 ; cx (width)
dd 200 ; cy (height)
dd 0 ; hwndParent
dd 0 ; hMenu
dd 0 ; hInstance
dd 0 ; lpCreateParams
msgbuf MSG <>
wnd_title db 'Owner of popup 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_popups ; 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 popup windows
Popups are created by calling CreateWindowEx with WS_POPUP
included in the style argument.
The hwndParent argument is used in this case as the
owner argument. When the window type (set in the style
argument) is set to WS_OVERLAPPED or WS_POPUP, this argument is
treated as an owner.
Notice that WS_POPUP replaces WS_OVERLAPPED. Also we have
added WS_CAPTION because popups do not automatically have a
caption (title bar).
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 popup_class_name ; lpszClass
popup_caption dd 0 ; lpszName
dd WS_VISIBLE or WS_POPUP or WS_SYSMENU or WS_THICKFRAME \
or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_CAPTION ; style
popup_x dd 0 ; x
popup_y dd 0 ; y
dd 200 ; cx (width)
dd 145 ; cy (height)
hOwner dd 0 ; hwndParent
dd 0 ; hMenu (control ID)
hInstance dd 0 ; hInstance
dd 0 ; lpCreateParams
popup_title1 db 'Popup1',0
popup_title2 db 'Popup2',0
popup_title3 db 'Popup3',0
popup_title4 db 'Popup4',0
.code
create_popups:
mov eax,[wcx.wcx_hInstance]
mov [hInstance],eax ; set hInstance argument
mov eax,[hMainWnd]
mov [hOwner],eax ; set owner to main window
mov [popup_x],150
mov [popup_y],150
mov [popup_caption],offset popup_title1
call create_one_popup
mov [hOwner],eax ; set owner to previous window
mov [popup_x],200
mov [popup_y],200
mov [popup_caption],offset popup_title2
call create_one_popup
mov eax,[hMainWnd]
mov [hOwner],eax ; set owner to main window
mov [popup_x],450
mov [popup_y],150
mov [popup_caption],offset popup_title3
call create_one_popup
mov eax,[hMainWnd]
mov [hOwner],eax ; set owner to main window
mov [popup_x],750
mov [popup_y],150
mov [popup_caption],offset popup_title4
call create_one_popup
ret
create_one_popup:
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