Assembly language examples using NASM on Windows

Basic Window Extended - 32 bit




Basic Window Extended 32

Description
Creates a window with simple functionality, 32 bit.
It demonstrates the processing of several WM_ messages, and improves upon the basic window example.

Assemble

Link

Notes
Double click the icon to run the executable.

Improvements over the basic window example

Optimisations
When a WM_ message has been processed, Windows usually expects a return value of 0. There now is a single point of return where
EAX is zeroed and the stack frame removed.
WM_CTLCOLOREDIT and WM_CTLCOLORSTATIC need a brush returned instead, in these cases the zeroing of EAX is skipped over.

Code

                                                ; Basic Window Extended, 32 bit. V1.04
ANSI_CHARSET         EQU 0                      ; Constants
BLACKNESS            EQU 42h
CLIP_DEFAULT_PRECIS  EQU 0
CS_BYTEALIGNWINDOW   EQU 2000h
CS_HREDRAW           EQU 2
CS_VREDRAW           EQU 1
DEFAULT_PITCH        EQU 0
ES_AUTOHSCROLL       EQU 80h
ES_CENTER            EQU 1
FALSE                EQU 0
GRAY_BRUSH           EQU 2
IDC_ARROW            EQU 7F00h
IDI_APPLICATION      EQU 7F00h
IDNO                 EQU 7
IMAGE_CURSOR         EQU 2
IMAGE_ICON           EQU 1
LR_SHARED            EQU 8000h
MB_DEFBUTTON2        EQU 100h
MB_YESNO             EQU 4
NULL                 EQU 0
NULL_BRUSH           EQU 5
OPAQUE               EQU 2
PROOF_QUALITY        EQU 2
SM_CXFULLSCREEN      EQU 10h
SM_CYFULLSCREEN      EQU 11h
SS_CENTER            EQU 1
SS_NOTIFY            EQU 100h
SW_SHOWNORMAL        EQU 1
TRUE                 EQU 1
WM_CLOSE             EQU 10h
WM_COMMAND           EQU 111h
WM_CREATE            EQU 1
WM_CTLCOLOREDIT      EQU 133h
WM_CTLCOLORSTATIC    EQU 138h
WM_DESTROY           EQU 2
WM_PAINT             EQU 0Fh
WM_SETFONT           EQU 30h
OUT_DEFAULT_PRECIS   EQU 0
WS_CHILD             EQU 40000000h
WS_EX_COMPOSITED     EQU 2000000h
WS_OVERLAPPEDWINDOW  EQU 0CF0000h
WS_TABSTOP           EQU 10000h
WS_VISIBLE           EQU 10000000h

WindowWidth          EQU 640
WindowHeight         EQU 170
Static1ID            EQU 100
Static2ID            EQU 101
Edit1ID              EQU 102
Edit2ID              EQU 103

extern _AdjustWindowRectEx@16                   ; Import external symbols
extern _BeginPaint@8                            ; Windows API functions, decorated
extern _BitBlt@36
extern _CreateFontA@56
extern _CreateSolidBrush@4
extern _CreateWindowExA@48
extern _DefWindowProcA@16
extern _DeleteObject@4
extern _DestroyWindow@4
extern _DispatchMessageA@4
extern _EndPaint@8
extern _ExitProcess@4
extern _GetDlgCtrlID@4
extern _GetMessageA@16
extern _GetModuleHandleA@4
extern _GetStockObject@4
extern _GetSystemMetrics@4
extern _InvalidateRect@12
extern _IsDialogMessageA@8
extern _LoadImageA@24
extern _MessageBoxA@16
extern _PostQuitMessage@4
extern _RegisterClassExA@4
extern _SendMessageA@16
extern _SetBkColor@8
extern _SetBkMode@8
extern _SetTextColor@8
extern _ShowWindow@8
extern _TranslateMessage@4
extern _UpdateWindow@4

global Start                                    ; Export symbols. The entry point

section .data                                   ; Initialized data segment
 Static1Colour    dd 0F0F0F0h                   ; Colour (0BBGGRRh)
 Static1ColourA   dd 020A0F0h
 Static2Colour    dd 000FFFFh
 Static2ColourA   dd 08000FFh
 Edit1TextColour  dd 0F590F5h
 Edit1BackColour  dd 0A00000h
 Edit2TextColour  dd 0A56E3Bh
 Edit2BackColour  dd 0FEFE8Eh
 BackgroundColour dd 0A56E3Bh   
 WindowName       db "Basic Window Extended 32", 0
 ClassName        db "Window", 0
 SegoeUI          db "Segoe UI", 0
 StaticClass      db "STATIC", 0
 EditClass        db "EDIT", 0
 Text1            db "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", 0
 Text2            db "abcdefghijklmnopqrstuvwxyz_0123456789", 0
 ExitText         db "Do you want to exit?", 0

section .bss                                    ; Uninitialized data segment
 hInstance        resd 1
 BackgroundBrush  resd 1
 Font             resd 1
 Static1          resd 1
 Static2          resd 1
 Edit1            resd 1
 Edit2            resd 1

section .text                                   ; Code segment
Start:
 push  NULL
 call  _GetModuleHandleA@4
 mov   dword [hInstance], EAX

 call  WinMain

.Exit:
 push  NULL
 call  _ExitProcess@4

WinMain:
 push  EBP                                      ; Set up a stack frame
 mov   EBP, ESP
 sub   ESP, 104                                 ; 104 bytes for local variables

%define Screen.Width       EBP - 104
%define Screen.Height      EBP - 100

%define ClientArea         EBP - 96             ; RECT structure. 16 bytes
%define ClientArea.left    EBP - 96
%define ClientArea.top     EBP - 92
%define ClientArea.right   EBP - 88
%define ClientArea.bottom  EBP - 84

%define wc                 EBP - 80             ; WNDCLASSEX structure. 48 bytes
%define wc.cbSize          EBP - 80
%define wc.style           EBP - 76
%define wc.lpfnWndProc     EBP - 72
%define wc.cbClsExtra      EBP - 68
%define wc.cbWndExtra      EBP - 64
%define wc.hInstance       EBP - 60
%define wc.hIcon           EBP - 56
%define wc.hCursor         EBP - 52
%define wc.hbrBackground   EBP - 48
%define wc.lpszMenuName    EBP - 44
%define wc.lpszClassName   EBP - 40
%define wc.hIconSm         EBP - 36

%define msg                EBP - 32             ; MSG structure. 28 bytes
%define msg.hwnd           EBP - 32             ; Breaking out each member is not necessary
%define msg.message        EBP - 28             ; in this case, but it shows where each
%define msg.wParam         EBP - 24             ; member is on the stack
%define msg.lParam         EBP - 20
%define msg.time           EBP - 16
%define msg.pt.x           EBP - 12
%define msg.pt.y           EBP - 8

%define hWnd               EBP - 4

 push  dword [BackgroundColour]
 call  _CreateSolidBrush@4                      ; Create a brush for the window backgound
 mov   dword [BackgroundBrush], EAX

 mov   dword [wc.cbSize], 48                    ; [EBP - 80]
 mov   dword [wc.style], CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW  ; [EBP - 76]
 mov   dword [wc.lpfnWndProc], WndProc          ; [EBP - 72]
 mov   dword [wc.cbClsExtra], NULL              ; [EBP - 68]
 mov   dword [wc.cbWndExtra], NULL              ; [EBP - 64]
 mov   EAX, dword [hInstance]
 mov   dword [wc.hInstance], EAX                ; [EBP - 60]

 push  LR_SHARED
 push  NULL
 push  NULL
 push  IMAGE_ICON
 push  IDI_APPLICATION
 push  NULL
 call  _LoadImageA@24                           ; Large program icon
 mov   dword [wc.hIcon], EAX                    ; [EBP - 56]

 push  LR_SHARED
 push  NULL
 push  NULL
 push  IMAGE_CURSOR
 push  IDC_ARROW
 push  NULL
 call  _LoadImageA@24                           ; Cursor
 mov   dword [wc.hCursor], EAX                  ; [EBP - 52]

 mov   EAX, dword [BackgroundBrush]
 mov   dword [wc.hbrBackground], EAX            ; [EBP - 48]
 mov   dword [wc.lpszMenuName], NULL            ; [EBP - 44]
 mov   dword [wc.lpszClassName], ClassName      ; [EBP - 40]

 push  LR_SHARED
 push  NULL
 push  NULL
 push  IMAGE_ICON
 push  IDI_APPLICATION
 push  NULL
 call  _LoadImageA@24                           ; Small program icon
 mov   dword [wc.hIconSm], EAX                  ; [EBP - 36]

 lea   EAX, [wc]                                ; [EBP - 80]
 push  EAX
 call  _RegisterClassExA@4

 push  SM_CXFULLSCREEN
 call  _GetSystemMetrics@4                      ; Get the current screen width
 mov   dword [Screen.Width], EAX                ; [EBP - 104]

 push  SM_CYFULLSCREEN
 call  _GetSystemMetrics@4                      ; Get the current screen height
 mov   dword [Screen.Height], EAX               ; [EBP - 100]

 mov   dword [ClientArea.left], 0               ; [EBP - 96]
 mov   dword [ClientArea.top], 0                ; [EBP - 92]
 mov   dword [ClientArea.right], WindowWidth    ; [EBP - 88]
 mov   dword [ClientArea.bottom], WindowHeight  ; [EBP - 84]

 push  WS_EX_COMPOSITED                         ; Extended style
 push  NULL
 push  WS_OVERLAPPEDWINDOW                      ; Style
 lea   EAX, [ClientArea]                        ; [EBP - 96]
 push  EAX
 call  _AdjustWindowRectEx@16                   ; Get window size for the desired client size
                                                ; Size is returned in ClientArea
 mov   EAX, dword [ClientArea.top]              ; [EBP - 92]
 sub   dword [ClientArea.bottom], EAX           ; New Height = ClientArea.bottom - ClientArea.top

 mov   EAX, dword [ClientArea.left]             ; [EBP - 96]
 sub   dword [ClientArea.right], EAX            ; New Width = ClientArea.right - ClientArea.left

 push  NULL
 push  dword [hInstance]
 push  NULL
 push  NULL

 push  dword [ClientArea.bottom]                ; Height. [EBP - 84]
 push  dword [ClientArea.right]                 ; Width. [EBP - 88]

 xor   ECX, ECX
 mov   EAX, dword [Screen.Height]               ; [EBP - 100]
 sub   EAX, dword [ClientArea.bottom]           ; Corrected window height. [EBP - 84]
 cmovs EAX, ECX                                 ; Clamp to 0 (top) if negative
 shr   EAX, 1                                   ; EAX = (Screen.Height - window height) / 2
 push  EAX                                      ; Y position, now centred

 mov   EAX, dword [Screen.Width]                ; [EBP - 104]
 sub   EAX, dword [ClientArea.right]            ; Corrected window width. [EBP - 88]
 cmovs EAX, ECX                                 ; Clamp to 0 (left) if negative
 shr   EAX, 1                                   ; EAX = (Screen.Width - window width) / 2
 push  EAX                                      ; X position, now centred

 push  WS_OVERLAPPEDWINDOW
 push  WindowName
 push  ClassName
 push  WS_EX_COMPOSITED
 call  _CreateWindowExA@48
 mov   dword [hWnd], EAX                        ; [EBP - 4]

 push  SW_SHOWNORMAL
 push  dword [hWnd]                             ; [EBP - 4]
 call  _ShowWindow@8

 push  dword [hWnd]                             ; [EBP - 4]
 call  _UpdateWindow@4

.MessageLoop:
 push  NULL
 push  NULL
 push  NULL
 lea   EAX, [msg]                               ; [EBP - 32]
 push  EAX
 call  _GetMessageA@16
 cmp   EAX, 0
 je    .Done

 lea   EAX, [msg]                               ; [EBP - 32]
 push  EAX
 push  dword [hWnd]                             ; [EBP - 4]
 call  _IsDialogMessageA@8                      ; For keyboard strokes
 cmp   EAX, 0
 jne   .MessageLoop                             ; Skip TranslateMessage and DispatchMessage

 lea   EAX, [msg]                               ; [EBP - 32]
 push  EAX
 call  _TranslateMessage@4

 lea   EAX, [msg]                               ; [EBP - 32]
 push  EAX
 call  _DispatchMessageA@4
 jmp   .MessageLoop

.Done:
 xor   EAX, EAX
 mov   ESP, EBP                                 ; Remove the stack frame
 pop   EBP
 ret

WndProc:
 push  EBP                                      ; Set up a stack frame
 mov   EBP, ESP
 sub   ESP, 68                                  ; 68 bytes for local variables

%define hWnd                EBP + 8             ; Location of the 4 passed parameters from
%define uMsg                EBP + 12            ; the calling function
%define wParam              EBP + 16            ; We can now access these parameters by name
%define lParam              EBP + 20

%define ps                  EBP - 68            ; PAINTSTRUCT structure. 64 bytes
%define ps.hdc              EBP - 68
%define ps.fErase           EBP - 64
%define ps.rcPaint.left     EBP - 60
%define ps.rcPaint.top      EBP - 56
%define ps.rcPaint.right    EBP - 52
%define ps.rcPaint.bottom   EBP - 48
%define ps.Restore          EBP - 44
%define ps.fIncUpdate       EBP - 40
%define ps.rgbReserved      EBP - 36

%define hdc                 EBP - 4

 cmp   dword [uMsg], WM_CLOSE                   ; [EBP + 12]
 je    WMCLOSE

 cmp   dword [uMsg], WM_COMMAND                 ; [EBP + 12]
 je    WMCOMMAND

 cmp   dword [uMsg], WM_CREATE                  ; [EBP + 12]
 je    WMCREATE

 cmp   dword [uMsg], WM_CTLCOLOREDIT            ; [EBP + 12]
 je    WMCTLCOLOREDIT

 cmp   dword [uMsg], WM_CTLCOLORSTATIC          ; [EBP + 12]
 je    WMCTLCOLORSTATIC

 cmp   dword [uMsg], WM_DESTROY                 ; [EBP + 12]
 je    WMDESTROY

 cmp   dword [uMsg], WM_PAINT                   ; [EBP + 12]
 je    WMPAINT

DefaultMessage:
 push  dword [lParam]                           ; [EBP + 20]
 push  dword [wParam]                           ; [EBP + 16]
 push  dword [uMsg]                             ; [EBP + 12]
 push  dword [hWnd]                             ; [EBP + 8]
 call  _DefWindowProcA@16
 jmp   Return

WMCLOSE:
 push  MB_YESNO | MB_DEFBUTTON2
 push  WindowName
 push  ExitText
 push  dword [hWnd]                             ; [EBP + 8]
 call  _MessageBoxA@16

 cmp   EAX, IDNO
 je    Return.WM_Processed

 push  dword [hWnd]                             ; [EBP + 8]
 call  _DestroyWindow@4                         ; Send a WM_DESTROY message
 jmp   Return.WM_Processed

WMCOMMAND:
 mov   EAX, dword [wParam]                      ; EAX = ID. [EBP + 16]

 cmp   AX, Static1ID
 je    .Static1

 cmp   AX, Static2ID
 je    .Static2

 jmp   Return.WM_Processed

.Static1:
 mov   EAX, dword [Static1Colour]
 mov   EBX, dword [Static1ColourA]
 mov   dword [Static1Colour], EBX
 mov   dword [Static1ColourA], EAX              ; Swap colours

 push  TRUE
 push  NULL
 push  dword [lParam]                           ; Static1 handle. [EBP + 20]
 call  _InvalidateRect@12                       ; Redraw control
 jmp   Return.WM_Processed

.Static2:
 mov   EAX, dword [Static2Colour]
 mov   EBX, dword [Static2ColourA]
 mov   dword [Static2Colour], EBX
 mov   dword [Static2ColourA], EAX              ; Swap colours

 push  TRUE
 push  NULL
 push  dword [lParam]                           ; Static2 handle. [EBP + 20]
 call  _InvalidateRect@12                       ; Redraw control
 jmp   Return.WM_Processed

WMCREATE:
 push  NULL
 push  dword [hInstance]
 push  Static1ID
 push  dword [hWnd]                             ; [EBP + 8]
 push  20                                       ; Height
 push  400                                      ; Width
 push  10                                       ; Y
 push  120                                      ; X
 push  WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
 push  Text1                                    ; Default text
 push  StaticClass
 push  NULL
 call  _CreateWindowExA@48
 mov   dword [Static1], EAX

 push  NULL
 push  dword [hInstance]
 push  Static2ID
 push  dword [hWnd]                             ; [EBP + 8]
 push  20                                       ; Height
 push  400                                      ; Width
 push  40                                       ; Y
 push  120                                      ; X
 push  WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTER
 push  Text2                                    ; Default text
 push  StaticClass
 push  NULL
 call  _CreateWindowExA@48
 mov   dword [Static2], EAX

 push  NULL
 push  dword [hInstance]
 push  Edit1ID
 push  dword [hWnd]                             ; [EBP + 8]
 push  20                                       ; Height
 push  400                                      ; Width
 push  70                                       ; Y
 push  120                                      ; X
 push  WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
 push  Text1                                    ; Default text
 push  EditClass
 push  NULL
 call  _CreateWindowExA@48
 mov   dword [Edit1], EAX

 push  NULL
 push  dword [hInstance]
 push  Edit2ID
 push  dword [hWnd]                             ; [EBP + 8]
 push  20                                       ; Height
 push  400                                      ; Width
 push  100                                      ; Y
 push  120                                      ; X
 push  WS_CHILD | WS_VISIBLE | ES_CENTER | WS_TABSTOP | ES_AUTOHSCROLL
 push  Text2                                    ; Default text
 push  EditClass
 push  NULL
 call  _CreateWindowExA@48
 mov   dword [Edit2], EAX

 lea   EAX, [SegoeUI]
 push  EAX
 push  DEFAULT_PITCH
 push  PROOF_QUALITY
 push  CLIP_DEFAULT_PRECIS
 push  OUT_DEFAULT_PRECIS
 push  ANSI_CHARSET
 push  NULL
 push  NULL
 push  NULL
 push  400                                      ; Weight
 push  NULL
 push  NULL
 push  NULL
 push  20                                       ; Size
 call  _CreateFontA@56
 mov   dword [Font], EAX

 push  FALSE
 push  dword [Font]
 push  WM_SETFONT
 push  dword [Static1]
 call  _SendMessageA@16                         ; Set Static1 font

 push  FALSE
 push  dword [Font]
 push  WM_SETFONT
 push  dword [Static2]
 call  _SendMessageA@16                         ; Set Static2 font

 push  FALSE
 push  dword [Font]
 push  WM_SETFONT
 push  dword [Edit1]
 call  _SendMessageA@16                         ; Set Edit1 font

 push  FALSE
 push  dword [Font]
 push  WM_SETFONT
 push  dword [Edit2]
 call  _SendMessageA@16                         ; Set Edit2 font

 jmp   Return.WM_Processed

WMCTLCOLOREDIT:                                 ; For colouring edit controls
 push  dword [lParam]                           ; [EBP + 20]
 call  _GetDlgCtrlID@4                          ; EAX = ID

 cmp   EAX, Edit1ID
 je    .Edit1

 cmp   EAX, Edit2ID
 je    .Edit2

.Default:
 push  NULL_BRUSH
 call  _GetStockObject@4                        ; Return a brush
 jmp   Return

.Edit1:
 push  dword [Edit1TextColour]
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetTextColor@8

 push  OPAQUE
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkMode@8

 push  dword [Edit1BackColour]
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkColor@8

 push  NULL_BRUSH
 call  _GetStockObject@4                        ; Return a brush
 jmp   Return

.Edit2:
 push  dword [Edit2TextColour]
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetTextColor@8

 push  OPAQUE
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkMode@8

 push  dword [Edit2BackColour]
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkColor@8

 push  NULL_BRUSH
 call  _GetStockObject@4                        ; Return a brush
 jmp   Return

WMCTLCOLORSTATIC:                               ; For colouring static controls
 push  dword [lParam]                           ; [EBP + 20]
 call  _GetDlgCtrlID@4                          ; EAX = ID

 cmp   EAX, Static1ID
 je    .Static1

 cmp   EAX, Static2ID
 je    .Static2

.Default:
 push  NULL_BRUSH
 call  _GetStockObject@4                        ; Return a brush
 jmp   Return

.Static1:
 push  dword [Static1Colour]
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetTextColor@8

 push  OPAQUE
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkMode@8

 push  0604060h
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkColor@8

 push  NULL_BRUSH
 call  _GetStockObject@4                        ; Return a brush
 jmp   Return

.Static2:
 push  dword [Static2Colour]
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetTextColor@8

 push  OPAQUE
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkMode@8

 push  0005000h
 push  dword [wParam]                           ; [EBP + 16]
 call  _SetBkColor@8

 push  GRAY_BRUSH
 call  _GetStockObject@4                        ; Return a brush
 jmp   Return

WMDESTROY:
 push  dword [BackgroundBrush]
 call  _DeleteObject@4

 push  dword [Font]
 call  _DeleteObject@4

 push  NULL
 call  _PostQuitMessage@4
 jmp   Return.WM_Processed

WMPAINT:
 lea   EAX, [ps]                                ; Starting address of PAINTSTRUCT. [EBP - 68]
 push  EAX
 push  dword [hWnd]                             ; [EBP + 8]
 call  _BeginPaint@8
 mov   dword [hdc], EAX

 push  BLACKNESS                                ; Operation
 push  0                                        ; Source Y
 push  0                                        ; Source X
 push  dword [hdc]                              ; Source device context
 push  20                                       ; Height
 push  400                                      ; Width
 push  130                                      ; Destination Y
 push  120                                      ; Destination X
 push  dword [hdc]                              ; Destination device context
 call  _BitBlt@36                               ; Blit a black rectangle

 lea   EAX, [ps]                                ; [EBP - 68]
 push  EAX
 push  dword [hWnd]                             ; [EBP + 8]
 call  _EndPaint@8

Return.WM_Processed:
 xor   EAX, EAX                                 ; WM_ has been processed, return 0

Return:
 mov   ESP, EBP                                 ; Remove the stack frame
 pop   EBP
 ret   16                                       ; Pop 4 parameters off the stack and return