Tutorial 2: MessageBox
HEORY:
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 Area, Provided You Haven 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 The Correct IMPORT LIBRARIES ORTEY 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 can not handle several Oriental Languages Which Have Several Thousands of Unique Characters............................
BUT MOST OF THE, 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.
EXAMPLE:
I'll present The Bare Program Skeleton Below. WE WILL FLESH IT OUT LATER.
.386.Model flat, stdcall.data .code start: End Start
The execution starts from the first instruction immediately below the label specified after end directive. 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, EXIXTPROCESS.
EXITPROCESS Proto UEXITCODE: DWORD
The above line is called a function prototype A function prototype defines the attributes of a function to the assembler / linker so it can do type-checking for you The format of a function prototype is like this:.. FunctionName PROTO [ParameterName]: DataType , [Parametername]: DataType, ...
In short, the name of the function followed by the keyword PROTO and then by the list of data types of the parameters, separated by commas. In the ExitProcess example above, it defines ExitProcess as a function which takes only one parameter of type DWORD. Functions Prototypes Are Very Useful When You Use The High-Level Call Syntax, Invoke. You Can Think of Invoke AS A Simple Call with Type-Checking. For Example, IF you do:
Call EXITPROCESS
WITHOUT PUSHING A DWORD ONTO The Stack, The asseber / linker not becomble to catch That error for you. You'll Notice It Later When Your Program Crashs. But if you use:
Invoke EXITPROCESS
The Linker Will Inform You That You Forgot To Push A DWORD ON The Stack Thus Avoiding Error. I Recommend You Use Invoke Instead of Simple Call. The Syntax of Invoke IS FOLLOWS:
Invoke expression [, arguments]
Expression Can Be The Name of a Function or It Can Be A Function Pointer. The Function Parameters Are Separated by Commas.
Most of function prototypes for API functions are kept in include files. If you use hutch's MASM32, they will be in MASM32 / include folder. The include files have .inc extension and the function prototypes for functions in a DLL is stored in .inc file with the same name as the DLL. for example, ExitProcess is exported by kernel32.lib so the function prototype for ExitProcess is stored in kernel32.inc. You can also create function prototypes for your own functions. Throughout my examples, I'll use hutch's windows.inc which you can download from http: //win32asm.cjb.netNow back to ExitProcess, uExitCode parameter is the value you want the program to return to Windows after the program terminates you can call ExitProcess like this.:
Invoke EXITPROCESS, 0
Put That Line Immediely Below Start Label, You Will Get A Win32 Program Which Immediely Exits To Windows, But It's A Valid Program Nonetheless.
.386 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/kernel32.inc includelib /masm32/lib/kernel32.lib .data .code start: invoke ExitProcess, 0 end start
option casemap: none tells MASM to make labels case-sensitive so ExitProcess and exitprocess are different Note a new directive, include This directive is followed by the name of a file you want to insert at the place the directive is In the above... example, when MASM processes the line include /masm32/include/windows.inc, it will open windows.inc which is in / MASM32 / include folder and process the content of windows.inc as if you paste the content of windows.inc there . hutch's windows.inc contains definitions of constants and structures you need in win32 programming. It does not contain any function prototype. windows.inc is by no means comprehensive. hutch and I try to put as many constants and structures into it as possible but there are still many left to be included. It'll be constantly updated. Check out hutch's and my homepage for updates. From windows.inc, your program got constant and structure definitions. Now for function prototypes, you need to include other include Files. th Ey Are All Stored IN / MASM32 / Include Folder.
In our example above, we call a function exported by kernel32.dll, so we need to include the function prototypes from kernel32.dll. That file is kernel32.inc. If you open it with a text editor, you will see that it's full of function prototypes for kernel32.dll If you do not include kernel32.inc, you can still call ExitProcess but only with simple call syntax you will not be able to invoke the function The point here is that:... in order to invoke a function, you have to put its function prototype somewhere in the source code. in the above example, if you do not include kernel32.inc, you can define the function prototype for ExitProcess anywhere in the source code above the invoke command and it will work. The include files are there to save you the work of typing out the prototypes yourself so use them whenever you can. Now we encounter a new directive, includelib. includelib does not work like include. It 's only a way To Tell the assembler what import library your program buys. When THE A ssembler sees an includelib directive, it puts a linker command into the object file so that the linker knows what import libraries your program needs to link with. You're not forced to use includelib though. You can specify the names of the import libraries in . the command line of the linker but believe me, it's tedious and the command line can hold only 128 characters.Now save the example under the name msgbox.asm Assuming that ml.exe is in your path, assemble msgbox.asm with:
ML / C / COFF / CP MSGBox.asm
/ C tells MASM to assemble only. Do not invoke link.exe. Most of the time, you would not want to call link.exe automatically since you may have to perform some other tasks prior to calling link.exe. / Coff tells MASM to create .obj file in COFF format. MASM uses a variation of COFF (Common Object File Format) which is used under Unix as its own object and executable file format. / Cp tells MASM to preserve case of user identifiers. If you use hutch's MASM32 package, you may put "option casemap: none" at the head of your source code, just below .model directive to achieve the same effect.After you successfully assemble msgbox.asm, you will get msgbox.obj msgbox.obj is. ..................................... ..
Then Go ON with LINK:
LINK / SUBSYSTEM: Windows / LibPath: C: / Masm32 / Lib msgbox.obj
/ Subsystem: Windows Informs Link What Sort of Executable this Program Is
/ Libpath:
Link Reads in The Object File and Fixes It With Addresses from The Import Libraries. When the process is finished you get msgbox.exe.
Now you get msgbox.exe. Go ON, Run It. You'll Find That Does Nothing. Well, We Haven't put anything INTERESTING INTO IT. 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 this string.
LPCAPTION IS A Pointer to the CAPTION OF THE Message Box
Utype specifies the icon and the number and type of button on the message box
Let's modify msgbox.asm to include the message box.
.386 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/kernel32.inc includelib /masm32/lib/kernel32.lib include /masm32/include/user32.inc includelib / masm32 /LIB/USER32.LIB
.Data Msgboxcaption DB "icion tutorial No.2", 0 MSGboxText DB "Win32 Assembly is Great!", 0
. Code Start: Invoke Messagebox, Null, Addr Msgboxtext, Addr Msgboxcaption, MB_OK Invoke EXITPROCESS, NULL End Start
Assemble and Run It. You will see a message box displaying the text "Win32 assembly is get!".
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 constants are documented in windows.inc. So you can refer to them by name instead of the values. This improves readability of your source code. The addr operator is used to pass the address of a label to the function. It's valid only in the context of invoke . directive You can not use it to assign the address of a label to a register / variable, for example You can use offset instead of addr in the above example However, there are some differences between the two:.. addr can not handle forward Reference While Offset Can. for Example, If The Label IS Defined Somewhere Further In The Source Code Than The Invoke Line, Addr Will Not Work.
Invoke Messagebox, Null, Addr MsgboxText, Addr Msgboxcaption, MB_OK
......
MSGBoxCaption DB "ICZelion Tutorial No.2", 0
MsgboxText DB "Win32 Assembly IS Great!", 0
MASM will report error. If you use offset instead of addr in the above code snippet, MASM will assemble it happily. Addr can handle local variables while offset can not. A local variable is only some reserved space in the stack. You will only know its address during runtime. offset is interpreted during assembly time by the assembler. So it's natural that offset will not work for local variables. addr is able to handle local variables because of the fact that the assembler checks first whether the variable referred to by addr is a global or local one. If it's a global variable, it puts the address of that variable into the object file. In this regard, it works like offset. If it's a local variable, it generates an instruction sequence like this before it actually Calls The Function: Lea Eax, Localvar
Push EAX
Since Lea CAN DETERMINE The Address of a Label At Runtime, this Works Fin