Tutorial 3: A Simple Window

In this tutorial, we will build a Windows program that displays a fully functional window on the desktop.

Theory:

Windows programs rely heavily on API functions for their GUI. This approach benefits both users and programmers. For users, they don't have to learn how to navigate the GUI of each new programs, the GUI of Windows programs are alike. For programmers, the GUI codes are already there,tested, and ready for use. The downside for programmers is the increased complexity involved. In order to create or manipulate any GUI objects such as windows, menu or icons, programmers must follow a strict recipe. But that can be overcome by modular programming or OOP paradigm.
I'll outline the steps required to create a window on the desktop below:
  1. Get the instance handle of your program (required)
  2. Get the command line (not required unless your program wants to process a command line)
  3. Register window class (required ,unless you use predefined window types, eg. MessageBox or a dialog box)
  4. Create the window (required)
  5. Show the window on the desktop (required unless you don't want to show the window immediately)
  6. Refresh the client area of the window
  7. Enter an infinite loop, checking for messages from Windows
  8. If messages arrive, they are processed by a specialized function that is responsible for the window
  9. Quit program if the user closes the window
As you can see, the structure of a Windows program is rather complex compared to a DOS program. But the world of Windows is drastically different from the world of DOS. Windows programs must be able to coexist peacefully with each other. They must follow stricter rules. You, as a programmer, must also be more strict with your programming style and habit.

Content:

Below is the source code of our simple window program. Before jumping into the gory details of Win32 ASM programming, I'll point out some fine points which will ease your programming. [ClassName:  B§  'SimpleWinClass'  0            ; the name of our window class
  AppName:             'Our First Window'  0          ; the name of our window

; Uninitialized data:

[HINSTANCE: ?     ; Instance handle of our program
 CommandLine: ?]

; Here begins our code
Main:
    call 'Kernel32.GetModuleHandleA' &NULL   | mov D§hInstance eax
   call 'User32.LoadIconA'  0  &IDI_WINLOGO | mov D§wc_hIcon eax  D§wc_hIconSm eax
    call 'User32.LoadCursorA' 0  &IDC_ARROW  | mov D§wc_hCursor eax
    call 'User32.RegisterClassExA'  WindowClassEX
    call 'User32.CreateWindowExA' &NULL ClassName AppName,
                                 &WS_OVERLAPPEDWINDOW,
                                 &CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT,
                                 &NULL &NULL D§hInstance &NULL
      mov D§WindowHandle eax

    call 'User32.ShowWindow'  D§WindowHandle &SW_SHOWNORMAL
    call 'User32.UpdateWindow'  D§WindowHandle

L1: call 'User32.GetMessageA' FirstMessage 0 0 0 | cmp eax 0 | je L9>
        call 'User32.TranslateMessage'  FirstMessage
        call 'User32.DispatchMessageA'  FirstMessage
    jmp L1<

L9: call 'Kernel32.ExitProcess' &NULL
 

__________________________________________________________________________________________
 

Proc MainWindowProc:
    Arguments @Adressee, @Message, @wParam, @lParam

    pushad
        .If D@Message = &WM_DESTROY
            call 'User32.PostQuitMessage' &NULL
        .Else
            popad
            call 'User32.DefWindowProcA' D@Adressee D@Message D@wParam D@lParam
            Exit
        .End_If

        popad | mov eax &FALSE
EndP
 
 
 

Analysis:

You may be taken aback that a simple Windows program requires so much coding. But most of those codes are just *template* codes that you can copy from one source code file to another. You can write only the codes in WinMain function. In fact, this is what  C compilers do. They let you write WinMain codes without worrying about other housekeeping chores. The only catch is that you must have a function named WinMain else C compilers will not be able to combine your codes with the prologue and epilogue. You do not have such restriction with assembly language. SpAsm just requires a "Main :" Label to know where the code execution will start.

Prepare yourself. This's going to be a long, long tutorial. Let's analyze this program to death!
 

[ClassName:  B§  'SimpleWinClass'  0
 AppName:             'Our First Window'  0]

[hInstance: ?
 CommandLine: ?]

Next are the "DATA" sections.
In .DATA, we declare two zero-terminated strings(ASCIIZ strings): ClassName which is the name of our window class and AppName which is the name of our window. Note that the two variables are initialized.
In .DATA?, two variables are declared: hInstance (instance handle of our program) and CommandLine (command line of our program). The unfamiliar data types are DWORD. You can look them up in windows.inc. Note that all variables in .DATA? section are not initialized, that is, they don't have to hold any specific value on startup, but we want to reserve the space for future use.

.CODE contains all your instructions.  The name of the other label is unimportant. You can name it anything you like so long as it is unique and doesn't violate the naming convention of SpAsm.
Our first instruction is the call to GetModuleHandle to retrieve the instance handle of our program. Under Win32, instance handle and module handle are one and the same. You can think of instance handle as the ID of your program. It is used as parameter to several API functions our program must call, so it's generally a good idea to retrieve it at the beginning of our program.
Note: Actually under win32, instance handle is the linear address of your program in memory.
Upon returning from a Win32 function, the function's return value, if any, can be found in eax. All other values are returned through variables passed in the function parameter list you defined for the call.
A Win32 function that you call will nearly always preserve the segment registers and the ebx, edi, esi and ebp registers. Conversely, ecx and edx are considered scratch registers and are always undefined upon return from a Win32 function.
Note: Don't expect the values of eax, ecx, edx to be preserved across API function calls.
The bottom line is that: when calling an API function, expects return value in eax. If any of your function will be called by Windows, you must also play by the rule: preserve and restore the values of the segment registers, ebx, edi, esi and ebp upon function return else your program will crash very shortly, this includes your window procedure and windows callback functions.
The GetCommandLine call is unnecessary if your program doesn't process a command line.
Next is the WinMain call. Here it receives four parameters: the instance handle of our program, the instance handle of the previous instance of our program, the command line and window state at first appearance. Under Win32, there's NO previous instance. Each program is alone in its address space, so the value of hPrevInst is always 0. This is a leftover from the day of Win16 when all instances of a program run in the same address space and an instance wants to know if it's the first instance. Under win16, if hPrevInst is NULL, then this instance is the first one.
Note: You don't have to declare the function name as WinMain. In fact, you have complete freedom in this regard. You don't have to use any WinMain-equivalent function at all. You can paste the codes inside WinMain function next to GetCommandLine and your program will still be able to function perfectly.
 
 
 

    call 'User32.LoadIconA'  0  &IDI_WINLOGO | mov D§wc_hIcon eax  D§wc_hIconSm eax
    call 'User32.LoadCursorA' 0  &IDC_ARROW  | mov D§wc_hCursor eax
    call 'User32.RegisterClassExA'  WindowClassEX
 

The inimidating lines above are really simple in concept. It just takes several lines of instruction to accomplish. The concept behind all these lines is  window class. A window class is nothing more than a blueprint or specification of a window. It defines several important characteristics of a window such as its icon, its cursor, the function responsible for it, its color etc. You create a window from a window class. This is some sort of object oriented concept. If you want to create more than one window with the same characteristics, it stands to reason to store all these characteristics in only one place and refer to them when needed. This scheme will save lots of memory by avoiding duplication of information. Remember, Windows is designed in the past when memory chips are prohibitive and most computers have 1 MB of memory. Windows must be very efficient in using the scarce memory resource. The point is: if you define your own window, you must fill the desired characteristics of your window in a WNDCLASS or WNDCLASSEX structure and call RegisterClass or RegisterClassEx before you're able to create your window. You only have to register the window class once for each window type you want to create a window from.
Windows has several predefined Window classes, such as button and edit box. For these windows (or controls), you don't have to register a window class, just call CreateWindowEx with the predefined class name.
The single most important member in the WNDCLASSEX is lpfnWndProc. lpfn stands for long pointer to function. Under Win32, there's no "near" or "far" pointer, just pointer because of the new FLAT memory model. But this is again a leftover from the day of Win16. Each window class must be associated with a function called window procedure. The window procedure is responsible for message handling of all windows created from the associated window class. Windows will send messages to the window procedure to notify it of important events concerning the windows it 's responsible for,such as user keyboard or mouse input. It's up to the window procedure to respond intelligently to each window message it receives. You will spend most of your time writing event handlers in window procedure.
I describe each member of WNDCLASSEX below:

WNDCLASSEX:
  cbSize                     D§      ?
  style                        D§     ?
  lpfnWndProc        D§      ?
  cbClsExtra             D§      ?
  cbWndExtra           D§      ?
  hInstance                D§      ?
  hIcon                       D§      ?
  hCursor                  D§      ?
  hbrBackground     D§      ?
  lpszMenuName      D§      ?
  lpszClassName     D§      ?
  hIconSm                 D§      ?
 

cbSize: The size of WNDCLASSEX structure in bytes. We can use SIZEOF operator to get the value.
style: The style of windows created from this class. You can combine several styles together using "or" operator.
lpfnWndProc: The address of the window procedure responsible for windows created from this class.
cbClsExtra: Specifies the number of extra bytes to allocate following the window-class structure. The operating system initializes the bytes to zero. You can store window class-specific data here.
cbWndExtra: Specifies the number of extra bytes to allocate following the window instance. The operating system initializes the bytes to zero. If an application uses the WNDCLASS structure to register a dialog box created by using the CLASS directive in the resource file, it must set this member to DLGWINDOWEXTRA.
hInstance: Instance handle of the module.
hIcon: Handle to the icon. Get it from LoadIcon call.
hCursor: Handle to the cursor. Get it from LoadCursor call.
hbrBackground: Background color of windows created from the class.
lpszMenuName: Default menu handle for windows created from the class.
lpszClassName: The name of this window class.
hIconSm: Handle to a small icon that is associated with the window class. If this member is NULL, the system searches the icon resource specified by the hIcon member for an icon of the appropriate size to use as the small icon.

 
 

   call 'User32.CreateWindowExA' &NULL ClassName AppName,
                                 &WS_OVERLAPPEDWINDOW,
                                 &CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT,
                                 &NULL &NULL D§hInstance &NULL
 

After registering the window class, we can call CreateWindowEx to create our window based on the submitted window class. Notice that there are 12 parameters to this function.

CreateWindowExA proto dwExStyle:DWORD,\
   lpClassName:DWORD,\
   lpWindowName:DWORD,\
   dwStyle:DWORD,\
   X:DWORD,\
   Y:DWORD,\
   nWidth:DWORD,\
   nHeight:DWORD,\
   hWndParent:DWORD ,\
   hMenu:DWORD,\
   hInstance:DWORD,\
   lpParam:DWORD

Let's see detailed description of each parameter:
dwExStyle: Extra window styles. This is the new parameter that is added to the old CreateWindow. You can put new window styles for Windows 95 & NT here.You can specify your ordinary window style in dwStyle but if you want some special styles such as topmost window, you must specify them here. You can use NULL if you don't want extra window styles.
lpClassName: (Required). Address of the ASCIIZ string containing the name of window class you want to use as template for this window. The Class can be your own registered class or predefined window class. As stated above, every window you created must be based on a window class.
lpWindowName: Address of the ASCIIZ string containing the name of the window. It'll be shown on the title bar of the window. If this parameter is NULL, the title bar of the window will be blank.
dwStyle:  Styles of the window. You can specify the appearance of the window here. Passing NULL  is ok but the window will have no system menu box, no minimize-maximize buttons, and no close-window button. The window would not be of much use at all. You will need to press Alt+F4 to close it. The most common window style is WS_OVERLAPPEDWINDOW. A window style is only a bit flag. Thus you can combine several window styles by "or" operator to achieve the desired appearance of the window. WS_OVERLAPPEDWINDOW style is actually a combination of the most common window styles by this method.
X,Y: The coordinate of the upper left corner of the window. Normally this values should be CW_USEDEFAULT, that is, you want Windows to decide for you where to put the window on the desktop.
nWidth, nHeight: The width and height of the window in pixels. You can also use CW_USEDEFAULT to let Windows choose the appropriate width and height for you.
hWndParent: A handle to the window's parent window (if exists). This parameter tells Windows whether this window is a child (subordinate) of some other window and, if it is, which window is the parent. Note that this is not the parent-child relationship of multiple document interface (MDI). Child windows are not bound to the client area of the parent window. This relationship is specifically for Windows internal use. If the parent window is destroyed, all child windows will be destroyed automatically. It's really that simple. Since in our example, there's only one window, we specify this parameter as NULL.
hMenu: A handle to the window's menu. NULL if the class menu is to be used. Look back at the a member of WNDCLASSEX structure, lpszMenuName. lpszMenuName specifies *default* menu for the window class. Every window created from this window class will have the same menu by default. Unless you specify an *overriding* menu for a specific window via its hMenu parameter. hMenu is actually a dual-purpose parameter. In case the window you want to create is of a predefined window type (ie. control), such control cannot own a menu. hMenu is used as that control's ID instead. Windows can decide whether hMenu is really a menu handle or a control ID by looking at lpClassName parameter. If it's the name of a predefined window class, hMenu is a control ID. If it's not, then it's a handle to the window's menu.
hInstance: The instance handle for the program module creating the window.
lpParam: Optional pointer to a data structure passed to the window. This is used by MDI window to pass the CLIENTCREATESTRUCT data. Normally, this value is set to NULL, meaning that no data is passed via CreateWindow(). The window can retrieve the value of this parameter by the call to GetWindowLong function.
 
 

      mov D§WindowHandle eax

    call 'User32.ShowWindow'  D§WindowHandle &SW_SHOWNORMAL
    call 'User32.UpdateWindow'  D§WindowHandle

On successful return from CreateWindowEx, the window handle is returned in eax. We must keep this value for future use. The window we just created is not automatically displayed. You must call ShowWindow with the window handle and the desired *display state* of the window to make it display on the screen. Next you can call UpdateWindow to order your window to repaint its client area. This function is useful when you want to update the content of the client area. You can omit this call though.

 

L1: call 'User32.GetMessageA' FirstMessage 0 0 0 | cmp eax 0 | je L9>
        call 'User32.TranslateMessage'  FirstMessage
        call 'User32.DispatchMessageA'  FirstMessage
    jmp L1<

L9: call 'Kernel32.ExitProcess' &NULL

At this time, our window is up on the screen. But it cannot receive input from the world. So we have to *inform* it of relevant events. We accomplish this with a message loop. There's only one message loop for each module. This message loop continually checks for messages from Windows with GetMessage call. GetMessage passes a pointer to a MSG structure to Windows. This MSG structure will be filled with information about the message that Windows want to send to a window in the module. GetMessage function will not return until there's a message for a window in the module. During that time, Windows can give control to other programs. This is what forms the cooperative multitasking scheme of Win16 platform. GetMessage returns FALSE if WM_QUIT message is received which, in the message loop, will terminate the loop and exit the program.
TranslateMessage is a utility function that takes raw keyboard input and generates a new message (WM_CHAR) that is placed on the message queue. The message with WM_CHAR contains the ASCII value for the key pressed, which is easier to deal with than the raw keyboard scan codes. You can omit this call if your program doesn't process keystrokes.
DispatchMessage sends the message data to the window procedure responsible for the specific window the message is for.

If the message loop terminates, the exit code is stored in wParam member of the MSG structure. You can store this exit code into eax to return it to Windows. At the present time, Windows does not make use of the return value, but it's better to be on the safe side and plays by the rule.
 
 

Proc MainWindowProc:
    Arguments @Adressee, @Message, @wParam, @lParam

This is our window procedure. You have to name it MainWndowProc if you want the Tree View feature of SpAsm doing its job, but this is *not* an Assembler requirement. The first parameter, hWnd, is the window handle of the window that the message is destined for. uMsg is the message. Note that uMsg is not a MSG structure. It's just a number, really. Windows defines hundreds of messages, most of which your programs will not be interested in. Windows will send an appropriate message to a window in case something relevant to that window happens. The window procedure receives the message and reacts to it intelligently. wParam and lParam are just extra parameters for use by some messages. Some messages do send accompanying data in addition to the message itself. Those data are passed to the window procedure by means of lParam and wParam.

 

    pushad
        .If D@Message = &WM_DESTROY
            call 'User32.PostQuitMessage' &NULL
        .Else
            popad
            call 'User32.DefWindowProcA' D@Adressee D@Message D@wParam D@lParam
            Exit
        .End_If
 
        popad | mov eax &FALSE
EndP

Here comes the crucial part. This is where most of your program's intelligence resides. The codes that respond to each Windows message are in the window procedure. Your code must check the Windows message to see if it's a message it's interested in. If it is, do anything you want to do in response to that message and then return with zero in eax. If it's not, you MUST call  DefWindowProc, passing all parameters you received to it for default processing.. This DefWindowProc is an API function that processes the messages your program are not interested in.
The only message that you MUST respond to is WM_DESTROY. This message is sent to your window procedure whenever your window is closed. By the time your window procedure receives this message, your window is already removed from the screen. This is just a notification that your window was destroyed, you should prepare yourself to return to Windows. In response to this, you can perform housekeeping prior to returning to Windows. You have no choice but to quit when it comes to this state. If you want to have a chance to stop the user from closing your window, you should process WM_CLOSE message. Now back to WM_DESTROY, after performing housekeeping chores, you must call PostQuitMessage which will post WM_QUIT back to your module. WM_QUIT will make GetMessage return with zero value in eax, which in turn, terminates the message loop and quits to Windows. You can send WM_DESTROY message to your own window procedure by calling DestroyWindow function.


[Iczelion's Win32 Assembly HomePage]