Mosaic: Creating the main frame

Now we will create the main frame of our program:

Add two prototypes after the includes:

WinMain     PROTO STDCALL   :DWORD, :DWORD, :DWORD, :DWORD
WndProc     PROTO STDCALL   :DWORD, :DWORD, :DWORD, :DWORD

These prototypes will tell masm how many parameters these functions take (4 DWORD's in this case)

Add these to the .data and .data? sections of your program:

.data
    AppName        db      "Mosaic",0
    ClassName      db      "Mosaic32",0

.data?
    hInstance      dd      ?

AppName and ClassName are strings used for the main window, hInstance is a DWORD that will hold the application instance handle.

Start of code:

.code
    start:
    ; Get module handle and save it
    invoke  GetModuleHandle, NULL
    mov     hInstance, eax

    ; Init Common Controls library
    invoke  InitCommonControls

    ; Run winmain procedure and exit program
    invoke  WinMain, hInstance, NULL, NULL, SW_SHOWNORMAL
    invoke  ExitProcess,eax

GetModuleHandle retreives the module handle, which is stored in hInstance. InitCommonControls is an initialization function for the common control library. This library contains several controls that we will use in our program, and it should be initialized by calling InitCommonControls. After that, WinMain is called (a function we will create soon). WinMain will return when the user quits the program, whereafter ExitProcess is called to quit the process.

The WinMain procedure:

;==================================================================
; WinMain
;==================================================================
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
    mov     wc.cbSize,SIZEOF WNDCLASSEX
    mov     wc.style, CS_HREDRAW or CS_VREDRAW
    mov     wc.lpfnWndProc, OFFSET WndProc
    mov     wc.cbClsExtra,NULL
    mov     wc.cbWndExtra,NULL
    push    hInstance
    pop     wc.hInstance
    mov     wc.hbrBackground,COLOR_WINDOW
    mov     wc.lpszMenuName, NULL
    mov     wc.lpszClassName,OFFSET ClassName

Here our icons are loaded from the resource file and their handles are stored in wc.hIcon and wc.hIconSm. Windows will use these handles to display the icons in the main window:

    invoke  LoadIcon, hInstance, ICON1_BIG
    mov     wc.hIcon, eax
    invoke  LoadIcon, hInstance, ICON2_SMALL
    mov     wc.hIconSm, eax
    invoke  LoadCursor,NULL,IDC_ARROW
    mov     wc.hCursor,eax
    invoke  RegisterClassEx, addr wc
    INVOKE  CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
            WS_OVERLAPPEDWINDOW-WS_MAXIMIZEBOX-WS_SIZEBOX,\
            CW_USEDEFAULT, CW_USEDEFAULT,258,350,NULL,NULL,\
            hInst,NULL
    mov     hwnd,eax
    invoke  ShowWindow, hwnd, CmdShow
    invoke  UpdateWindow, hwnd
    .WHILE  TRUE
        invoke  GetMessage, ADDR msg,NULL,0,0
        .BREAK  .IF (!eax)
        invoke  TranslateMessage, ADDR msg
        invoke  DispatchMessage, ADDR msg
    .ENDW
    mov     eax,msg.wParam
    ret
WinMain endp

The WinMain functions initializes a WNDCLASSEX structure that includes parameters of the windows class, then RegisterClassEx is called to register the window class named 'Mosaic32'. CreateWindowEx is called to create a new window based on the new class, ShowWindow and UpdateWindow will make the window visible. Then the program goes in an infinite loop until the user closes the window. This loop is called the message pump (see my tutorials for more info).

The window style is WS_OVERLAPPEDWINDOW without a Maximize-button and without a sizebox (=not resizeable). The window is 258x350 pixels.

The window also needs a window procedure that handles all the messages sent to the window. In our program this is the function WndProc, which takes 4 parameters: hWnd contains the window handle, uMsg the message, wParam and lParam the message parameters.

;==================================================================
;   Window procedure
;==================================================================
WndProc proc    hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
mov eax, uMsg
    .IF     eax==WM_CREATE
        ; yet to do
    .ELSEIF eax==WM_DESTROY
        invoke  PostQuitMessage, NULL
    .ELSE
        invoke  DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
ret
WndProc endp

The window procedure processes the WM_DESTROY message to quit when the user quits.

After you've done all this, your files should look like this: mosaic1.zip

Now run make.bat to assemble & link your program. You should have no errors (you might get a warning about gdi32 but never mind about that) and you should be able to run the program. It has a small icon in the upper left corner, the big icon is visible in the task list (ALT+TAB) and your mosaic.exe has the big icon as icon.

Blank mosaic window