A Basic GUI Program--WINDOW02.ASM

This program basically produces a Windows 95 style application window. The application gets a Taskbar button. You can change the window's size, and all the usual buttons appear and are functional. Just like our minimal GUI program, you can move the window around. The only things missing are scroll bars and a menu bar. Back to Win95 ASM Page

Organizing the API

The API function names are determined by the linker import libraries, but the names of structures and constants are not. We also don't want to create an EXTRN for every function because your assembler might not optimize the .OBJ file (by eliminating unused EXTRN's.)
    We can manage this by creating two kinds of .INC files: one for constants, structures, and API entry names, usable by any program; and one for link names, unique to each import library set. If the link name differs from its "generic" or "SDK" name, the generic name is text equated. All our other code will declare EXTRN's as needed. Note that with the Borland linker, the link name and the entry name are one and the same, so the second .INC file is unnecessary.
    The supplied version of win32.inc requires link names to be equated before it is included.
    ; choose the following, if necessary:
    include    vclib.inc    ; Microsoft VC++ .lib link names

    include    win32.inc    ; constants, structures, and entry names

Organizing the program

Many early Windows programmers repeatedly wrote or cut-and-pasted the WinMain "create-and-message-loop" code. We are going to take most of it out and place it in a module called winmain.asm. This will allow us to improve this section of code without having to reassemble everything else. The minimum necessary for the Basic GUI Program is in module wmain02.asm.
    Every GUI app we build with the winmain module starts in this module. It first calls InitApp, which initializes the program and returns two pointers: a WNDCLASSEX pointer in EDI, and a CREATEARGS pointer in ESI. These are used to register and create the main application window. These registers are used because they are preserved by the Win32 API, and, in the case of ESI, makes the CREATEARGS data immediately available for a block copy.
    After creating the window, the main message loop is entered, and when it exits, the winmain module calls EndApp for cleanup. EndApp returns an error code in EAX for ExitProcess.
    extrn   InitApp:near,RegisterClassEx:near,CreateWindowEx:near
    public _start

    .code
_start:
    call    InitApp         ; call custom app initialization

    push    edi
    call    RegisterClassEx

    sub     esp,48    ; allocate argument list
    mov     edi,esp   ; set block move destination
    mov     ecx,12    ; number of arguments
    rep movsd
    call    CreateWindowEx

    ; ... message loop ...

    extrn   ExitProcess:near,EndApp:near

    .code
    call    EndApp  ; call custom app cleanup

    push    eax     ; (error) return code
    call    ExitProcess
CREATEARGS holds the CreateWindowEx parameters. This allows us to use a block copy, avoiding a bunch of PUSH instructions.
    The C/C++ include file windows.h does not define this structure. Instead, it defines CREATESTRUCT (used by the WM_CREATE message), which holds the CreateWindowEx parameters in reverse order--a holdover from Win16.

The application module

This is where we define InitApp, EndApp, and our window procedure.
    public  InitApp,EndApp
    extrn   GetModuleHandle:near

    .code
InitApp:
    mov     edi,offset wc
    mov     esi,offset cwargs

    push    large 0         ; NULL string pointer means
    call    GetModuleHandle ; get HINSTANCE/HMODULE of EXE file
    mov     [edi].wcxInstance,eax
    mov     [esi].cwargInstance,eax

    ; ...initialization code...

    ret

EndApp:
    xor     eax,eax ; assume no errors
    ret

The fields of the basic window class

wcxSize = size WNDCLASSEX:
    This field is set to the size of the WNDCLASSEX structure. This allows for future expansion by Microsoft.

wcxStyle = CS_VREDRAW + CS_HREDRAW + CS_DBLCLICKS:
    The field holds a combination of style codes. With the appropriate styles, we make our default window request repaints when it resized, and we allow it to detect double-clicks.

wcxWndProc = WndProc:
    We set this to point to our main window procedure. It's the same as the WndProc in our minimal GUI program.

wcxClsExtra = 0, wcxWndExtra = 0:
    We don't need to add any auxiliary information, so we request no extra bytes.

wcxInstance = GetModuleHandle(0):
    This field determines which module owns the window class. We set this to the handle of the EXE module.

wcxIcon = LoadIcon(NULL, IDI_WINLOGO):
    Although Win95 displays this by default, we put this in so we can choose another default.

wcxCursor = LoadCursor(NULL, IDC_ARROW):
    Without this, the cursor display doesn't always show properly when transiting our default window.

wcxBkgndBrush = COLOR_WINDOW + 1:
    Without this, the client area will start with a portion of the screen display. COLOR_WINDOW is a system color, and it will be affected by the Screen Display properties (right-click on the desktop, then choose Properties, then Appearance.)
    Because NULL means no color and there exists a system color with the ID value of 0, the value of a system color must be incremented by one before placing it in this field.

wcxMenuName = NULL:
    We still do without a menu.

wcxClassName = wndclsname:
    Address of the class name string in NUL-terminated format.

wcxSmallIcon = LoadIcon(NULL, IDI_WINLOGO):
    Use the same set of icons as wcxIcon.

    extrn   LoadIcon:near,LoadCursor:near

DEFAULT_CS_STYLE equ CS_VREDRAW + CS_HREDRAW + CS_DBLCLKS

    .data
wc  WNDCLASSEX <size WNDCLASSEX,DEFAULT_CS_STYLE,WndProc,0,0, 0, \
                0,0,COLOR_WINDOW+1, 0,wndclsname, 0>
wndclsname db 'winmain',0

    .code
    push    large IDI_WINLOGO
    push    large 0         ; hInstance, 0 = stock icon
    call    LoadIcon
    mov     [edi].wcxIcon,eax
    mov     [edi].wcxSmallIcon,eax

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

Creating the basic window

For our default window, we put text in the title bar, and make our window look like a typical Win95 window.
DEFAULT_STYLE   equ WS_VISIBLE + WS_OVERLAPPED + WS_CAPTION + WS_SYSMENU \
                    + WS_THICKFRAME + WS_MINIMIZEBOX + WS_MAXIMIZEBOX
DEFAULT_EXSTYLE equ WS_EX_WINDOWEDGE + WS_EX_CLIENTEDGE
DEFAULT_X       equ 100
DEFAULT_Y       equ 100
DEFAULT_WIDTH   equ 200
DEFAULT_HEIGHT  equ 200

    .data
    align    4
cwargs CREATEARGS <DEFAULT_EXSTYLE,wndclsname,def_title,DEFAULT_STYLE, \
            DEFAULT_X,DEFAULT_Y, DEFAULT_WIDTH,DEFAULT_HEIGHT, 0,0, 0, 0>
def_title db 'Application',0