Cogs and Levers A blog full of technical stuff

Windows programs with masm32

In a previous post I wrote about the basics of getting a program written and compiled using masm32 for the windows platform. In today’s post, I’m going to walkthrough the basic anatomy of a windows program that play the key roles in your application.

This information is language agnostic. It’s standard windows programming knowledge that I’ll present in the form of assembly programming with masm32. By the end of this blog post, you’ll have a good starting point (in boilerplate code) for any program that you want to write with masm32.

The basic pieces

In any windows program (using the Win32 API) there are some basic pieces that you’ll see most of the time.

  • Main entry point (WinMain)
  • Window creation
  • Window handler procedure (WNDPROC)
  • Message pump

These basic building blocks that you’ll see in just about all windows applications.

WinMain

This is your application’s main entry point. It’s analogous to the main function that you use when writing applicatioons in C/C++. From Microsoft’s site:

The user-provided entry point for a graphical Windows-based application.

Its function signature takes the following form:

int CALLBACK WinMain(
  _In_ HINSTANCE hInstance,
  _In_ HINSTANCE hPrevInstance,
  _In_ LPSTR     lpCmdLine,
  _In_ int       nCmdShow
);

This definition needs to be translated from C into assembly language. The HINSTANCE, LPSTR and int data types all reduce down to DWORD easily, so:

WinMain proc 	hInst 		:dword, 
				hPrevInst 	:dword,
				szCmdLine 	:dword,
				nShowCmd 	:dword

Being at the assembly layer, we’re exposed a little earlier in the process than what WinMain affords us, so we have extra work to do in terms of getting our application running. We as the implementers of the program need to invoke WinMain directly as this is not done for us.

invoke 	GetModuleHandle, NULL
mov		hInstance, eax

invoke	GetCommandLine
mov		lpszCmdLine, eax

invoke 	WinMain, hInstance, NULL, lpszCmdLine, SW_SHOWDEFAULT
invoke	ExitProcess, eax

GetModuleHandle allows us to fill the hInstance parameter and GetCommandLine gives us lpszCmdLine. hPrevInstance, according to the documentation is always NULL and nShowCmd is an SW_ series value that controls how our main application window is shown.

Window creation

The creation of a window is broken down into a few smaller steps:

  • Register a window class
  • Create the window
  • Show the window

Registering a window class is just filling out the WNDCLASSEX structure and calling RegisterClassEx. Registering a windows class establishes base or common attributes about a window that you can re-use in subsequent calls to CreateWindow.

local 	wc 		:WNDCLASSEX
local 	msg 	:MSG
local 	hWnd 	:HWND

szText	szClassName, "BasicWindow"
szText	szWindowTitle, "First Window"

mov		wc.cbSize, sizeof WNDCLASSEX
mov		wc.style, CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
mov 	wc.lpfnWndProc, WndProc
mov 	wc.cbClsExtra, NULL
mov		wc.cbWndExtra, NULL

push	hInst
pop 	wc.hInstance

mov		wc.hbrBackground, COLOR_BTNFACE + 1
mov		wc.lpszMenuName, NULL
mov 	wc.lpszClassName, offset szClassName

invoke	LoadIcon, hInst, IDI_APPLICATION
mov		wc.hIcon, eax
mov		wc.hIconSm, eax

invoke	LoadCursor, hInst, IDC_ARROW
mov		wc.hCursor, eax

invoke	RegisterClassEx, addr wc

The macro szText is setup to allow you to ad-hoc define string variables where ever you need to. The rest of this is mainly filling out the structure and finally registering it. Setting lpfnWndProc has special significance to us here as we’ll go on to describe WNDPROC and its role in the application.

WindowProc callback function

When an application receives messages from the operating system, it handles this information through the window procedure. The window procedure interface is defined as:

LRESULT CALLBACK WindowProc(
  _In_ HWND   hwnd,
  _In_ UINT   uMsg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);

Again, translating this into assembly:

WndProc proc 	hWin 	:dword,
				uMsg 	:dword,
				wParam 	:dword,
				lParam 	:dword

The message pump

Finally, the thing that’s keeping our application alive is the message pump. It’s a loop of GetMessage, TranslateMessage and DispatchMessage.

MessagePump:

	invoke 	GetMessage, addr msg, NULL, 0, 0

	cmp 	eax, 0
	je 		MessagePumpEnd

	invoke	TranslateMessage, addr msg
	invoke	DispatchMessage, addr msg

	jmp 	MessagePump

MessagePumpEnd:

All together now

The following gist is all of the pieces assembled in a basic application that’s runnable (once assembled and linked).