--->Diese Seite gibts auch in Deutsch.

WIN32 ASSEMBLER TUTORIAL CHAPTER 1.2BETA
-=( T$ )=-

Finished the first part? Then let's continue by creating a window. The code can be used as the base of further projects.

Content:

This time there will be hardly any source code in the text, since most things are language independent. Source codes for NASM/TASM/MASM are attached in the ZIP again.

Note: The best windows.inc to date was made by Iczelion and Hutch. Unfortunately, it only works with MASM. For NASM there is a port of an older MASM-windows.inc which contains everything that's important. The .inc file for TASM I know only covers Win3.x so you have to get the data relevant for Win32 from the other include files.

1. Windows and Window Classes

In Windows a window is not only necessary in order to display something on the screen but it's the pivot of all communication in Windows. In order to get and evaluate keypresses, mouse movements, etc., you need a window. Some things can also be polled, but
1. this is not effective and
2. something can happen between two polls which will remain unnoticed.

Not only the standard window but also PopUp menus, buttons, the desktop, TreeViews, etc. are windows. Every window is based on a window class which describes certain attributes of this window. Window classes are especially useful for programs that use several windows.
A window can have many attributes: minimized, maximized, displayed, hidden, active, inactive, with or without a title bar... The attributes can be changed by both your own program and external programs. (Usually only two states of a button are used: hidden or shown. But you can also minimize or maximize it. That will give your program an entirely new, weird look.)

2. Messages and Message Processing

Messages are sent in order to keep the programs updated about events or requests.

They are created by: They are sent to: A message consists of a MessageID which indicates the type of the message and two parameters with extended information on the event (lParam and wParam).

To process the messages, a MessageQueue is created for the window or the thread. It collects all incoming messages. You evaluate them using the function GetMessage. GetMessage ALWAYS returns a message. That's because this function waits (and releases CPU time to other programs) until a message has arrived. If you don't want to wait, you can use PeekMessage instead. This function returns immediately. Therefore it also returns whether a message has arrived at all. If the return value of GetMessage equals 0, WM_QUIT has occurred.

Using DispatchMessage you forward the message to the window procedure by means of the OS.

A small list of important messages:

WM_CREATEThe window has been created. This message is automatically produced by CreateWindowEx.
WM_QUITThe program is to exit.
WM_DESTROYThe window is to be destroyed.
WM_PAINTThe window is to be re-painted.
WM_KEYDOWN, WM_KEYUPI don't think I have to explain these. Useful: The scancode is included.
WM_SYSKEYDOWN, WM_SYSKEYUPLike above, but combined with the ALT keys (menu commands).
WM_MOUSEMOVEGuess what... Additionally, it contains the state of the mouse buttons. For the mouse buttons itself there are also WM_LBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONDOWN, WM_RBUTTONUP.
WM_TIMERThis is sent by the standard timer if it's activated. The multimedia timers, however, are a better solution for faster demos and games.
WM_ACTIVATEThe application is activated or terminated. You should evaluate this message in demos and games in order to stop all threads to prevent the program from continuing in the background.
WM_COMMANDA menu item has been selected.

3. The Window Procedure

Every window has a window procedure in which it evaluates the messages that have been forwarded to it by DispatchMessage.
So far, we have only called Windows functions. Now for a change Windows calls our function! These CALLBACKs match the STDCALL convention, like most of the API functions: The parameters are put onto the stack, the return value will be in EAX, the stack has to be cleared by the called function. Callbacks also occur when using the multimedia timer, for instance.

The parameters of the window procedure are:
Window handle, MessageID, wParam, lParam.
In exactly this order they are located on the stack when the window procedure is called. They correspond to the order of the MSG structure for GetMessage, PeekMessage, and DispatchMessage.

Now you can read the MessageID from the stack. If the message is relevant, you can evaluate it using wParam and lParam. Otherwise leave it to Windows.

If you have evaluated the message yourself, you can return directly using RET 16. The number 16 after RET indicates the number of bytes to be removed from the stack.

If the message has been evaluated incompletely or not at all, you can leave the evaluation to DefaultWndProc. This Windows function contains all necessary elements to handle the messages for a window.

Since our window procedure is only an extension of DefaultWndProc, both functions have the same parameter structure. You can use that by directly JuMPing into the function instead of putting the parameters onto the stack and CALLing DefaultWndProc. Of course this requires leaving the stack unchanged. This way we can skip creating the parameters and returning to the caller procedure - that's now done by DefaultWndProc.

4. Menus

Menus aren't necessary but they're easy to implement. There are two ways to do it:
a) directly in your program, using the Windows functions (if you're really bored and don't know what to do),
b) in a resource script, either manually using a text editor or visually using a resource editor.

About the syntax of the menus in our example program:
Win002menu MENU defines a menu called Win002menu. The menu itself is implemented in the following BEGIN...END-block.
MENUITEM "Menüpunkt" , zahlcreates a menu item called "menuitem" with the given ID number.
POPUP "Untermenüname"creates a sub-menu item with the given name. The sub-menu is in a BEGIN-END-block again.
MENUITEM SEPARATORcreates a separator line.
Now you can load the menu, either as a menu with a fixed position like the menu below the title bar of a window or as a menu with a variable position like a context menu.
You can (but don't have to) change the outer appearance of the menu using API function. After using a menu you have to delete it again, otherwise you'll get a memory overflow after some time.

In the example code the menu is used in the easiest way: If you include it in the definition of the window class, it is automatically loaded, activated and deleted.

As soon as the user selects a menu item, a WM_COMMAND message is created. Its item wParam contains the ID of the menu item.

5. Example program

Action #1: Create a window class

RegisterClassEx defines a window class.
The following items of the WNDCLASSEX structure are needed:
cbSizeDefines the size of the structure. Use size WNDCLASSEX
styleDescribes the behaviour of the window. See API "Class Styles" (CS_xxx)
lpfnWndProcPointer to the window procedure
cbClsExtraNeeded if the class shall be extended. Can be left 0.
cbWndExtraThe same for a window. For all classes except dialogues, this can be left 0.
hInstanceMarks our program. Is retrieved using GetModuleHandle.
hIconIcon for the window. Use 0 for a standard icon, otherwise the handle of a resource.
hCursorLike icon, 0 = standard cursor.
hbrBackgroundBackground colour. If 0, the window won't get coloured automatically.
lpszMenuNameName of the menu resource (0 = no menu).
lpszClassNameName which identifies the class. You can call it as you want.
hIconSmSmall icon. See normal icon.
Handles for resources have to be created using LoadIcon, LoadCursor or LoadBitmap!

Action #2: Displaying the window

CreateWindowExcreates the window.
The parameters are (in the order they appear in the source):
lpParamPoints to a value for lParam of the WM_CREATE message. Only needed for apps with child windows. Otherwise 0.
hInstanceAlready explained somewhere else...
hMenuHere you can specify a menu handle. Since we've already specified a menu in the class, you can use 0 here. In this case you do not need to compute its handle.
hWndParentHandle of the ParentWindow. Since we don't create a child window, we use 0.
nHeightNo comments.
nWidthI need not say anything about this.
yRelated to the positition of the window's top-left corner.
xLike y.
dwStyleVarious constants combined with OR determining the visual appearance and the behaviour of the window.
lpWindowNamePoints to the name of the window. Just for optical reasons.
lpClassNamePoints to the name of the class. This class must exist, otherwise there will be no window (see RegisterClassEx and WNDCLASSEX).
dwExStyleExtension of dwStyle. Supported by Win9x/NT4 and higher.
If successful, the function will return a handle to the window, otherwise it will return 0.
If a window has been created, several messages such as WM_CREATE have been DIRECTLY sent to the window procedure. That means, the window procedure is not called by the MessagePump here!

Action #3: The MessageLoop aka MessagePump

Using GetMessage you empty the Message Queue one by one. GetMessage needs four parameters. With the first two parameters, you can restrict the type of messages to be processed, e.g. to mouse-messages only. To get all messages, set both parameters to 0.
The next parameter restricts the received messages to a certain window. You have to set it to the corresponding window handle. If you use 0, you will get all messages related to our program.
The last parameter points to a buffer of a size of seven DWords in which the message is stored.
This function returns three possible values: 1 stands for a successfully received message. 0 means that WM_QUIT has been received. It is a request to quit the program, especially the MessageLoop. You can also get -1, but then something really messed up, like a wrong window handle (which practically means that either your code or the whole system has got fucked up). If you don't want to wait for GetMessage having received a message, you can also use PeekMessage. This allows you to display your animation in the MessageLoop. It works fine, but a separate thread for it is the smarter solution.

DispatchMessage now forwards the message to a window procedure. If you want text input, you should, however, first convert the WM_KEYDOWN messages to WM_CHAR messages using TranslateMessage.

Action #4: The Window procedure

Not much to say about that: You check if the current message is interesting. If it is, you can do something with it and return, if it isn't, you can leave it to Windows. You can also process a message and then pass it to DefWndProc, but that's rarely useful.

Perhaps PostQuitMessage might be interesting. Like its name says, it posts a WM_QUIT message. The parameter of this function becomes the lParam of the message and represents a return value. Instead of this function you could also set a flag, but since other sources might create WM_QUIT too, this is the "cleaner" variant.

6. What else?

I have no idea what else to say about this topic so let's continue the tute in the next issue. The code will get more multimedial there.
Mail the Author: webmeister@deinmeister.de

Homepage Programming Win32Asm Downloads Software Hardware Cartoons Sitemap