Tutorial 2: MessageBox

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

Theory:

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 for use by any Windows programs. These functions are stored in several 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.
When a Windows program is loaded into memory, Windows reads the information stored in the program. That information includes the names of functions the program uses and the DLLs those functions reside in. When Windows finds such info in the program, it'll load the DLLs and perform function address fixups in the program so the calls will transfer control to the right function.
There are two categoriesof API functions: One for ANSI and the other for Unicode. The names 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.
We are usually familiar with ANSI strings, which are arrays of characters terminated by NULL. ANSI character is 1 byte in size. While ANSI code is sufficient for European languages, it cannot handle several oriental languages which have several thousands of unique characters. That's why UNICODE comes in. A UNICODE character is 2 bytes in size, making it possible to have 65536 unique characters in the strings.
 

Example:

I'll present the bare program skeleton below. We will flesh it out later. Click on [New] in the [File] menu option of SpAsm, and write:
 

Main:

In the above skeleton, the execution will start at the first instruction immediately below start label. The execution will proceed instruction by instruction until some flow-control instructions such as jmp, jne, je, ret etc is found. Those instructions redirect the flow of execution to some other instructions. When the program needs to exit to Windows, it should call an API function, ExitProcess.

call 'KERNEL32.ExitProcess'

without pushing a dword onto the stack, the assembler/linker will not be able to catch that error for you. You'll notice it later when your program crashes. You have call ExitProcess like this:

call  'KERNEL32.ExitProcess'  0

Put that line immediately below "Main:" label, you will get a win32 program which immediately exits to Windows, but it's a valid program nonetheless.

Main:
        call 'Kernel32.ExitProcess'  0
 

From windows.inc, your program got constant and structure definitions. In our example above, we call a function exported by kernel32.dll. If you open it with a text editor, you will see that it's full of function prototypes for kernel32.dll.

Now, if you click on [Compile] you get a new file on your disk, named 'New.exe'. Rneame it 'msgbox.exe'. Go on, run it. You'll find that it does nothing. Well, we haven't put anything interesting into it yet. But it's a Windows program nonetheless. And look at its size! In my PC, it is 1,536 bytes.

Next we're going to put in a message box. Its function prototype is:

MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD

hwnd is the handle to parent window. You can think of a handle as a number that represents the window you're referrring to. Its value is not important to you. You only remember that it represents the window. When you want to do anything with the window, you must refer to it by its handle.
lpText is a pointer to the text you want to display in the client area of the message box. A pointer is really an address of something. A pointer to text string==The address of that string.
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
Let's modify msgbox.asm to include the message box.
 

[MsgBoxCaption: B§  'Iczelion Tutorial No.2'  0
 MsgBoxText:       B§  'Win32 Assembly is Great!'  0]

Main:
    call  'USER32.MessageBoxA'   &NULL  MsgBoxText  MsgBoxCaption  &MB_OK
    call  'KERNEL32.ExitProcess'  &NULL
 

Complie and run it. You will 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 every ANSI string in Windows must be terminated by NULL (0 hexadecimal).
We use two constants, &NULL and &MB_OK. Those windows Equates are integrated inside SpAsm assembler. So you can refer to them by Symbols instead of the values. This improves readability of your source code.
The 2 operators are used to pass the address of a label to the function, not a value. To pass a value we would have to do something like this:

[dWordLabel: 20]

    mov eax D§dWordLabel
 
 

     

[Iczelion's Win32 Assembly HomePage]