Tutorial 2: MessageBox

In this tutorial, we will create a fully functional Windows program that displays a message box saying "Win32 assembly is great!".

Download the example file here.

Preliminary:

Windows prepares a wealth of resources for Windows programs. Central to this is the Windows API (Application Programming Interface). Windows API is a huge collection of very useful functions that reside in Windows itself, ready to be used by any Windows programs. These functions are located in dynamic-linked libraries (DLLs) such as kernel32.dll, user32.dll and gdi32.dll. Kernel32.dll contains API functions that deal with memory and process management. User32.dll controls the user interface aspects of your program. Gdi32.dll is responsible for graphics operations. Other than "the main three", there are other DLLs that your program can use, provided you have enough information about the desired API functions.
Windows programs dynamically link to these DLLs, ie. the codes of API functions are not included in the Windows program executable file. In order for your program to know where to find the desired API functions at runtime, you have to embed that information into the executable file. The information is in import libraries. You must link your programs with the correct import libraries or they will not be able to locate API functions.
There are two types of API functions: One for ANSI and the other for Unicode. The name of API functions for ANSI are postfixed with "A", eg. MessageBoxA. Those for Unicode are postfixed with "W" (for Wide Char, I think). Windows 95 natively supports ANSI and Windows NT Unicode.
But most of the time, you will use an include file which can determine and select the appropriate API functions for your platform. Just refer to API function names without the postfix.

Content:

I'll present the bare program skeleton below. We will fill it out later.

.386
.model flat, stdcall

.data

.code
start:
end Main

Every Windows program must call an API function, ExitProcess, when it wants to quit to Windows.
In this respect, ExitProcess is equivalent to int 21h, ah=4Ch in DOS.
Here's the function prototype of ExitProcess from winbase.h:

void WINAPI ExitProcess(UINT uExitCode);

void means the function does not return any value to the caller.
WINAPI is an alias of STDCALL calling convention.
UINT is a data type, "unsigned integer", which is a 32-bit value under Win32 (it's a 16-bit value under Win16)
uExitCode is the 32-bit return code to Windows. This value is not used by Windows as of now.

In order to call ExitProcess from an assembly program, you must first declare the function prototype for ExitProcess.

.386
.model flat, stdcall
ExitProcess     PROTO     :DWORD
.data
.code
start:
     invoke    ExitProcess, 0
end start

That's it. Your first working Win32 program. Save it under msgbox.asm.
Assuming ml.exe is in your path, assemble msgbox.asm with:

/c tells MASM to assemble only. Do not invoke Link.
/coff tells MASM to create .obj file in COFF format
/Cp tells MASM to preserve case of user identifiers

Then go on with link:

/SUBSYSTEM:WINDOWS  informs Link what sort of executable this program is
/LIBPATH:<path to import library> tells Link where the import libraries are. In my PC, they're located in c:\masm611\lib

Now you get msgbox.exe. Go on, run it. You'll find that it does nothing. Well, we haven't put anything interesting in it yet. But it's a Windows program nonetheless. And look at its size! In my PC, it is 1,536 bytes. 
The line:

is a function prototype. You declare the function name followed by the keyword "PROTO"  and lists of data types of the parameters separated by commas. MASM uses function prototypes to check the number and type of parameters passed to functions.
The best place for function prototypes is in an include file. You can create an include file full of frequently used function prototypes and data structures and include it at the beginning of your asm source code.
You call the API function by using INVOKE keyword: INVOKE is really a kind of specialized call. It checks number and type of parameters and pushes parameters on the stack according to the default calling convention (in this case, stdcall). By using INVOKE instead of normal call, you can prevent stack errors from incorrect parameter passing. Very useful. The syntax is: where expression is a label or function name.
Next we're going to put in a message box. Its function declaration is:

int WINAPI MessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

where hwnd is the handle to parent window
lpText is a pointer to the text you want to display in the client area of the message box
lpCaption is a pointer to the caption of the message box
uType specifies the icon and the number and type of buttons on the message box

Under Win32 platform, HWND, LPCSTR, and UINT are all 32 bits in size.

Let's modify msgbox.asm to include the message box.

.386
.model flat, stdcall
ExitProcess      PROTO      :DWORD
MessageBoxA PROTO      :DWORD, :DWORD, :DWORD, :DWORD
.data
MsgBoxCaption  db "Iczelion Tutorial No.2",0
MsgBoxText       db "Win32 Assembly is Great!",0
.const
NULL        equ  0
MB_OK    equ  0
.code
    Main:
     INVOKE    MessageBoxA, NULL, ADDR MsgBoxText, ADDR MsgBoxCaption, MB_OK
     INVOKE    ExitProcess, NULL
    end Main

Assemble and run it.(You have to include user32.lib in your Link parameter, since link info of MessageBoxA is in user32.lib)
You'll see a message box displaying the text "Win32 Assembly is Great!".
Let's look again at the source code.
We define two zero-terminated strings in .data section. Remember that all strings in Windows must be terminated with zero.
We define two constants in .const section. We use constants to improve clarity of the source code.
Look at the parameters of MessageBoxA. The first parameter is NULL. This means that there's no window that *own* this message box.
The operator "ADDR" is used to pass the address of the label to the function.


[Iczelion's Win32 Assembly HomePage]