Back to ACM Student Chapter Newsletters

Welcome to the Student Chapter of the ACM
'C' Programming in windows 95
Misplaced Capitalism on the Internet
The GMR Effect
The Y2K Problem - Mission Improbable
The Internet Has Changed Our Lives

'C' Programming In Windows 95

by Chaim Mintz
Windows: What Is It? Window Messages
Brief History of Windows Windows and Controls
Win 16 vs. Win 32 Handles
The Windows Api Device contexts
Dynamic Link Libraries Hello World!
Event Driven Programming Where To go From Here

Windows: What is it?

The first step in learning how to program Windows is to get familiar with its history, interface architecture, and terminology. Windows began as a front-end GUI for DOS. DOS was text based, and only advanced users were able to use computers. However, because Windows is graphics oriented, the user no longer had to be as proficient with computers to be able to use them. Because more and more people were able to use computers and the prices of home computers were becoming affordable for the average person, computers started becoming more and more common in the home. Right now, Windows is installed in over 90% of home computers. It is almost essential for a programmer to know how to program in Windows to be considered for a job. This article will provide an introduction to Windows programming in "C". Because this article deals mostly with the Windows API, and it is assumed that the reader has a working knowledge of "C" in DOS, UNIX, or some other platform, a discussion of how to program in "C" will not be made.

top of this page

A Brief History of Windows

The first version of Windows was released in the early 1980's. At the time, Windows required only 256k and two floppy disks. Obviously, in those days, PC's were only equipped with a maximum of 640k, so 256k seemed like a lot. Both, the code for Windows and any running applications had to fit in those 256k. Of course, if the computer had the luxury of 640k, Windows took full advantage of it. When the 286 came out, it introduced a new mode called Protected Mode. It allowed the computer to access 24 megabytes of RAM. When the 386 came out, it revolutionized processors. The 386 registers were 32 bits wide, and in protected mode, it had the capacity to use four gigabytes of RAM. Windows was ported to protected mode in version 3.0. Now, Windows was a protected mode multitasking pseudo-operating system. When the joint venture between Microsoft and IBM to produce OS2 failed, Windows was the only graphical multitasking operating system on the market for PCs. When computers became more common in homes, most of the computers had Windows on it. In 1995, Microsoft took another big step in making computers easier and more reliable by porting Windows to 32-bit code, making it a complete operating system which ran without DOS, and revamped the entire interface. When they released Windows 95, it was an immediate hit.

top of this page

Win16 VS. Win32

When Windows was created, registers and busses were only 16 bits wide, and all programming was done in 16-bit code. From the 386 and on, the capability of 32-big coding existed. One of the main problems is calling 32-bit functions from 16-bit code. There are ways to do so, but I don't advise you to start looking into it. Because Windows 95 is written in 32-bit code, and Windows 3.1 is written in 16-bit code, Microsoft had to include the Windows 3.1 DLL's in Windows 95 to allow 16 bit programs to function correctly. Because of this, there are two Windows APIs: Win16 and Win32. The Win32 API is also included in Windows NT, and has hundreds of more functions than the Win16 API. This paper only discusses the Win32 API, and whenever it says the Windows API, it refers to the Win32 API. Also, whenever, something is mentioned about Windows 95, it is always applicable to Windows NT 4 and Windows 98, unless stated otherwise.

top of this page

The Windows API

Windows has over 1500 functions built into it. These functions are called the Windows API. These functions are the core of windows, and it allows the application programmer to harness the power of Windows without having to program the logic behind creating a window, or drawing to it. Some of the things the Windows API does are create windows, send messages to windows, destroy windows, and draw to the screen. The Windows API also has over 6000 constants (#DEFINE) and over 2400 structures that are used with the API functions. Most of the functions called in Windows programs are from the API, and some API functions internally call other API functions. Having a thorough understanding of the important API functions and structures, and knowing how to interact with Windows are the most important aspects of Window programming.

top of this page

Dynamic Link Libraries

The way Windows was able to fit so much code in that 256k when it was first released was through the use of Dynamic Link Libraries, or DLL's. DLL's are libraries of functions that can be loaded when needed and freed from memory when they are not needed anymore. The whole Windows API is contained in DLL's, so it may not all be in memory whenever Windows is running. Three major pieces of Windows are USER32.DLL, KERNEL32.DLL and GDI32.DLL. Note that not all DLL's have a DLL extension; in fact these 3 DLL's used to have an EXE extension in Windows 3.1. By putting a lot of the code in DLL's, it allowed programs to be bigger than the available memory, and it also allowed different programs to share the same functions by loading that DLL, thus saving disk space and memory space. Another advantage of DLL's is that if a programmer codes a new nifty algorithm, if the code is in a DLL, only the DLL needs to be replaced, and as long it maintains downward compatibility, all of the other programs can take advantage of the new DLL. Finally, another key advantage of DLL's is that because the DLL does not have to exist at compile time, it can allow an application to have plug-ins. For example, when Microsoft Word opens a document, it has a DLL for each readable document, and when you try to open the file, it calls on the right DLL to parse it. So if all of the sudden a new word processing program enters the market and Microsoft wants Word to be able to import from it, it can just make a new DLL and tell Word that for those kinds of files, call this new DLL. Users who would want to import from this new format would go to Microsoft's web site and download this DLL. DLL's can also store resources in them, such as bitmaps and string tables. Of course, the Windows API provides the functions for loading, unloading, reading the resources, and calling functions in DLL's. However, a down side of DLL's is that a misbehaving program can overwrite a shared DLL with an older version, or a user that is cleaning up his hard drive can erase a shared DLL, which can really mess up a computer.

top of this page

Event Driven Programming

In DOS, since your program is the only program running, it can get exclusive access to the keyboard, video card, mouse, and any other I/O device. However, Windows is a multitasking system, and at any given time more than one application can be running. Each program must cooperate with Windows and allow other programs to use the shared hardware. For this reason, the developers of Windows chose to use an "event driven" architecture. This means that we just create the main window, sit back, and wait for Windows to inform us of anything which may be of interest. The skeleton "C" program in Windows consists of the WinMain procedure (a Windows substitute of the main procedure.) The WinMain procedure creates the application's main window, and enters its message-processing loop. When the user wants to close the window, the application should posts the quit message, which in turn exits the message loop and terminate the application. This event driven architecture allows many applications share access to the hardware, because the window only gets the Windows messages if the mouse is over that window, or if it is active when the keyboard is pressed.

top of this page

Window Messages

When we create our main window, we must tell Windows which procedure gets all of it's messages (called a WNDPROC), and that procedure usually has a big switch() statement and acts according to each message. Although there are over 150 different messages Windows could send an application, only about 15 or so are essential to an application. For any action on the computer that the folks at Microsoft thought might be of interest to the application, Windows sends a message to the window. The programmer must respond to those messages or let Windows handle it. All messages related to child windows (controls) are also sent to the main window. The typical procedure to handle the messages received are of four parameters: hwnd, which is the handle of the windows that received the message; iMsg, which is the message number; wParam and lParam which are message specific parameters. The message number is the most important of these, because it holds the number of the messages about which Windows is trying to let us know. Instead of trying to know all the integers that correspond to the message types, there are a whole bunch of #define lines in windows.h that allow us to use mnemonics instead. WM_KEYDOWN is easier to remember than 0x100. The first part of the message identifier is what the message relates to. All Window Messages start with WM. The second part is what the message is for, in this case to tell us that a key was pressed when our window had the focus. When we create an application, we first must list which message we care about, and the rest we let windows handle. The messages that most Windows applications handle are WM_CREATE for initialization code, WM_PAINT to paint the window whenever Windows asks us to, WM_DESTROY to close the application when the user closes the window, WM_KEYDOWN when the user presses any key when our window has the focus, and WM_LBUTTONDOWN when the user clicks on our window. When we receive a message that we are not interested in we just pass it along to DefWindowProc, and Windows will do the default action for that message.

top of this page

Windows and Controls

Now that the application has created the main window, it can draw directly onto it. However, most applications will need a menu bar and some controls on the window, such as a toolbar, a status bar, or a textbox. Windows comes with a library of controls called the Windows Common Controls. There were eight built in Windows controls in Windows 3.1 (check boxes, combo boxes, edit boxes, list boxes, option buttons, pushbuttons, scroll bar controls, and static controls), and Windows 95 introduced eighteen more (animation, date/time picker, header, hot-key, imagelist, listview, calendar, progress bar, rebar, rich-edit box, slider, spinner button, status bar, tabbed sheets, toolbars, tooltips, and the treeview). Some of the new controls in Windows 95 may require Internet Explorer 4. It is also possible for a programmer to create his own controls. These controls are treated just like a regular window in respect to creating them, drawing on them, and accessing their contents, but their messages are still sent to the parent window's WNDPROC. Controls are also sometimes referred to as child window.

top of this page

Handles

The way Windows uniquely identifies each window is through a Windows Handle, or HWND for short. A HWND is just a typedef to an unsigned integer. Each control is also identified with an HWND. Handles are also used for other things, such as fonts, pens, and brushes. Whenever you see the term HWND, it means handles to windows or controls. The HWND is returned when you create a window via the CreateWindow() function, and because functions that operate on a window expect to be passed an HWND as a parameter, it is a good idea to save that HWND into a variable that can be accessed by most of your program.

top of this page

Device Contexts

In order to draw anything to the surface of a window, you first must obtain a handle to the device context (HDC) for that window. You can create Device Context for any window by calling the CreateDC(HWND) function and pass it the handle of the window. All drawing functions need that HDC to be passed, and when you finish drawing, you release the HDC by calling the ReleaseDC(HDC) function. You can also obtain the HDC if you call the BeginPaint function when you start painting the window in response to the WM_PAINT message, but you must call EndPaint when you are done.

top of this page

Hello World!

I will now present you with customary "Hello World!" program, and I will explain what each line does. Try to follow line by line:

#include 


LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstanc, LPSTR lpCmdLine, int iCmdShow)
{
	HWND hwnd;
	MSG msg;
	WNDCLASSEX wndclass;

	wndclass.cbSize = 
        sizeof (wndclass);
	wndclass.style = CS_HREDRAW |    
        CS_VREDRAW;
	wndclass.lpfnWndProc =   
        WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = 
        hInstance;
	wndclass.hIcon = 
        LoadIcon (NULL, 
        IDI_APPLICATION);
	wndclass.hCursor = 
        LoadCursor (NULL, 
        IDC_ARROW);
	wndclass.hbrBackground = 
        (HBRUSH) GetStockObject
        (WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = 
        "HellowWorld";
	wndclass.hIconSm = 
        LoadIcon (NULL, 
        IDI_APPLICATION);

	RegisterClassEx(&wndclass);

        hwnd = 
        CreateWindow("HelloWorld", 
        "Hello World Program",
        WS_OVERLAPPEDWINDOW, 
        CW_USEDEFAULT, CW_USEDEFAULT, 
        CW_USEDEFAULT, CW_USEDEFAULT, 
        NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while(GetMessage(&msg, NULL, 
        0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;

	switch (iMsg)
	{
	case WM_PAINT:
		hdc = BeginPaint(hwnd, 
             &ps);

		TextOut(hdc, 0, 0,  
              "Hello World!", 12);

		EndPaint(hwnd, &ps);
		return 0;
	case WM_CHAR:
        case WM_LBUTTONDOWN:
              MessageBox (hwnd, "You 
              Clicked me!", "Hello 
              World", 
              MB_ICONEXCLAMATION);
              RETURN 0;
       case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
       }
       return DefWindowProc(hwnd,  
       iMsg, wParam, lParam);
}

The WinMain procedure creates the application's main window and enters its message - processing loop. The first step is to register the window class, which is the first parameter in the Create Window function. We fill up the WNDCLASSEX structure to reflect the type of window we want. Wndclass.lpfnWndProc = WndProc tells Windows that it should send all messages to the WndProc function. We use Windows predefined icons and cursors by calling the LoadIcon and LoadCursor functions with NULL as the first parameter. When we register a window class, we can create many windows of this type by just specifying the class, so we do not have to specify the features of this window every time we want to create it. We call this new window class "Hello World", and it is a standard, no-frills window. We call the CreateWindow function, and it returns us the HWND, which we save in the hwnd variable. The first parameter in CreateWindow is the window class, so we put in "Hello World". The second parameter is the caption of the new window, which we specify to be "Hello World Program". We then make the window visible according to the parameter that we get in the WinMain that tells how Windows wants our application to start out, minimized, maximized, etc. Next, we call UpdateWindow to send the WM_PAINT message to our window, so it will paint itself. Finally, we start our message - processing loop. The GetMessage function waits until any message is put into our message queue. Until there is a message, our application's process is blocked. When there is a message, it puts the message information into the MSG structure. It returns a 0 if the message is a quit message, so it will break out of the loop; otherwise, it will return a 1. If it returns a 1, we call TranslateMessage and DispatchMessage, which will make sure our WndProc is called. In our WndProc, we have a switch statement that looks at iMsg. If it is a WM_PAINT message, it will call BeginPaint to obtain the HDC, then it will call TextOut to print our message on the screen, and then EndPaint to let Windows know that we finished painting. If the message is WM_LEFTBUTTONDOWN (left mouse click) or WM_CHAR (keyboard press), a messagebox will come up. If it is a WM_DESTROY message, it means that the user closed the window (by clicking on the X on the upper right, double clicking on the application icon on the upper right, or pressing Alt+F4), so we call PostQuitMessage, which will cause GetMessage to return 0 and in turn cause the application to terminate.

To build this program, start up Visual C++, and from the File menu, choose New, and from the projects tab, choose Win32 Application. Type in a name of the project and press OK. Choose to create an empty Win32 Project. When it finished creating the new project, click Add to Project, New... from the Project menu, pick C/C++ source file, and press OK. Type in the above program into the edit window, and save the project. Click on the Execute Program icon, or press Control+F5. The project should compile and link without any errors, and the window should come up with the words "Hello World" on it. Notice how Windows supplied the window, the icons, and the mouse cursors without us having to type in too much code. Such a program in DOS can easily be over a thousand lines of code!

top of this page

Where To Go From Here

Obviously, reading this paper will not make you into a Windows programmer; it can take you over six months to be comfortable with programming Windows. The best places to help you learn more are the Internet, and books. The book I recommend most is "Programming Windows", by Charles Petzold, published by Microsoft Press. Petzold's book is the standard for learning the Win32 API, and it covers essential topics such as controls, input, menus, clipboard, and essential graphics. It also touches on some advanced subjects such as secondary windows, memory management, multithreading, DLLs, and COM. The best reference for any Windows programmer is the MSDN. The MSDN is a library of documentation put out by the folks at Microsoft. It comes with any of the tools in the Microsoft Visual Studio. It has a reference to the whole Windows API, technical articles from the people at Microsoft, the knowledge base, and some books. Microsoft puts out updates of it for a subscription fee, but any copy should be good enough to cover the fundamentals. Your goal should not be to memorize every single function in the Windows API. Rather, once you understand the event driven structure and what Windows is capable of doing, look in your references for the proper functions that you will need. Look at the source code of other Windows programs, and try to dissect it, line by line. Even if you never plan on programming in Windows in "C", you will still need to know the Windows API. Creating advanced programs in Visual Basic almost always requires you to rely on the Windows API, which is why most high-level languages now allow the programmer to call the API directly. Only when you learn the Windows API will you feel that you control Windows.

top of this page