Let's make windows in Windows!


Now that you've had a wiff of what you're in for, it's time to do the one thing I wished someone could have done with me while I was learning, walk me through a small program. If you want, just copy and paste the bits of code into Microasm. Just make sure you read all the explanation. I'll start off with the most necessary (I'll go in order so you can assume that any new bits of code go directly underneath the last unless I specify otherwise). Just one note before we begin, minutae and trivial details (like spaces after a comma, or number of lines between commands, and other trivial details) don't matter in assembly.
	.386
	.model	flat

	include win32.inc

	public _start

	.code
_start:
Just copy this in every program you ever write (so long as you're using TASM at least). Please download my win32.inc and move it into your include folder. If your compiler is claiming that you have undefined symbols, its because you're not using the same constant names as I am. This include file uses the same symbols as C (this is very common in assembly). People generally learn assembly after C, personally, I still don't know C but could pick it up in a day if I had to. Anyway, the above code is necessary for every program you write (though none of it will ever be executed when you run your program). You must also have a
	end	_start
at the end of your program to signify the end of code. But now you're set to start! Hmmmm. What's the first thing we need? Well we want to create a Window, a living and breathing entity that the computer must be able to communicate with. So we need our entity to have a name, or something that the computer will use to separate it from other programs (kind of like an ip). So we will give our program (or module) a module handle (or name) by calling the GetModuleHandleA command. Remember you could just stick in your code somewhere or in an include file that GetModuleHandle equ GetModuleHandleA if that A really bothers you.
	extrn	GetModuleHandleA:near	;do this once for every different call!

	push	large 0				;parameter
	call	GetModuleHandleA
Viola! Our program has a name! Right now it is in the eax register (all functions will use the eax register to communicate the results of the function call). We must store his name because it is very important. So let's make us a variable and assign it the value of our program's name!
	.data
hisname dd ?
	.code

	mov		hisname, eax	;moves eax to hisname variable

Since the eax register is 32-bit, we need a 32-bit variable to store it. That's why we use dd (define double word) instead of dw (define word) or db (define byte). When we change back to our code, we tell the machine to move the contents of the eax register into our name variable. This name variable is actually the handle of the file that created the program (for those who care) and would be different if it was called from a different file (a dll for example).

Now we have to start thinking about our window. First we need to tell the computer a couple of things about our window. To do this we need to use the WNDCLASSEX struct. A struct is just a collective group of variables. Look in your win32.inc include file (do a search) and you'll find this somewhere:

WNDCLASSEX STRUC
wcxSize		dd ?	   ; cbSize
wcxStyle	dd ?    ; style
wcxWndProc	dd ?    ; lpfnWndProc
wcxClsExtra	dd ?    ; cbClsExtra
wcxWndExtra	dd ?    ; cbWndExtra
wcxInstance	dd ?    ; hInstance
wcxIcon		dd ?    ; hIcon
wcxCursor	dd ?    ; hCursor
wcxBackgroundBrush dd ?    ; hbrBackground
wcxMenuName	dd ?    ; lpszMenuName
wcxClassName	dd ?    ; lpszClassName
wcxSmallIcon	dd ?	   ; hIconSm
WNDCLASSEX ENDS
Do not paste this in your code! I just said find it and make sure it's in your include file! If not, paste it in your include file and save it. Now we got to make sure we have some things first. We are going to have to give each of the variables in the above struct a value so let's do that. But first we have to create a variable that actually IS the struct. This is all done in the data:
.data
wc	WNDCLASSEX <size WNDCLASSEX,0,WndProc,0,0,0,0,0,COLOR_WINDOW+1, 0,wndclsname, 0>
wndclsname db 'Henry',0
We have called our WNDCLASSEX struct the variable wc to refer to it. By using those brackets and using commas as separators, we can assign values to each of the variable in the struct in this one line.

The first variable wcxSize gets the size of the struct which the compiler calculates when you use the size command.

We give a zero to style for now.

WndProc is the name of the location that we will declare (as WndProc: followed by a bit of code) that will give commands to the program when certain events occur (you'll see it soon).

Then there's five zeroes that you won't have to worry about yet.

Now we have to decide on the colour of the background of our window. So we use the windows you have customized for Win95 already. COLOR_WINDOW refers to the constant that you've assigned for the colour of your windows. The +1 is necessary so put it in.

A menu? Not yet. We make this a zero.

The real name of our program! We have to give it a real name not a number! So we define a string to be Henry to be his name. Throught assembly, you'll almost always need a zero at the end of your string. This is how the CPU knows that the string ends.

Finally we through in one more zero since we aren't worrying about icons right now.

But wait! Our number name wasn't assigned to him! We have to do that now! One of those five zeroes (the third one) should correspond to the handle of the program. So let's insert it now (note: This one-line method isn't the only way to give values to variables in a struct). Also, you can't move a variable directly into another variable. You must use the registers as a medium. I arbitrarily chose ebx.

	.code
	mov	ebx, hisname
	mov	wc.wcxInstance, ebx
Now we are ready to register our window. This is easily done:
	extrn	RegisterClassExA:near
	push	offset wc	;our wndclassex struct
	call	RegisterClassExA
Now it's time for our window's birth! But first we should give it a name (the title that will be in the title bar).
	.data
	title1 db 'My Application',0
Now we will create our window.
	.code
	extrn	CreateWindowExA:near

	push	large 0		; lpParam 
	push	hisname		; name from GetModuleHandle
	push	large 0		; menu hmenu
	push	large 0		; parent hwnd
	push	large 300		; height
	push	large 400		; width
	push	large 100		; y
	push	large 100		; x
	push	WS_OVERLAPPEDWINDOW	; Style use constants from SDK
	push	offset title1		; title bar
	push	offset wndclsname	; Class name from wc (Henry)
	push	WS_EX_OVERLAPPEDWINDOW	; extended style constants from SDK
	call	CreateWindowExA

Now our window will show up! The name, height, width, x, y, title, class name are obvious and the zeroes are nothing to worry about right now. The WS_OVERLAPPEDWINDOW and WS_EX_OVERLAPPEDWINDOW are default window styles. They are just a combination of many constants that a normal window has. One of the most important constants to have is the WS_VISIBLE constant or esle your window will be invisible! This is included in the constant that I used above. Go ahead and open win32.inc and search for it. You'll also find many other constants you could use to change your window. You should use your SDK as a reference to what all these constants do. If you need help with this, contact me.

So now we have most of the program done. We have a window registered and created. Now we have to insert some code that will allow us to process events. In other words, we have to give our window life! So insert the following code which is mandatory for all programs:

	extrn	GetMessageA:near,DispatchMessageA:near,TranslateMessage:near

	.data
msgbuf MSG	<>

	.code
msg_loop:
	push	large 0		; uMsgFilterMax
	push	large 0		; uMsgFilterMin
	push	large 0		; hWnd (filter), 0 = all windows
	push	offset msgbuf	; lpMsg
	call	GetMessageA		; returns FALSE if WM_QUIT
	or	eax, eax
	jz	end_loop

	push	offset msgbuf
	call	TranslateMessage

	push	offset msgbuf
	call	DispatchMessageA

	jmp	msg_loop
end_loop:

	extrn	ExitProcess:near

	push	large 0	; (error) return code
	call	ExitProcess
This code is where the program dwells. It will stay in this loop and read messages (or events) that come to it. It will automatically do some functions (like minimizing) but not all. MSG is another struct (go and find it if you want), it is used to store the current message and details about it (like the mouse's coordinates when the event occured). When the program is terminated, this loop jumps to end_loop and then exits the program. Leave this code as is (do not fiddle with it much).

Now it's time to define that WndProc I was talking about earlier. This is where we customize our own processes.

	extrn	DefWindowProcA:near
WndProc:
	mov	eax,[esp+4+4]		; message ID
	cmp	eax, WM_PAINT
	je	wm_paint
	cmp	eax,WM_DESTROY		; about to start window destruction
	je	start_destroy

	jmp	DefWindowProcA		; delegate other message processing
As you can see, the WndProc comes right after the code to quit the program, that way it won't be accidently run. Everytime the screen changes, or something overlaps part of your window, it must be repainted. This is why we need to check if we need to repaint via the WM_PAINT constant. If we do need to repaint, it jumps to the appropriate place to repaint the window. Though windows retains the shape of the window, anything you draw or write on a window must be repainted. However, this ain't our problem right now so right now our repaint will be just this:
wm_paint:
	ret
Nothing! It will go to this repainting section and return right away! That's just cause we don't need to repaint anything. However the next one, the WM_DESTROY message is very important. You could create a window and then press Alt-F4 or click the X to close the window. This will make the window close, but your program will still be running! So here is the code to end your program:
	extrn PostQuitMessage:near
start_destroy:
	push	large 0
	call	PostQuitMessage

	xor	eax,eax
	ret	
This code will post the message to quit. When it is encountered in the message loop, the jz end_loop is satisfied so the program exits the loop and ends the process.

And just like that your program is finished! Your program should follow this overall map:

Prelim - GetModuleHandle - Register class - create window - message loop - WndProc - paint - Quit

Remember, your program will never leave the message loop unless you tell it to (like the PostQuitMessage does). So go ahead and compile it. Then play around with different constants and see their combined effect. I also want to mention the versatility of the CreateWindow command. This function is also used to add buttons, text fields, combo boxes, etc. on your window. You don't need to register it again though! All you have to do is enter the right parameters, call CreateWindow and boom a button appears. The function will return the handle of the button that will be used to identify it when pressed. However you need a new WndProc for each of these and of course you can't use the name WndProc again, so pick another name!

If you want to see the full code in order to make sure you got it right, go to the examples page using the link below. You should also try some of the books on my Home page, trust me it's a lot easier with them. Personally, I grew up with computers but still I prefer something physical over an on-screen tutorial. Also, we put these tutorials up for free, they get paid to teach, who do you think will do a better job?

If you read through this tutorial without knowing the actual language, (ie. you didn't know how to define a variable, you were unfamiliar with the mov, push, and jump commands) Then you should go read up on it at the Art of Assembly tutorial. The link is on my home page. After you know the basic commands, then you can read through my tutorial which will teach you how to use them.

 
Time to put your newly acquired knowledge to work with these examples . . .

Home - Next

1