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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
I will now present you with customary "Hello World!" program, and I will explain what each line does. Try to follow line by line:
#includeLRESULT 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!
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.