Windows Operating System Programming

Document last modified: 

Windows programming generally involves developing a GUI, VB and other languages are well suited for GUI development by hiding much of the programming complexity using object oriented techniques. Assembly language is not often used for Windows GUI programming but is useful to examine the basic concepts in graphical operating system function calls.

Invoke/Local/Addr

The invoke and proc statements can replace the Call, pushing and removing parameters, and accessing parameters by referring to the eBp register explicitly type checking of parameters is performed. The local definition defines a dynamic variable that is accessible only within the procedure, allocation/deallocation code is generated by the Assembler. The addr operator is equivalent to the offset operator. Consider the C++ and corresponding prototype and call in Assembly:

int f(int x, int &y, char &z) 
{
   int i;
   i = x+y;
   return i;
}
 f  proc x : dword, y : ptr dword, z : ptr byte
    local i : dword
    mov	  eAx, x
    mov	  eBx, y		; y : ptr
    add	  eAx, [eBx]
    mov	  i, eAx
    ret
 f  endp
 f  proc near
    push  eBp
    mov   eBp, eSp
    sub   eSp, 4        ; local i 
    mov	  eAx, [eBp+4]  ; x
    mov	  eBx, [eBp+8]	; y : ptr
    add	  eAx, [eBx]
    mov	  [eBp-4], eAx
    mov   eSp, eBp
    pop   eBp
    ret
 f  endp
c = f(4, a, b);
    invoke  f, 4, offset a, offset b
    mov     c, eAx
    push offset b
    push offset a
    push 4
    call f
    add  eSp, 12
    mov  c, eAx

Minimal Windows Program using C++ and Assembler

The following C++ and Assembler programs display a message box on the screen which you can download and run, about the simplest Windows program possible. The message box is produced when calling the MessageBox function with the parameters for text and style to display:

 invoke MessageBox, 0, addr MsgBoxText, addr MsgCaption, MB_OK

Assembling/Executing - Though Visual Studio comes with Masm32 it does not seem to have the necessary include files.

#include "stdafx.h"   









char MsgCaption[]="Message Box Caption";
char MsgBoxText[]="Message Box Text";


int __stdcall WinMain(HINSTANCE hInstance, 
                      HINSTANCE hPrevInstance,
                      LPSTR lpCmdLine, 
                      int nCmdShow)
{
	MessageBox(0,
		   MsgBoxText, 
		   MsgCaption, 
		   MB_OK);
	return 0;
}
.386 
.model flat, stdcall 
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
MsgCaption      db "Message Box Caption",0
MsgBoxText      db "Message Box Text",0

.code
WinMain	proc	near




	invoke 	MessageBox, 0, \
		addr MsgBoxText, \
		addr MsgCaption, \
		MB_OK

	invoke ExitProcess, 0

WinMain	Endp
	end 	WinMain

"Hello World" Windows Programming using C++ and Assembler

The minimal Windows program to print the familiar "Hello World"  in both C++ and Assembler is more complex due to opening a standard window and handling some of the user events, download and run to observe program behavior. A cursory examination of either program gives the justified impression that Windows programs consist mainly of calls to Windows operating system functions.

The programs are nearly identical in implementation but the C++ executable is over ten times larger than the Assembler. The key functions in each are:

WinMain

  1. Called when program starts.
  2. Constructs and initializes the window.
  3. Iterates a message receive and dispatch loop until the window is closed.
  4. Indirectly calls WndProc by dispatching messages to operating system.

WndProc

  1. Handles messages passed from operating system callback.

WinMain is invoked first, similar to the C++ main function. Its primary function is to initialize the process, open a window, and execute a receive and dispatch message iteration. The messages are sent to the operating system which in turn calls the WndProc function to handle the messages.

WndProc is the heart of a Windows program where the user interface is implemented by handling events such as mouse movements, key presses, etc. In our simple example only two events are handled, the WM_PAINT event to display the Hello World text, and the WM_DESTROY event to destroy or close the window process.

#include "stdafx.h"











char szAppName[]="Hello World Window";	// Window title
char szClassName[]="Window Class";
char szHelloWorld[]="Hello World";



int WndProc(HWND hWnd, UINT message, 
            WPARAM wParam, LPARAM lParam)
{
 PAINTSTRUCT ps;
 HDC hdc;

 switch (message) {
   case WM_PAINT:
	hdc = BeginPaint(hWnd, &ps);	// Allocate paint 
	TextOut(hdc,0,0,szHelloWorld,11);	
	EndPaint(hWnd, &ps);		// Deallocate
	break;
   case WM_DESTROY:
	PostQuitMessage(0);		// Close
	break;
   default:
	return DefWindowProc(hWnd, message, wParam, lParam);
 }
 return 0;
}






int __stdcall WinMain(HINSTANCE hInstance, 
		HINSTANCE hPrevInstance, 
		LPSTR lpCmdLine, int nCmdShow){
 WNDCLASSEX wcex;
 MSG msg;
 HWND hWnd;

 wcex.cbSize 		= sizeof(WNDCLASSEX); 
 wcex.style		= CS_HREDRAW | CS_VREDRAW;
 wcex.lpfnWndProc	= (WNDPROC)WndProc;
 wcex.cbClsExtra	= 0;
 wcex.cbWndExtra	= 0;
 wcex.hInstance		= hInstance;
 wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
 wcex.lpszMenuName	= 0;
 wcex.lpszClassName	= szClassName;
 wcex.hIcon		= 0;
 wcex.hIconSm		= 0;
 wcex.hCursor		= LoadCursor(NULL,IDC_ARROW);

 

 RegisterClassEx(&wcex);

 hWnd = CreateWindow(szClassName, szAppName, 
	WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 
	0, CW_USEDEFAULT, 0, NULL, NULL, 
        hInstance, NULL);

 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);
 
// Main message loop
 while (GetMessage(&msg, NULL, 0, 0))
 {	TranslateMessage(&msg);
	DispatchMessage(&msg);
 }
 return msg.wParam;
}
.386 
.model flat, stdcall 
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib

.data
AppName  	db 	"HelloWorld Window",0
ClassName 	db 	"SimpleWinClass",0
helloWorld	db	"Hello World"

.code

WndProc proc hWnd:HWND, uMsg:UINT, \
	     wParam:WPARAM, lParam:LPARAM
  LOCAL      ps:PAINTSTRUCT

  @@switch:
	cmp 	uMsg,WM_DESTROY
	je	@@destroy
	cmp	uMsg, WM_PAINT
	je	@@paint
	jmp	@@default	
    @@destroy:			  	   ; WM_DESTROY event
	invoke 	PostQuitMessage,0 	   ; Quit
	jmp	@@endswitch
    @@paint:			  	   ; WM_PAINT event
	invoke 	BeginPaint,hWnd, ADDR ps   ; Allocate paint
	invoke 	TextOut,eAx,0,0,ADDR helloWorld, 11	
	invoke 	EndPaint,hWnd, ADDR ps	   ; Deallocate
	jmp	@@endswitch
    @@default:				   
	invoke DefWindowProc,hWnd,uMsg,wParam,lParam
	ret
  @@endswitch:
	Mov	eAx, 0
	ret
WndProc endp

WinMain proc 	hInstance:HINSTANCE,\
		hPrevInst:HINSTANCE,\
		CmdLine:LPSTR,CmdShow:DWORD
  LOCAL  wc:WNDCLASSEX
  LOCAL  msg:MSG
  LOCAL  hwnd:HWND

  mov    wc.cbSize,SIZEOF WNDCLASSEX
  mov    wc.style, CS_HREDRAW or CS_VREDRAW
  mov    wc.lpfnWndProc, OFFSET WndProc	; Event Handler
  mov    wc.cbClsExtra, 0
  mov    wc.cbWndExtra, 0
  push   hInstance
  pop    wc.hInstance
  mov    wc.hbrBackground, COLOR_WINDOW+1
  mov    wc.lpszMenuName, 0
  mov    wc.lpszClassName, OFFSET ClassName
  mov    wc.hIcon, 0			; Default icons
  mov    wc.hIconSm, 0
  invoke LoadCursor, 0, IDC_HAND	; Use hand cursor
  mov    wc.hCursor, eAx

  invoke RegisterClassEx, addr wc

  invoke CreateWindowEx, 0,ADDR ClassName,ADDR AppName,\
         WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
         CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,0,0,\
         hInstance,0
  mov    hwnd, eAx

  invoke ShowWindow, hwnd,SW_SHOWNORMAL
  invoke UpdateWindow, hwnd

  @@while:	; Main message loop
      invoke 	GetMessage, ADDR msg,0,0,0
      cmp	eAx, 0	
      Jne	@@do
      Jmp	@@endwhile
   @@do:
      invoke 	TranslateMessage, ADDR msg
      invoke 	DispatchMessage, ADDR msg
      Jmp	@@while
   @@endwhile:
      mov     	eax,msg.wParam
      ret
WinMain endp

main proc	near		; Windows Startup code
     	invoke 	GetModuleHandle, NULL

     	invoke 	WinMain, eAx, 0, 0, 0

     	invoke 	ExitProcess, 0
main endp

	end main

Handling more Windows events - Key Presses

The WndProc is invoked by the Windows operating system in response to events related to the window in which the event occurred. For example, pressing a key in a window sends a key pressed message to the WndProc for that window (remember that you may have several windows open at one time).

The following example adds handling of key press events and display of the characters to the window. Only the parts of the program changed from above are included, you'll notice that the WinMain does not change and the WndProc merely adds the code necessary buffering the key pressed in response to WM_CHAR message and displaying the characters buffered in response to the WM_PAINT message. Download and run to observe the program behavior when keys are pressed.

char buffer[1000];
int  count=0;

int WndProc(HWND hWnd, UINT message, 
            WPARAM wParam, LPARAM lParam)
{
 PAINTSTRUCT ps;
 HDC hdc;

 switch (message) {
   case WM_DESTROY:
	PostQuitMessage(0);		// Close
	break;
   case WM_CHAR:
	buffer[count] = (char) wParam;
	InvalidateRect(hWnd, 0, TRUE);
	count++;
	break;
   case WM_PAINT:
	hdc = BeginPaint(hWnd, &ps);	// Allocate paint 
	TextOut(hdc,0,0,buffer,count);	
	EndPaint(hWnd, &ps);		// Deallocate
	break;
   default:
	return DefWindowProc(hWnd, message, wParam, lParam);
 }
 return 0;
}
.data
buffer		db	1000 dup(?)
count		dd	0

.code
WndProc proc hWnd:HWND, uMsg:UINT, \
	     wParam:WPARAM, lParam:LPARAM
  LOCAL      ps:PAINTSTRUCT

  @@switch:
	cmp 	uMsg,WM_DESTROY
	je	@@destroy
	cmp	uMsg, WM_CHAR
	je	@@char
	cmp	uMsg, WM_PAINT
	je	@@paint
	jmp	@@default	
    @@destroy:			  	   ; WM_DESTROY event
	invoke 	PostQuitMessage,0 	   ; Quit
	jmp	@@endswitch
    @@char:	
	push	wParam
	pop	eAx
	mov	eBx, count
	mov	buffer[eBx], Al            ; Save key press
	invoke  InvalidateRect, hWnd, 0,TRUE
	inc	count
	jmp	@@endswitch
    @@paint:			  	   ; WM_PAINT event
	invoke 	BeginPaint,hWnd, ADDR ps   ; Allocate paint
	invoke 	TextOut,eAx,0,0,ADDR buffer, count	
	invoke 	EndPaint,hWnd, ADDR ps	   ; Deallocate
	jmp	@@endswitch
    @@default:				   
	invoke DefWindowProc,hWnd,uMsg,wParam,lParam
	ret
  @@endswitch:
	Mov	eAx, 0
	ret
WndProc endp

Handling even more Windows events - Mouse Moves - Sketch

The WndProc is invoked by the Windows operating system in response to events related to the window in which the event occurred. For example, moving the mouse in a window sends a mouse message to the WndProc for that window.

The following example adds handling of mouse click event to implement a sketch program. Only the parts of the program changed from above are included, you'll notice that again the WinMain does not change and the WndProc merely adds the code to store the <x,y> points locating the mouse movement across the screen and displaying lines through the array of <x,y> points in response to the WM_PAINT message. Download and run to observe the program behavior when the mouse is moved.

.data
ClassName 	db 		"SimpleSketchClass",0
AppName  	db 		"Sketch",0
points		POINT		1000 dup (<0,0>)
npoints		dd		0

.code
WndProc proc hWnd:HWND, uMsg:UINT, \
             wParam:WPARAM, lParam:LPARAM
   LOCAL ps:PAINTSTRUCT

   @@switch:	cmp 	uMsg,WM_DESTROY
		je	@@destroy
		cmp	uMsg, WM_MOUSEMOVE
		je	@@mousemove
		cmp	uMsg, WM_PAINT
		je	@@paint
		jmp	@@default	
	@@destroy:				; WM_DESTROY event
		invoke 	PostQuitMessage,0
		jmp	@@endswitch
	@@mousemove:				; WM_MOUSEMOVE event
		mov	eAx, size POINT		; Left mouse button
		mul	npoints	
		mov	eSi, eAx
		inc	npoints
		mov 	eAx,lParam		; Extract <x,y> mouse
		and 	eAx,0ffffh		; location from lParam
		mov 	points[eSi].x, eAx	; and save in points
		mov 	eAx,lParam		; array
		shr 	eAx,16
		mov 	points[eSi].y, eAx
		invoke 	InvalidateRect,hWnd,NULL,TRUE
		jmp	@@endswitch
	@@paint:				; WM_PAINT event
		invoke 	BeginPaint,hWnd, ADDR ps
		invoke	Polyline, eAx, addr points, npoints
		invoke 	EndPaint,hWnd, ADDR ps		
		jmp	@@endswitch
	@@default:			
		invoke DefWindowProc,hWnd,uMsg,wParam,lParam		
		ret
    @@endswitch:
	Mov	eAx, 0
	ret
WndProc endp

 

C Functions - File Copy

The standard library for the C language contains many useful functions for file handling, running other process, math, etc. These functions can be easily accessed by defining the PROTO for the C function and then INVOKE the function. The following example illustrates by copying a file to the screen.

If testing, the C program should be named file.C or some name with a C extension.

Copy the MSVCRT.LIB, located at:

The Assembly program below can be assembled with the MSVCRT.LIB in the same directory by:

The C program copies file.C while the Assembler program copies file.asm to the console screen. The commands necessary at IUS are:

  1. Copy the following Assembler program and save as file.asm.
  2. copy C:\Program Files\Microsoft Visual Studio .NET\VC7\Lib\MSVCRT.LIB
  3. v:\common\user\c335\assembler32
  4. ml /coff /Zi file.asm /link MSVCRT.LIB /subsystem:console
  5. file









int *fopen( const char *filename, 
            const char *mode );
int getc( int *stream );
int putchar( int c );


char filename[7] = "file.c";
char mode[2] = "r";


int main(void) {
	int *fp = fopen(filename, mode);
	int c;

	while ((c=getc(fp)) != -1)
		putchar(c);
	return 0;
}
.386
.model flat, stdcall
option casemap:none
      include \masm32\include\windows.inc
      include \masm32\include\kernel32.inc
      include \masm32\include\masm32.inc
      includelib \masm32\lib\kernel32.lib
      includelib \masm32\lib\masm32.lib

fopen	proto	near C, 
                filename:near ptr dword, mode:near ptr dword
getc 	proto	near C, fileptr : dword
putchar	proto	near C, char : dword

.data
filename	db	"file.asm",0
mode		db	"r",0

.code
main	proc	near
	local	fp : dword
	invoke	fopen, addr filename, addr mode
	mov	fp, eAx
@@while:
	invoke	getc, fp
	cmp	Al, -1
	jne	@@do
	jmp	@@endwhile
   @@do:
	invoke	putchar, eAx
	jmp	@@while
@@endwhile:

     	invoke 	ExitProcess, 0	
main	endp
	end 	main