Check box example: caption options

This example shows one way to use check boxes in a nondialog window. It also shows you what happens when these caption options are combined. It shows how to change a window style after it has been created. And, finally, it shows how to force the redrawing of a window frame.
    Source file is wincaptn.asm.

Back to Win32 ASM Page.

First we take care of the main window, and the application start and end.

        .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

        public _start

        .code
_start:

        .data
        align   4
wc      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

wndclsname db 'winmain',0

        extrn   GetModuleHandle:near

        .code
        push    large 0         ; NULL string pointer means
        call    GetModuleHandle ; get HINSTANCE/HMODULE of EXE file
        mov     [wc.wc_hInstance],eax

        extrn   LoadIcon:near,LoadCursor:near

        .code
        push    large IDI_WINLOGO
        push    large 0         ; hInstance, 0 = stock icon
        call    LoadIcon
        mov     [wc.wc_hIcon],eax

        push    large IDC_ARROW
        push    large 0         ; hInstance, 0 = stock cursor
        call    LoadCursor
        mov     [wc.wc_hCursor],eax

        extrn   RegisterClass:near

        .code
        push    offset wc
        call    RegisterClass

        .data
        align   4
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   40                 ; x
         dd   40                 ; y
         dd   300                ; cx (width)
         dd   110                ; cy (height)
         dd   0                  ; hwndParent
         dd   0                  ; hMenu
         dd   0                  ; hInstance
         dd   0                  ; lpCreateParams

wnd_title db 'Caption (title bar) styles',0

        extrn   CreateWindowEx:near

        .code
        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,[wc.wc_hInstance]
        mov     [esp+40],eax      ; set hInstance argument in stack
        call    CreateWindowEx

        extrn   GetMessage:near,DispatchMessage:near

        .data
        align   4
msgbuf MSG      <>

        .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:

        extrn   ExitProcess:near

        .code
        push    large 0 ; (error) return code
        call    ExitProcess
Next we define our test window. We explicitly add in the WS_CAPTION because we will use a copy of this style argument as a basis for style changes. It turns out that although an overlapped windows always creates a caption (title bar), the title bar can be removed after the window is created by changing the window style.
    The window procedure for this window is DefWindowProc because no special message handling is required.
;
; the test display window
;
        .data
        align 4

testwnd dd 0

wc2     dd      CS_VREDRAW or CS_HREDRAW  ; 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      wndclsname2               ; lpszClassName

test     dd   0                  ; dwExStyle
         dd   wndclsname2        ; lpszClass
         dd   caption2           ; lpszName
         dd   WS_VISIBLE or WS_OVERLAPPED or WS_CAPTION  ; style
         dd   160                ; x
         dd   160                ; y
         dd   200                ; cx (width)
         dd   200                ; cy (height)
         dd   0                  ; hwndParent
         dd   0                  ; hMenu
         dd   0                  ; hInstance
         dd   0                  ; lpCreateParams

wndclsname2 db 'testdisplay',0
caption2 db 'Display test',0
Now we define the check boxes, which are defined as buttons. There is no WNDCLASS structure because the button class is already defined and registered. We will handle all of the important check box activity in the main window, which is the parent (and container) of all these check boxes.
; this holds the style bits used to change
;   the style options of the test window
;
        align   4

test_style dd 0

;
; control IDs for buttons
;
IDCTL_SYSMENU equ 101
IDCTL_SIZEBOX equ 102
IDCTL_MINBOX  equ 103
IDCTL_MAXBOX  equ 104
;
; Temporary CreateWindowEx argument list for the check box "button" windows
;
        align   4

chkbox   dd   0                  ; dwExStyle
         dd   btnclsname         ; lpszClass
         dd   0                  ; lpszName
         dd   WS_VISIBLE or WS_CHILD or BS_CHECKBOX  ; style
         dd   0                  ; x
         dd   0                  ; y
         dd   0                  ; cx (width)
         dd   0                  ; cy (height)
         dd   0                  ; hwndParent
         dd   0                  ; hMenu
         dd   0                  ; hInstance
         dd   0                  ; lpCreateParams

btnclsname  db 'button',0        ; letter case doesn't matter
;
; option/button table
;
        align 4

sysmenu dd WS_SYSMENU,sysmenu_str,10,10,250,15,IDCTL_SYSMENU
sizebox dd WS_THICKFRAME,sizebox_str,10,25,250,15,IDCTL_SIZEBOX
minbox  dd WS_MINIMIZEBOX,minbox_str,10,40,250,15,IDCTL_MINBOX
maxbox  dd WS_MAXIMIZEBOX,maxbox_str,10,55,250,15,IDCTL_MAXBOX
;
; button texts
;
sysmenu_str db 'WS_SYSMENU',0
sizebox_str db 'WS_THICKFRAME/WM_SIZEBOX',0
minbox_str  db 'WS_MINIMIZEBOX',0
maxbox_str  db 'WS_MAXIMIZEBOX',0
There is only one window procedure defined by us, and that is the one for the main window, the one with the check boxes in it. We start with the message dispatch, and the "close application" command.
        extrn   DefWindowProc:near

        .code
WndProc:
        mov     eax,[esp+4+4]           ; message ID
        cmp     eax,WM_COMMAND          ; controls clicked
        je      on_command
        cmp     eax,WM_CREATE           ; window created
        je      on_create
        cmp     eax,WM_DESTROY          ; about to start window destruction
        je      on_destroy
        jmp     DefWindowProc           ; delegate other message processing


        extrn   PostQuitMessage:near

on_destroy:
        push    large 0
        call    PostQuitMessage

        xor     eax,eax
        ret     16
We create the check boxes here, as a response to WM_CREATE. At this time, the main window has been created, and there is a window handle for it. The handle is used to assign the window as parent to each check box, which are created as a child window. A child window must be assigned a parent when it is created.
    We also register the test window class here, and create the test window.
;
; the main window
;
        .data
        align   4

mainwnd dd 0

        .code
on_create:
        mov     eax,[esp+4+0]  ; hwnd
        mov     [mainwnd],eax
        mov     eax,[wc].wc_hInstance
        mov     [wc2].wc_hInstance,eax
        push    offset wc2
        call    RegisterClass

        push    esi
        push    edi

        mov     esi,offset test
        mov     eax,[esi+12]            ; style argument
        mov     [test_style],eax        ; save original style
        call    install_subwindow
        mov     [testwnd],eax


        mov     esi,offset chkbox
        mov     edi,offset sysmenu
        call    install_chkbox

        mov     esi,offset chkbox
        mov     edi,offset sizebox
        call    install_chkbox

        mov     esi,offset chkbox
        mov     edi,offset minbox
        call    install_chkbox

        mov     esi,offset chkbox
        mov     edi,offset maxbox
        call    install_chkbox

        pop     edi
        pop     esi

        xor     eax,eax    ; signal a successful CREATE
        ret     16
;
; Create chkbox
;
;       ESI = address of CreateWindowEx arguments
;       EDI = chkbox table entry
;
; Returns:
;
;       EAX = BUTTON handle
;
install_chkbox:
        mov     eax,4[edi]
        mov     [chkbox+8],eax    ; lpszName, set window name = window text
        mov     eax,8[edi]
        mov     [chkbox+16],eax   ; x
        mov     eax,12[edi]
        mov     [chkbox+20],eax   ; y
        mov     eax,16[edi]
        mov     [chkbox+24],eax   ; cx
        mov     eax,20[edi]
        mov     [chkbox+28],eax   ; cy
        mov     eax,24[edi]
        mov     [chkbox+36],eax   ; hMenu, ctl ID when WS_CHILD
        call    install_subwindow
        ret
;
; Create main window's subwindows
;
;       ESI = address of CreateWindowEx arguments
;
; Returns:
;
;       EAX = handle of subwindow
;
install_subwindow:
        mov     eax,[mainwnd]
        mov     [esi+32],eax            ; hwndParent, make "mainbox" owner or parent
                                        ;   of new window
        mov     eax,[wc].wc_hInstance   ; get instance
        mov     [esi+40],eax            ; set hInstance of window class
        sub     esp,48    ; allocate args
        mov     edi,esp
        mov     ecx,12
        rep movsd
        call    CreateWindowEx
        ret
This is where most of the work happens, in response to WM_COMMAND. If the command is not a button (check box) command, we quit. Otherwise we fetch the style option. Because the option is encoded in a single bit, the option doubles as a bit mask.
        extrn   SendMessage:near
        extrn   SetWindowLong:near
        extrn   RedrawWindow:near

on_command:
        mov     eax,[esp+4+8]   ; wParam
        mov     edx,[esp+4+12]  ; lParam
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_SYSMENU
        je      select_sysmenu
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_SIZEBOX
        je      select_sizebox
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_MINBOX
        je      select_minbox
        cmp     eax,(BN_CLICKED shl 16)+IDCTL_MAXBOX
        je      select_maxbox
        jmp     exit_on_command
;
; Get styles
;
select_sysmenu:
        mov     ecx,[sysmenu]
        jmp     toggle
select_sizebox:
        mov     ecx,[sizebox]
        jmp     toggle
select_minbox:
        mov     ecx,[minbox]
        jmp     toggle
select_maxbox:
        mov     ecx,[maxbox]
        jmp     toggle
Now we get and reset the check box state. Here we see that messages are the main means of transferring information to and from the controls.
toggle:
        push    ecx             ; save style

        push    edx             ; save ctl handle

        push    large 0
        push    large 0
        push    large BM_GETCHECK
        push    edx             ; ctl handle from lParam
        call    SendMessage

        pop     edx             ; retrieve ctl handle

        xor     eax,1           ; toggle check state

        push    large 0
        push    eax             ; new check state
        push    large BM_SETCHECK
        push    edx             ; ctl handle
        call    SendMessage

        pop     ecx             ; retrieve style
Then we change the window style of our test window.
        xor     [test_style],ecx        ; toggle style bit
        push    [test_style]            ; new style
        push    large GWL_STYLE
        push    [testwnd]               ; test window
        call    SetWindowLong
Changing the window style does not cause a redrawing of the window. Without the following, the test window doesn't change until it is selected. And the redraw is imperfect.
    So we add this API call to force a redrawing of the test window without selecting it. The title bar is part of the "frame".
        push    large (RDW_FRAME+RDW_INVALIDATE+RDW_UPDATENOW)
        push    large 0         ; update rectangle
        push    large 0         ; update region (overrides rect)
        push    [testwnd]       ; test window
        call    RedrawWindow

exit_on_command:
        xor     eax,eax    ; signal WM_COMMAND was processed
        ret     16

        end     _start