1 Introduction to Windows Concepts

What Is Windows?


Figure 1.1

Microsoft Windows is a graphical environment. The early versions of Windows, those prior to 3.1, ran on an Intel 8088, 8086, 80286, 80386, or 80486 processor and at least 640K of conventional memory and as little as 2MB of total memory. Windows 3.1 required at least an 80286 processor and 4MB of total memory. Windows 95 in its initial release requires an 80386 or higher processor with at least 4MB of memory. The earlier versions of Windows ran on MS-DOS or PC-DOS version 3.1 or later; Windows 95 contains its own underlying support and will not run on any other version of DOS. Windows NT is a complete operating system without any underlying DOS (although it emulates a significant fraction of DOS, there is no actual DOS present) and requires an 80386 or higher processor with at least 16MB of memory. Both Windows 95 and Windows NT represent a new and significant improvement over the older versions of Windows (3.1 and Windows for Workgroups 3.11) in that they support programs that use the native 32-bit instruction set of the computers. This makes programming simpler and programs run faster.

Windows provides a multitasking graphical windowing environment that runs multiple applications simultaneously. Each application displays all output in a rectangular area of the computer screen called a window. The entire computer screen is referred to as the desktop. A user can arrange windows on the desktop in a manner similar to placing pieces of paper on an actual desk. For example, you can run two programs in adjacent windows while other programs are temporarily set aside, as shown in Figure 1.1. All the programs in the figure are running, even those set aside as icons. Although most applications are idle when the user isn't interacting with them, some applications may be working even when iconic. For example, a spreadsheet application might continue to recalculate the values of a spreadsheet while iconic. A communications program, email reader, or other type of communication program may continue to read modem or network messages and process them even when it is iconic.

For you, the programmer, Windows provides a rich programming environment that supplies extensive support for developing easy-to-use and consistent user interfaces. Menus, dialog boxes, list boxes, scroll bars, push buttons, and other components of a user interface are supplied to the developer by Windows. Windows provides device-independent graphics, thereby allowing you to write programs without having detailed knowledge of the hardware platform on which they will eventually run. This device independence extends to the basic hardware devices on a personal computer. A programmer also can access the keyboard, mouse, printer, system timer, and serial communication ports in a device-independent manner.

What Is Win32?

Win32 is a family of Windows programming interfaces. It is the standard programming interface for Windows 95 and Windows NT. The Win32 Application Program Interface (API) is specified in terms of 32-bit values, rather than the 16-bit values used to program Windows 3.x and earlier versions of Windows. In addition to the performance improvements that result from the larger integer size, the use of 32-bit addresses greatly simplifies that task of programming. Programmers work in a "flat" 32-bit address space, instead of the 16-bit "segmented" addresses of the older Windows versions. The Win32 API also includes many new functions. For example, the Win95 API supports graphical operations such as Bézier curves and the use of (restricted) graphical transformation matrices, and the kernel allows you to create powerful and flexible programs by using a capability called multithreading. The Windows NT API supports everything of the Win95 API as well as more powerful graphics capabilities (for example, graphics coordinates greater than 65,536 units and fully general transformation matrices) and sophisticated file protection and sharing. Windows NT also supports multiprocessor systems, that is, systems in which there are two or more processors (CPUs) sharing the same physical memory.

Both the Win95 and Windows NT APIs support powerful controls, such as the Rich Text Control, that allow you to construct much nicer user interfaces quickly. Like the "common dialogs" introduced so successfully into Windows 3.1, the Win32 "common controls" make it easy to standardize the "look and feel" of your applications.

Win32 also supports a style of control called the "OCX" control. OCX controls are implemented using OLE Automation. Unlike VBX controls, which could only operate under Win16, you can build OCX controls in both 16- and 32-bit versions. Using OCX controls allows you to readily integrate new kinds of controls into your application.

There is even a version of Win32, called Win32s, which allows you to deliver 32-bit programs that run on Windows 3.1 or Windows for Workgroups 3.11, although the interest in these platforms is diminishing rapidly.

While a full comparison of Windows 95, Windows NT, and Win32s is outside the scope of this book, we will point out significant and important differences in these APIs throughout the book.

An Historical Perspective on User Interfaces

Early "personal" computing was often characterized by computers that had toggle switches and lights.1 To load a program into a computer, a user set a number of toggle switches in a proper pattern, then pressed a button to load one character, byte, or word into the computer's memory. Needless to say, this method was very slow and extremely error-prone. Output from the computer was often displayed in binary on a panel of lights.

A major improvement came about with the introduction of punched paper tape, punched cards, and line printers. After the tape or card was punched correctly, programs could at least be loaded more accurately and output produced in a readable form. Barring events such as a dropped deck of punched cards or paper jams in the printer, interacting with a computer became much less tedious and error-prone.

Still, interacting with the computer was often difficult. Card decks of programs often were saved until enough work had accumulated to run them in batches. Having a particular job run through the computer more than once a day was an event worth celebrating. The introduction of Teletypes (TTYs) and, later, CRT ("Cathode Ray Tube") or VDT ("Video Display Terminals") terminals (called "glass TTYs" at the time) enabled a user to interact directly with a computer, often for the first time. Such access was pioneered in the 1960s by such systems as MIT's CTSS ("Compatible Time-Sharing System") and MULTICS (MULTIplexed Computer System), among many others. Video terminals came into common usage in the late 1970s, and graphical displays, dating back to the mid-1960s, became economically feasible for widespread usage in the early 1980s.2

Initially, display terminals were treated as nothing more than printing terminals at which you could see only the last 24 lines or so of what had been printed. In fact, the DOS operating system on IBM personal computers or compatible machines still treats the display in this fashion. Often when you ask for a directory listing, it will flash right on by. You get to see only the last few lines of a long directory listing.

As part of the continuing quest for better user interfaces, the computer display was used as a randomly accessible device rather than as a sequential line-by-line device such as a printing terminal. Programs started displaying forms on the screen and accepting input from input fields. Programmers were still limited in a number of ways, however. It was commonly assumed that a display consisted of 24 lines by 80 columns. The effects of such assumptions are still with us today. The text display of the first IBM personal computer was a 25-line by 80-column display. Programs that were designed for a 24-line by 80-column display were expected to be easily be converted to run on the personal computer. As a bonus, that twenty-fifth line was available for PC-specific versions of the programs. Displays such as the Color Graphics Adapter (CGA), Enhanced Graphics Adapter (EGA), Video Graphics Array (VGA), super VGA, and 8514 adapters can display graphics as well as text with varying numbers of lines and columns. Some older programs work oddly with such displays, often using only the first 25 lines and the left-most 80 columns. Using the graphics modes of such displays was even more difficult for a programmer to deal with. Built-in support for graphics was practically nonexistent, so each programmer had to reinvent the graphical wheel. And after a program was working correctly on a particular graphical display, a new wheel had to be painstakingly carved from stone to get the program running on a different display. This process was not aided by the fact that many display cards that claimed they were "compatible" with the "standard" were not, nor were they compatible with each other.

Microsoft Windows was announced in November 1983 as a solution to these problems. It was released in November 1985 as Windows version 1.01. A number of small revisions to Windows were released over the next two years. During this time, several other software manufacturers released software products that ran programs in graphical or windowing environments. Most of these are now history.

With the introduction of Windows 2.0 in November 1987, Windows applications switched from the "tiled" windows of the 1.x versions to the "overlapped" windows used by all later versions of Windows. This and other changes in the appearance and interaction of the user interface for Windows 2.0 were made to be consistent with the user interface of the OS/2 Presentation Manager.

Windows 3.0 was introduced in May 1990 and brought many significant additions to the windowing environment, both for the user and for the developer. From the first look at the display, the user noticed a distinct improvement in the appearance of the desktop over the earlier versions. Many of the icons and buttons had a "three-dimensional" look. Proportionally spaced type was used in the menus and dialog boxes. Windows 3.0 was no longer limited by the 640K boundary and could use extended memory. This is the memory above the one-megabyte boundary available on Intel 80286 and more powerful processors. Multiple, complex applications could now run simultaneously side-by-side on a color graphical display. User interfaces had come a long way.

Windows 3.1 was introduced in April 1992 and further improved the Microsoft Windows environment. Windows 3.1 was easier for a user to install and configure. It included a tutorial that taught new users how to use Windows productively from the beginning. From a programming viewpoint, many of the detailed concepts of memory management that made Windows programming so difficult in 3.0 and earlier became nearly obsolete; programmers could and did write programs that could consider allocating a megabyte bitmap.

Windows 3.1 introduced such concepts as the Common Dialog Boxes. Common Dialog Boxes, such as Open, SaveAs, Font selection, Color Selection, Communications, Print Setup and Print, now look the same throughout the system. You can use these same common dialogs in your own Windows applications. When you use the common dialogs for common functions, you make your application familiar to a new user. A user finds that your application performs the same function as another application (saving a file, printing a document, etc.) using the same user interface.

The Windows 3.1 File Manager was much faster than all previous versions and allowed the user to drag files from the File Manager and drop them onto other applications. This concept, difficult to program in Windows 3.1, is pervasive and easy to program in Win32. You can make your Win32 application drag-and-drop aware so that a user can drop files on it, if appropriate.

Windows 3.1 introduced TrueType scalable fonts. Using TrueType fonts allows your application to display good-looking output on your screen and print to any type of printer using the best resolution the printer is capable of. TrueType fonts are scalable, which means you or the user can choose any font size without the user's having to store separate bitmap files for every size of every font. TrueType is supported in all Win32 interfaces.

Windows 3.1 also introduced a new subsystem, Win32s, a 32-bit operating environment that runs on the 16-bit Windows 3.1 platform. Win32s is a subset of Windows 95, and suffers from many of the limitations of the underlying 16-bit platform, including the critical resource limitations in the "User" and "GDI" modules. The lack of adequate development tools, and serious bugs in the early versions, limited the usefulness of Win32s. However, significant new releases of Win32s have enabled developers with 32-bit compilers to develop 32-bit applications that run on the 16-bit platform. However, some important applications such as the Office95 Suite and the Visual C++ environment will not run on Win32s. Writing a program that runs on Win32s requires great care and awareness of the 16-bit limitations.

Windows for Workgroups version 3.1 was introduced in November of 1992. Basically identical to Windows 3.1, it added network support. Most of the differences between the two are in the modules that provide the network interfacing and the file and printer sharing capabilities. Windows for Workgroups 3.11, a revision, was introduced in December 1993.

Windows NT version 3.1 was introduced in August 1993. Windows NT is a complete operating system written from the ground up. It supports preemptive multitasking, which means that if one program hangs you do not have to reboot the entire system. Nor are you locked out from using other applications while a long, complicated operation consumes hours of computer time. All earlier versions of Windows depended on cooperative multitasking, in which you, the programmer, had to explicitly release control during long computations. Much folklore surrounded the correct ways of doing this, particularly when it was discovered that the traditional ways of doing this interfered with laptop "power management" software. You no longer have to worry about releasing control; when appropriate, your control will be preempted by another task. A task in Windows NT also runs in a completely separate address space, making it impossible for it to damage any other running task, or the operating system itself, or to be damaged by an errant task.

Windows NT also introduced powerful new graphics capabilities, such as Bézier curves, paths, and transformation matrices, among other features. Further, it supports security and protection, thereby allowing you to grant or restrict permission to your files, and meets DoD C2 security requirements. Windows NT can run on multiprocessor systems, in which two or more processors share the same memory. It assumes the existence of a network and has built-in peer-to-peer and client/server networking. And it's is portable. It is implemented on a variety of 32-bit platforms, including not only the traditional Intel platforms (386, 486, Pentium, and Pentium Pro), but also RISC-based platforms such as MIPS R4000, the Digital Alpha, and the PowerPC. Each of these platforms presents the same API. If you do not write platform-specific code in your application, it should compile and run on all of these platforms.

Windows NT Advanced Server was released in July 1993. A version of Windows NT, Windows NT Server was optimized to be a file server. It can support products such as the SQL Server for doing client/server database systems. It can also serve as a central file system, thereby enabling a tape backup program running on a Windows NT server to back up all the workstations on the network.

Windows NT Server 3.5 was introduced in September 1994. Windows NT Workstation 3.5 was introduced in October 1994. This was followed shortly by the release of Windows NT Server 3.51 in May 1995 and Windows NT Server 3.51 following in July 1995. From the API viewpoint, these represent minor enhancements and bug fixes to the original Windows NT API.

Windows NT did have one significant drawback: It required substantial resources to operate. While the 16-bit Windows 3.1 could operate on a low-end 386 processor with as little as 2MB of memory, the first release of Windows NT 3.1 required a minimum of 16MB of memory. The minimum requirement for Windows NT 3.5 and 3.51 is 12MB, but some applications require more. Programming environments such as Visual C++ recommend a minimum of 20MB of memory, with 32MB preferred. These requirements put Windows NT out of reach as a desktop environment for thousands of users who did not have the ability to upgrade to larger machines. Windows 95, code-named "Chicago" during its early development, was the response to this.

Windows 95, released in August 1995, is a hybrid of a 32-bit environment running on a largely 16-bit substrate. It implements a subset of the Win32 API. Although not portable-much of it is still written in assembly language-it is unlike Windows NT in that it runs in as little as 4MB of memory (although serious developers will want at least 16MB to run the 32-bit development environments). Windows 95 supports preemptive multitasking, multithreading, and separate address spaces for tasks, much like Windows NT, but there is only a moderate amount of protection between address spaces and virtually none on the file system. Windows 95 introduced new standard controls, which are also available on Windows NT, and a replacement for the old "Program Manager" interface; this new interface is also available for Windows NT. And like Windows NT, it is "network-aware" and absorbs all the functionality of Windows for Workgroups. Many of the advanced graphics features of Windows NT, such as paths and Bézier curves, also were implemented in Windows 95.

Windows 95 is not actually a subset of the Win32 interface of Windows NT 3.x; it is a subset of the "Level 4" Win32 interface. This interface contains significant new API functionality not available in Windows NT 3.x, but which is available in Windows NT 4.x. The lack of these functions in Windows NT 3.x can cause you problems; we try to point out the differences as often as we can.

Windows 95 also includes some capabilities not available on Windows NT 3.x, such as telephony and modem support via the Telephony API (TAPI).

Both Windows 95 and Windows NT will continue to evolve. Indeed, Windows 95 is an evolutionary step for most users, allowing them to run 32-bit applications on more modest hardware. Windows NT is recommended for heavy-duty enterprise computing, particularly where security of information is involved.

Windows NT Workstation 4.0 and Windows NT Server 4.0 were introduced in August 1996. These releases implement the full 4.x level API, thereby making the Windows 95 API a subset of the Windows NT 4.0 API. With only a moderate amount of care, you can write applications that run on both Windows 95 and Windows NT 4.x. They need only test for the proper execution of functions not implemented on Windows 95.

Shortly before the release of Windows 95, Microsoft announced that Windows NT 4.0 would require only 10MB of memory, while the next release of Windows 95 would be targeted for 8MB of memory. We are suspicious of these small numbers, since we have used Windows 95, Windows NT 3.51, and Windows NT 4.0. The changes in memory cost render most of these "small machine" arguments moot. As a developer, you should expect to require at least 32MB. Memory cost, once seen as the significant barrier to the adoption of Windows NT, is no longer a significant factor. Memory prices dropped by a factor of more than 25 between the introduction of Windows NT 3.1 and Windows NT 4.0.

Microsoft is heavily committed to the support and evolution of the Win32 API. As new application domains evolve, including so-called "palmtop" devices, "Personal Digital Assistants" (PDAs), and even embedded systems, Microsoft is looking at the Win32 API as the definitive interface to the underlying facilities on all these systems.

The subject of this book is developing 32-bit Windows applications. Windows applications can broadly be placed in four classes:

1. Those that must run under Win32s

2. Those that must run on either Windows 95 or Windows NT

3. Those that require Windows 95

4. Those that require Windows NT

You can also add additional limitations, such as requiring Windows NT 4.x instead of Windows NT 3.x. Some applications require Windows 95 or Windows NT 4.x and will not run on the 3.x level systems. Some that were hastily converted from 16-bit Windows still require 16-bit interface code that can run only on Windows 95. And others require device drivers that have not yet been converted to Windows NT but which still run on Windows 95.

Because Win32s is at best a stopgap measure during the transition to Windows 95 and Windows NT, we touch only briefly on a few of the constraints you must obey if you want your program to run under Win32s. We also only briefly discuss those differences between Windows 95 and Windows NT that affect the GUI; a complete discussion of multithreading or security is beyond what we can hope to cover in depth in this book. However, since this book is also intended for Windows 3.x programmers who want to start writing in Win32, as well as those coming to Windows programming for the first time, we point out significant differences between the Win16 and Win32 API, particularly as they affect porting existing 16-bit applications to Win32 or, even more important, porting your knowledge of 16-bit programming to Win32. This book teaches 32-bit Windows programming using techniques that work under Windows 95 and Windows NT. All example programs in this book run properly under both, with two qualifications. An example program that calls a Windows NT-specific function obviously won't work well under Windows 95, which doesn't have that function. Careful programming is required so that an application that relies on a Windows NT capability, such as transformation matrices or file security, will run without that capability under Windows 95. In fact, Microsoft requires this as part of their "logo certification" for 32-bit applications: The application must run on both Windows 95 and Windows NT, even if it can use Windows NT facilities not available on Windows 95.

Differences between a Windows Program and a Typical DOS or Unix Program


Figure 1.2

There are a number of differences between a Windows program and a typical DOS program. Windows applications must share system resources; DOS applications generally don't need to share. Windows applications produce graphical output; DOS applications generally produce text displays. Windows applications also accept input and manage memory quite differently from DOS applications. Windows applications do not require detailed hardware knowledge, whereas DOS applications often work on only one type of device.

Resource Sharing

Windows applications are designed from the start to share the resources of the system on which they run. Resources such as main memory, the processor, the display, the keyboard, hard disks, and diskette drives are shared by all applications running under Windows. To accomplish this sharing, a Windows application must interact with the resources of the computer only through Windows' Application Program Interface (API). Accessing the resources only through the Windows API enables Windows to control those resources. This restriction enables multiple programs to run concurrently in the Windows environment.

By contrast, DOS applications typically expect all the resources of the system to be available to the application. A DOS application can allocate all available memory without concern for other applications. It can directly manipulate the serial and parallel ports. It can even intercept the keyboard interrupts and handle key down/key up transitions directly. DOS applications do not need to be designed to share system resources.

Unix programmers, on the other hand, will see nothing new about the Win32 operating environment in this regard. Like Unix, the Win32 memory environment is based on the notion of a private address spaces.

Graphical User Interface

Windows applications typically run with a graphical user interface (GUI)3. Because multiple applications may be running concurrently, each application is given access to the graphical display through a "window". It is through this window that an application interacts with the user. Windows applications are not restricted to one window per application. A Windows application may have more than one window on the graphical display simultaneously.

Windows also provides a rich set of input controls as part of the graphical user interface. A few of these controls are dialog boxes, combo boxes, all types of buttons, menus, and scroll bars. Figure 1.2 shows a dialog box containing tab controls, push buttons, radio buttons, up-down ("spin") controls, edit controls, standard combo boxes, combo boxes containing graphics (done as "owner-draw" controls), and three custom controls. Dialog boxes appear on the screen to display information to the user and to request input. In other words, they provide a method for a programmer to "package" a conversation or dialog with the user. Push buttons, check boxes, and radio buttons allow the user to select true/false, on/off, enabled/disabled types of program options without the traditional requirement of a question-and-answer conversation between the user and the application. Menus can be displayed as necessary to enable the user to direct program actions. Scroll bars enable a user to indicate to an application what portions of a file, document, or other output display should be displayed in the current window.

What about Unix, X, Motif, OS/2, or the Macintosh?

Many programmers approaching Win32 for the first time are coming from a background of Unix, X Windows, Motif, OS/2, or even the Macintosh. Those coming from a traditional Unix "shell" background may encounter the same problems in paradigm shift that DOS programmers do: Interaction is quite different. Those coming from an X Windows, Motif, or Macintosh background will find few surprises. Details of the style differ and some of the naming conventions are different, so you have to learn new names for old ideas. There also are differences in the API calls. Those coming from an OS/2 Presentation Manager background will likewise find a strong similarity. The OS/2 PM is quite similar, in some ways, identical, to the Win32 API.

Input Facilities

User input is one of the greatest differences encountered by a programmer new to the Windows environment. Windows controls the input devices and distributes input from the devices among each of the multiple concurrent applications as required. A Windows application cannot read an input device such as the mouse or keyboard on demand. A Windows application is structured to accept input whenever it is produced by the user, rather than demand it when it is required by the application. If you come from a conventional DOS or Unix programming background, be aware that there is no getch() function.4

A Windows application is told whenever a user presses or releases a key. As the mouse cursor moves across an application's window, a stream of messages pours into the application, keeping it informed as to the mouse cursor's current location. When a mouse button is pressed or released, Windows notifies the application of the event and tells it where in the window the mouse cursor was pointing when the event occurred. While the user is selecting from a menu, checking a check box, pushing a push button, or scrolling a scroll bar, to name just a few examples, the application program receives a constant flow of messages updating it as to exactly what the user is doing.

This aspect of Windows applications is quite different from standard DOS or Unix shell applications. It requires a program that is structured quite differently from a standard DOS or Unix shell program. Windows applications are not necessarily more difficult to write than standard DOS or Unix shell programs. The structure is unfamiliar, and old habits and methods no longer work. However, after you master the new concepts involved in Windows programming, you will be able to write applications that are easier to use than many text-based DOS or Unix shell applications. The programming environments, particularly such rich libraries and tooling as the Microsoft Foundation Classes (MFC), in many ways make program construction easier than conventional DOS or Unix programming, simply because the tooling does the boring parts for you.

Memory Management

Memory is but another resource that all applications share in the Windows environment. Unlike a standard DOS program that often assumes that the entire computer's memory is available for its use, multiple Windows applications must share available memory. Instead of initially reserving all memory required by an application and holding it until the application terminates, a Windows application should allocate only the storage required at any given time and free it as soon as it is no longer needed.

Early versions of Windows required that you use elaborate memory management strategies designed to cope with the limitations of the 640K memory space of the 8088, and later with the segmented architecture of the 16-bit protected memory of the 286, 386, and higher Intel processors. Win32 programmers can generally use the familiar malloc() function of C or new of C++. There are a few exceptions that we point out where they occur.

Device-independent Graphics

Finally, Windows provides device-independent graphical operations. As soon as a Windows programmer can draw lines and circles on the graphical display, the same program will draw the figures correctly on all graphical displays supported by Windows. In fact, this device independence is not limited to displays. The same graphical operations that produce a pie chart on the display also will draw one on a dot-matrix printer, a laser printer, a plotter, or other output devices supported by Windows. And, unlike DOS, you need only one interface for printing. You almost never have to worry about whether the user has an FX-80 from the early days of PCs or a 600 dpi full-color laser printer.

The Windows Programming Model

Windows programming has the reputation of being difficult to learn. This reputation is partially due to the richness of the programming environment. The sheer number of Windows API functions can easily overwhelm you. There are two significant impediments to learning Windows programming:

1. Windows programs require a different conceptual model of the problem than is used by traditional DOS or Unix shell programs.

2. The tools used to write a program based on this conceptual model do not directly support the concepts used.

The Conceptual Model

Windows applications are programs that react to different forms of user input and provide sophisticated graphical output for the user. Any window displayed by an application must react to user actions. Menus must pop down and enable selections. Push buttons must depress and spring back when released. Check boxes must check and uncheck themselves. Objects in general in Windows must react when manipulated. This encourages a conceptual view of a Windows application as a collection of objects. This is exactly the view proposed by object-oriented programming.

The concept of object-oriented GUI programming originated in the mid-1970s at the Xerox Palo Alto Research Center (PARC) along with the concept of graphical programming environments. In object-oriented programming, a programmer creates abstract data types. These abstract data types are commonly called objects and consist of a data structure and associated functions, commonly called methods, that manipulate the data structure. Typically, the data structure of an object is completely unknown outside of the methods it provides to the outside world. This approach, called data encapsulation, enables the internal structure of the object to change at will. As long as the external interface provided by its methods remains unchanged, the rest of the program does not need to know what the object looks like internally or how it implements its functionality.

These concepts apply quite naturally to Windows applications. There are many objects in Windows, including these:

Windows and Their Associated Window Functions

As mentioned earlier, when a program creates a window, that window has certain characteristics-its location on the screen, a title, a size, a menu, and so on. Think of these as its physical characteristics. But it also has other characteristics: how it reacts to the notifications of various events. These are its behavioral characteristics. All windows have an associated function, called the window function, that determines how the window reacts to the notification of an event. The notification itself is called a message. For example, buttons and scroll bar controls, themselves contained within a window, are also windows. So they too have an associated window function that controls how the button or scroll bar control reacts. A scroll bar "window" reacts to a mouse click by visually changing the appearance of the window and sending a message to its parent window, which notifies the parent of the request to scroll.

We've stated that Windows notifies a window when an event occurs that might affect the window. This is usually described differently. That is, a message is sent to the window or, depending on your point of view, the window receives a message notifying it of the event. In actuality, Windows calls the function associated with the window, passing information in the arguments of the call that describe the event that has occurred. Windows, their associated window functions, and the messages they receive are, therefore, interrelated. This is illustrated in Figure 1.4 (unavailable), where three windows are shown. Two windows are of the same kind, called a window class, and one is a different kind. The two "class A" windows have a single associated function that handles their messages, and the "class B" window has a similar function that handles its messages.

Where do messages come from? What kinds of messages are there? How do you get them? Messages originate from many different sources. Although most messages originate from the Windows operating environment, an application may send messages from one window to another. A window may even send itself a message. A Windows application uses messages to tell itself to do something in the future. Later, when the message arrives, the application performs the desired action. An application may send a message to a completely different application. Often a message received from Windows will result in a deluge of spawned messages. What kinds of messages are there? Most messages are notifications of some external event. Something has happened, such as a mouse click or a key press. Such notifications are sent to a selected window (which window is a topic we cover in some depth). This window may in turn send notifications to other windows. Windows are also sent messages that are requests for information from the window. A message may

may ask a window what its caption is, or its status, or if it is willing to be destroyed. Finally, there are messages that tell a window to do something, for example, to change its caption, or its color, or some other feature of its appearance. How do you get them? Because delivering messages in the proper sequence to the proper destination is the key to Windows operations, we will look at how this is done in detail.

Windows Queues and the Message Loop

Many messages in Windows originate from devices. Depressing and releasing a key on the keyboard generates interrupts that are handled by the keyboard device driver. Moving the mouse and clicking the mouse buttons generate interrupts that are handled by the mouse device driver. These device drivers call Windows to translate the hardware event into a message. The resulting message is then placed into the Windows system queue.

There are two types of queues in Windows: system and thread. There is only one system queue. Hardware events that are converted into messages are placed into the system queue. Messages reside in the system queue only briefly. Each running Windows application has its own unique thread queue. (In Win32, each running application can have multiple threads of execution, and each thread that is processing messages has its own unique thread queue, but we haven't yet discussed multithreading in detail.) Windows transfers the messages in the system queue to the appropriate thread queue. Each of a program's thread queues holds all messages for all windows running in a particular thread. Consequently, within a thread all messages are processed in first-in-first-out (FIFO) order.
Programmers who know the Win16 architecture may remember that each application had its own private application queue and that there was a single system queue. However, this system queue was handled somewhat differently. The Win16 subsystem, running under Win32, simulates this system queue behavior with respect to interacting Win16 applications.

Windows implements the sharing of the shared resources (such as the mouse and the keyboard) by using the system queue. When an event occurs, a message is placed into the system queue. Windows then must, in effect, decide which thread queue should receive the message How it does this can vary, depending on the event. Windows uses the concept of the input focus to decide which thread queue should receive the message. The input focus is an attribute possessed by only one window in the system at a time. The window with the input focus is the focal point for all keyboard input. Keyboard messages are moved from the system queue into the thread queue for the thread with a window that presently has the input focus. As the input focus moves from window to window, Windows moves keyboard messages from the system queue to the proper thread queue. This is performed on a message-by-message basis because certain keystrokes are -requests to change the input focus from one window to another window. Subsequent keyboard messages in the system queue may then go to a different thread queue.

Mouse messages are handled a little differently. The mouse is another shared resource. Mouse messages usually are sent to the window that is underneath the mouse pointer. When multiple windows are overlapped, the one on the top-that is, the one being displayed-receives the mouse message. The one exception to this rule involves capturing the mouse. When a Windows application captures the mouse (which is done by making a Windows function call), Windows moves all subsequent mouse messages from the system queue to the capturing window's thread queue, no matter where the mouse is pointing on the screen. The application must eventually release the captured mouse to allow other applications to use it.

The hardware timer generates periodic interrupts that are handled by the timer device driver. Like the keyboard and mouse device drivers, the timer device driver calls Windows to translate the hardware event into a message. Unlike the keyboard and mouse messages, the resulting timer message is placed directly into a program's application queue. Because multiple programs might request that timer messages be periodically sent to one or more of their windows, a single timer interrupt may result in Windows placing timer messages in multiple application queues. In this way, Windows makes the single hardware timer a shareable device. Individual windows each think they have their own private timer.

Windows places other messages into a program's thread queue as a result of the program's calling certain Window functions. For example, a program might call Windows to tell it that an area of a window is no longer up-to-date. Windows will place a message into the thread queue for that window that eventually results in the program's redrawing the out-of-date region of the window.

Now that as a program's thread queues are filling with messages, how does the program get the message from a thread queue and deliver it to the proper window function? You write a small piece of code called the message loop. The message loop retrieves input messages from the application queue and dispatches them to the appropriate window functions. The message loop continually retrieves and dispatches messages until it retrieves a special message that signals that the loop should terminate. One message loop is the main body of a Windows application. A Windows application initializes, repeatedly executes the message loop logic until instructed to stop, and then terminates.

A program calls the Windows GetMessage function to retrieve a message from its thread queue. Windows moves the message from the queue into a data area within the program.

Now the program has the message, but the message still needs to be sent to the proper window function. To do this, the program calls the Windows DispatchMessage function. You might wonder why you call Windows to send a message to a window function within your program. A program may create more than one window. Each window may have its own unique window function, or multiple windows may use the same window function, as shown in Figure 1.4 (unavailable). In addition, many of the window functions for window types provided by Windows are not in your program at all; they are inside Windows. The DispatchMessage function in Windows hides all this complexity by determining which of the program's window functions or Windows's built-in window functions gets the message. It then calls the proper window function directly. Figure 1.5 (unavailable) shows the path that keyboard input takes through the system.

Windows Operating Modes

Windows 95 and Windows NT run on microprocessors of the Intel 80x86 family. Presently this family includes the 80386, 80486, and Pentium microprocessors and excludes the older 8086, 8088, 80186, 80188, and 80286 microprocessors. The 80386 and above microprocessors can run in protected mode, which is required by Windows 95 and Windows NT. The hardware capabilities of these various processors vary greatly from the low end of the spectrum to the high end. Windows is not limited to the capability of the least powerful microprocessor in the series. For example, Windows NT can run on 8-processor Pentium multiprocessor systems and take full advantage of all the processors. Windows 95 is currently limited to a single processor system. For most of this book, we assume you understand that Windows NT also runs on a variety of non-Intel platforms.

Windows 95 and Windows NT run in the so-called "386" enhanced mode on an 80386, 80486, or Pentium processor. Virtual memory capabilities enable Windows to use disk space as additional (but slower) main memory. This allows Windows applications to use more "memory" than is physically available. Windows applications running under Windows execute in the protected mode of the processor.

It is best to test an application in both Windows 95 and Windows NT. Microsoft requires this for their "logo certification" program. You need to test your code on both platforms because there are facilities of each environment that are not available in the other.

Program Memory Models

In Win16, the programmer had to be aware of "memory models", such as "medium" and "large", deal with the effects of the segmented addresses referencing blocks of memory larger than 64K ("huge" pointers), and deal with the differences between 16-bit ("near") pointers and 32-bit ("far") segmented pointers. All of this is irrelevant in Win32 programming. There is just one "memory model": a uniform 32-bit address space.

This is both good and bad. It greatly simplifies programming, removes size restrictions, and can significantly improve performance (pointer arithmetic in Win16's "huge" model required a runtime subroutine call to do the arithmetic!) It also removes restrictions that Win16 programmers had to live with, such as the 8,192-selector limit (if you are just starting as a Windows programmer, be grateful you don't have to know about this). But in Win16, if you allocated a segment and then addressed off the end of it, you were usually greeted with a General Protection Fault (GPF). In Win32, you might simply overwrite a piece of your own memory you never heard of. This can result in bizarre behavior. Those who programmed in DOS or in mainframe, mini, or workstation environments already know about this problem. However, if you became accustomed to Win16 trapping your bad writes, you must be aware that this capability is now gone. Fortunately, unlike as with DOS, you cannot overwrite the operating system when using Windows NT. It is challenging, but not impossible, to overwrite the operating system in Windows 95. (This means it is hard to do by accident.)

However, it was very satisfying to be able, in this book, to eliminate lengthy, tedious, and complex discussions of segmented memory and the various memory models.

Memory Models and Porting 16-bit Code

In Win16 programming, the programmer had to be aware of the details of the memory model. For technical reasons, the 16-bit environment could not support more than one copy of a program running if the "large" model was used, so programmers who wished to write applications with multiple instances used the "medium" model. Many programmers used the medium model even when there was no compelling reason to do so. In Win32, there is only one memory model, so all of these details seem to be irrelevant. But alas, they are not, if you are porting an older program. Programmers often played clever tricks knowing that pointers could be 16-bit values, such as putting two pointers in a single 32-bit word, or a pointer and a window handle in a single 32-bit word. But in Win32, both pointers and handles are 32-bit values. These Win16 tricks often make your life difficult if you are porting an existing Win16 application.

__cdecl and __stdcall Calling Sequences

Calling most Windows functions requires that you pass some number of parameters to the function. Parameters to a function are passed on the stack. Placing copies of the parameters on the stack is called pushing parameters on the stack. Removing them is called popping parameters from the stack. Parameters in a function call may be pushed on the stack from left to right (as they are listed in the C language function call) or from right to left. As it turns out, C functions normally use a right-to-left ordering when pushing parameters on the stack (that is, the first-that is, leftmost-parameter is the last parameter pushed onto the stack) and expects the caller to remove the parameters from the stack. However, when running on Intel platforms, -Windows API calls expect to remove the parameters from the stack themselves. These cases are distinguished by the __cdecl (the default) and the __stdcall linkage types. All Win32 API functions running on Intel platforms expect parameters to be pushed in right-to-left ordering.
Programmers who know the Win16 API may be familiar with the __cdecl and __pascal linkage types. Windows API and callback functions in Win16 were FAR PASCAL. In Win32 they are __stdcall. Note also that the __pascal linkage type pushed parameters left-to-right and expected the caller to remove the parameters.

After a function has completed its job, the pushed parameters must be removed from the stack. That is, the correct number of parameters must be popped from the stack. The compiler generally does not know at compile time how many parameters a particular function might receive. In fact, a specific C function might receive a different number of parameters each time it is called. The printf function is the ultimate example of such usage. A called C function cannot clean up the parameters passed to it on the stack because the number of parameters might vary and is unknown until runtime. Responsibility, therefore, lies with the calling program. After the call returns from the function, the same number of parameters that were pushed onto the stack are removed from the stack.

As it turns out, there is a slightly more efficient calling sequence. By your restricting a function to a fixed number of arguments, parameters may be pushed in either order. Also, the compiler knows at compile time the number of parameters expected by a function. The compiler can then generate the code to remove the parameters from the stack at the end of the called function rather than after every call to the function. Generating this code once instead of multiple times saves space in the resulting program. In addition, the Intel and several other architectures provide hardware support for returning from a function while simultaneously removing parameters from the stack. The C language carefully does not specify if parameters should be pushed in left-to-right or right-to-left order. You should be very careful how you call API functions, and you should not make any assumptions about the order in which parameters are evaluated. This is because you might port your code to some non-Intel Windows platform whose compiler takes advantage of this freedom and evaluates the parameters in a different order. (In addition to the "official" NT platforms, such as the PowerPC, MIPS, and DEC Alpha, there is now a trend in the industry to adopt the Windows API for non-Windows platforms. For example, the Open Software Foundation (OSF) has a specification of a Windows API, and at least three vendors offer X-based or Motif-based versions of the Windows API, implemented as a set of libraries that run on stock Unix platforms.)

By default, the Microsoft C compiler generates calls to functions using the __cdecl calling sequence that supports a variable number of parameters. This supports the C language as defined. However, most Windows functions take a fixed number of parameters. So these functions use the __stdcall calling sequence to gain the additional efficiency. The Microsoft C compiler recognizes two keywords, __cdecl and __stdcall, that enable you to specify on a function-by-function basis which calling sequence to use. The __cdecl keyword tells the compiler that the next function uses the C calling sequence, and the __stdcall keyword tells the compiler that the next function uses the more efficient calling sequence. Windows functions are defined in the windows.h file, via a number of typedefs, to use the __stdcall calling sequence.

To make your program "platform-independent", you should not use these keywords in your code. Windows API functions are normally declared with a macro, WINAPI, and Windows callback functions are declared with a macro CALLBACK. A given platform may define these differently than Microsoft C does. If you use the actual reserved words, your code may not port to a different compiler or hardware platform.

Static and Dynamic Linking

We've discussed how to use the correct calling sequence when calling the window function so that it can find the passed information. A couple of questions remain. Where is the window function? And how does the call in your application get connected with the function hiding somewhere inside Windows?

The linker connects a call in your application to its called function. When linking one or more object modules (.obj files) together to form an executable program (.exe file), the linker matches calls within the object modules to functions within either the same or another object module. After performing this step, if unresolved calls remain, the linker searches library files for the missing functions. The library files searched by the linker are those specified on the LINK command line (which you usually specify via your development environment) and those explicitly named within the object modules being linked. If, after searching all named libraries, the linker still cannot find the called function, the linker will produce an "unresolved external reference" message telling you that one or more of the functions called by your program could not be found. If the linker does find the function, its object code is copied from the library and inserted into the executable file. This is the usual form of linking and is called static linking. Static linking requires the linker to know at link time where a function will reside in memory and to have access to the object code comprising the function.

The linker, however, does not know at link time where a Windows function resides in memory. Windows is a cooperative environment. Multiple applications run simultaneously. It would be nice if a method could be devised by which all applications running concurrently could share a single copy of each Windows function. Such a method does exist and is called dynamic linking. Actually, a Windows function might appear in memory at a different address in each program that uses it, so even if the linker knew the location for one task, the function would not necessarily be at the same location when called by another task. The address will also change from one release to another of Windows and be different in Windows 95 and Windows NT.

This may surprise you. You would think that "the operating system" would always be in the same location. In fact, in any given release of Windows, the operating system does appear in the same locations in all the programs. But in Windows, much of what we think of as "the" operating system is actually distributed among dozens of dynamic link libraries (DLLs), so the common dialogs, common controls, OLE support, and the like are not actually part of the core operating system. Depending on which DLLs you require, and what order they are required in, you will find that they load into each task in a different location and may even load into your own application in different locations each time it is run. So dynamic linking has to deal with this.

Dynamic linking is a technique by which the linker defers full resolution of a function call until the program is executed. Rather than fix all calls at link time, the linker inserts additional information into the executable program, informing Windows of the unresolved functions. As Windows loads the program, the deferred calls are dynamically resolved to the proper function within Windows. The linker determines that an external reference should be dynamically linked through the use of an import library.

The linker searches import libraries for undefined external references present in the program being linked, just as it searches object code libraries. However, import libraries contain no code. Instead, they contain records that name the Windows module containing the external reference and the name or number of the entry point within the Windows module containing the function. This information is copied to the executable program, and the external reference is considered resolved by the linker. Later, when the program is executed, Windows uses this information to resolve the call.

There are several import libraries used for linking Windows programs. The most important are those that supply the interfaces for the key modules of Windows: kernel32.lib, user32.lib and gdi32.lib. These libraries contains no code. Instead, they contain import records for the key API Windows functions that can be called by a Windows application. A library also can contain the object modules for functions used in static linking as well as the import records used for dynamic linking.

Dynamic Link Libraries

The linker can produce either program modules or dynamic link libraries (DLLs). Program modules are executable (.exe) files that you can run. Dynamic link libraries are also executable (.dll) files but are never directly executed. The two are quite different from each other.

Programs run as tasks under Windows, and they receive messages. A task is an independent executing entity. For example, you can run two copies of the NotePad program concurrently. Windows creates two tasks, one running each copy of the NotePad program, but only one copy of the code for the NotePad program is in memory. Each task, however, has its own private data. Hence each task can be working on a different file.

DLLs are simply collections of routines that can be used by programs or by other DLLs. A DLL never runs as a task under Windows. A DLL function must be called, directly or indirectly, from a running program that is a task under Windows. A program calls a subroutine in a DLL through dynamic links as described earlier. The Windows functions your application calls are contained within dynamic link libraries. Windows itself consists of the following four main dynamic link libraries:

1. user32.dll manages the Windows environment in addition to managing all your application's windows. Menus, built-in controls, and the like are managed here.

2. kernel32.dll provides the system services of Windows. These services include multitasking, multithreading, memory management, and resource management.

3. gdi32.dll provides the Graphic Device Interface, which is what you use whenever you draw on a window. Operations for drawing lines and text, for example, are here.

4. console subsystem (in kernel32.dll) provides an interface for character-based applications. Since this book concentrates on the GUI aspects of programming Windows, we have little to say about this capability.

Programmers who know the Win16 system may be familiar with the module names gdi.exe, kernel.exe, and user.exe. In fact, if you look in the Windows system directory, you will find these modules. These are used to provide the interface between a running Win16 application and the Win32 environment.

DLLs are created much like a Windows program is. The linker links object module files together, thereby creating an executable file. As we mentioned, this executable file is not directly executed. When Windows loads a program containing external references that need to be dynamically linked, those references will name the DLL and the entry point within the DLL that resolves the reference. Windows then looks for a DLL by that name and loads it in addition to the original program. That DLL may, in turn, contain external references to another DLL that need resolving. This process continues until all references are resolved or a DLL cannot be found.

Using dynamic link libraries instead of static link libraries provides a number of benefits to you, the programmer. Multiple applications using a single library routine from a static link library each contain a separate copy of the routine. When multiple applications use a routine contained in a DLL, all the applications share a single copy of the routine. In addition, when a static link library function is changed, all applications that use the routine must be relinked to use the updated version. It is bad enough having to relink, but first you have to determine which programs use the changed function or else give up and relink everything "just in case". When a routine in a DLL is changed, the DLL is relinked and all applications that need it will use the updated version the next time they are executed. There is no need to update the application simply because a library routine has changed.

Of course, this method has its own set of pitfalls. For example, you must, if you are updating a DLL, make sure that it is still compatible with earlier versions, since a large code base may exist of which you have no knowledge and over which you have no control. You must provide a way for a program to find out which version of a DLL it is using. Also, if the program depends on features at a certain release level of the DLL, it should be able to detect and report an attempt to use an earlier version. Your "installer" must make sure that it doesn't overwrite a newer version of the DLL with an older version on its distribution disk. Nonetheless, the power and flexibility of using DLLs is so significant that they are the mechanism of choice in most cases.

Exports and Imports, or Who's Looking for Whom?

Dynamic link libraries provide functions that are called by programs or other DLLs. A dynamic link library must export a function in order for it to be used by another DLL or program. Exporting a function creates a record in the executable file. This record identifies the name and ordinal number of the entry point as well as the location of the function within the module.

A program must import functions that it calls using dynamic links. Importing a function notifies the linker that the reference is a dynamic link and will not be resolved until runtime. A function can be imported either by placing an explicit import statement in the application's module definition file (a file needed when linking the application) or by allowing the linker to find an import record for the called function in a library. Either way, the linker places a record in the program's executable file to notify Windows of the dynamic link. When Windows loads a program and finds an import record for a dynamic link, it resolves the call. The import record names the dynamic link library and the function name or ordinal number of the routine within the library that the program is calling. Windows loads the named DLL and looks for an export record for the same function name or ordinal number. The export record tells Windows where in the library the function resides. Windows now dynamically binds the call in the program to the function with the DLL.
Programmers who know Win16 are aware of the __export keyword used to mark callbacks and DLL entry points. In Win32, a callback is simply a __stdcall linkage and needs no special export declaration. A DLL entry point can be specified in the module-definition file, on the linker command line, or by adding the "storage class modifier" __declspec(dllexport) to the declaration of the entry point.

Windows Memory Management

Windows programs can consist of one or more code segments and one or more data segments. Dynamic-link libraries may have one or more code segments and one or more data segments. Multiple Windows programs can be running concurrently, and multiple instances of the same program also may run concurrently. In Win32, some number of these data segments (perhaps none) can be shared with all programs that are using the DLL.
Experienced Win16 DLL writers are painfully aware of the limitations of Win16 DLLs, namely, that there is at most one data segment and it is shared among all clients of the DLL. In Win32, you can have both private and shared data segments and a DLL can even allocate thread-specific storage.

The windows.h Header File

The next chapte has an example Windows program. It doesn't do much, but one of the first things you will notice about it is that many of the data types used by the program are not standard C data types. These data types are defined in an include file that must be included in every Windows module. All windows programs should start with the following statement:

#include <windows.h>

A significant percentage of the Windows API is defined by the windows.h file. In earlier versions of Windows, this file included all of the basic definitions for the API calls, the symbolic constants associated with those calls, and the structures and typedefs that defined data values. As Windows evolved, several problems arose with this approach:

windows.h typedef Declarations

windows.h also uses many typedef declarations to permit a Windows program to be more independent of the actual architecture of the computer on which it is running. For example, a char variable on one system might be signed, but on another it might be unsigned. In fact, the Microsoft C compiler has the /J command line option, which changes the default type for char variables from signed to unsigned at compile time. While its use is uncommon (it was added so that older, pre-ANSI-standard unsigned-character-based C code could be compiled), it doesn't hurt to be precise about character specifications.

Other typedefs are used to declare types for which C does not have a native type. For example, C does not have a native Boolean type. Although a char or an int may be used as a Boolean, confusion arises when others read your code. Questions arise such as, "Is it significant that this variable has the value -1 as opposed to +1? After all, it is an integer variable." Defining and using an explicit Boolean type, BOOL, eliminates this potential confusion. Resist all temptations to confuse BOOL and integer types. If you declare a Boolean type, follow the style guidelines we suggest in Figure 1.6 (unavailable).

It is particularly important that you never test for a true Boolean by writing (var == TRUE). This is because many API calls that specify that they return a BOOL value in fact return 0 for FALSE and some other nonzero value for "true". Thus the test against the literal TRUE will fail. This is a source of many obscure bugs. In particular, Windows 95 often returns a nonzero value not equal to TRUE for many API functions for which Windows NT returns the literal value TRUE. The formal definition of the API function is that it returns type BOOL. This means that code that tests for equality to TRUE would function correctly on Windows NT but could (apparently inexplicably) fail when run on Windows 95.

Similarly, do not confuse integer and Boolean operations by testing for integer 0 with the Boolean not (!) operator. It is far clearer to write, read, and understand the test

int p;

// ...

if ( p == 0 )

than the rather dated, and obscure

int p;

// ...

if (!p)

It is best to use the typedefs declared by windows.h whenever possible. Some of the more common and basic types are listed in Table 1.4. windows.h uses the convention of prefixing a type with NP for a "near pointer", an irrelevant Win16 concept, to the type; LP for a far (long) pointer to the type and P for a pointer (unspecified as to near or far) to a type. In Win32, for compatibility with existing code, the NP and P prefixes are retained but are identical in all respects to the LP prefix. You should use only the LP prefix for new Win32 code. Why LP instead of just P? Well, for one thing, all of Microsoft's documentation explicitly specifies LP-style names. This suggests that they have not decommitted from the naming convention. In addition, with the future 64-bit platforms we may yet again get "near" (32-bit) and "far" (64-bit) pointers. Since most software companies will not publicly discuss future plans, we cannot tell what the future will hold in this regard. So we have chosen to follow Microsoft's current convention.
Table 1.4: Basic data types defined by windows.h
Desired Data Type Type to Use windows.h typedef
constant pointer to a Unicode string LPCWSTR const wchar_t *
pointer to a Unicode string LPWSTR wchar_t *wchar_t
constant pointer to an 8-bit string LPCSTR const char *
pointer to an 8-bit string LPSTR char *
constant pointer to a string LPCTSTR LPCWSTR or LPCSTR
pointer to a string LPTSTR LPWSTR or LPSTR
pointer to a byte LPBYTE BYTE *
pointer to an int LPINT int *
pointer to a 16-bit unsigned integer LPWORD WORD *
pointer to a 32-bit signed integer LPLONG long *
pointer to a 32-bit unsigned integer LPDWORD DWORD *
generic pointer LPVOID void *
The following are synonyms for the above:
pointer to a byte PBYTE BYTE *
pointer to an int PINT int *
pointer to a 16-bit unsigned integer PWORD WORD *
pointer to a 32-bit signed integer PLONG long *
pointer to a 32-bit unsigned integer PDWORD DWORD *
generic pointer PVOID void *
The following are obsolete in Win32:
near pointer to a byte NPBYTE BYTE NEAR *
near pointer to a signed integer NPINT int NEAR *
near pointer to a 16-bit unsigned integer NPWORD WORD NEAR *
near pointer to a 32-bit signed integer NPLONG long NEAR *
near pointer to a 32-bit unsigned integer NPDWORD DWORD NEAR *

The LP forms of the pointers are those that are specified by the API. The P forms are considered "more natural" by purists because the "large" model that required "far" (LP) pointers is now history. For consistency with the API documentation, we use LP-style definitions throughout this book.

The definition of the WORD typedef changed subtly in the Windows 3.1 version of the windows.h header file, and the older folklore often persists. The change in definition may also cause problems in porting code. Previously versions of the windows.h header file defined a WORD as an unsigned int. Beginning with Windows 3.1, a WORD is defined as an unsigned short. Both types are equivalent to a 16-bit unsigned value in programs compiled for a 16-bit version of Windows. However, the types are not equivalent in programs compiled for a 32-bit version of Windows.
If you are porting existing code from Win16 to Win32, one important place to check is for consistent and correct use of WORD. In particular, the use of WORD instead of UINT may give you portability problems. Also, watch out for window handlers that declare "WORD wParam" instead of "WPARAM wParam", the recommended practice starting with Windows 3.1. The declaration of wParam as a WORD will cause unexpected failures if you attempt to run the code in Win32.

An int in the C language is the "natural size" supported by the architecture. For a 16-bit processor, an integer's natural size is 16 bits. Similarly, for a 32-bit processor, an integer's natural size is 32 bits. A short int can be smaller than an int and is typically 16-bits wide. A long can be larger than an int and is typically 32-bits wide. (All three types could use the same size, and on some word-oriented architectures such as Digital Signal Processors (DSPs), there are C compilers that implement int, short, long, and even char as 32-bit values!) An unsigned int is a 16-bit value in 16-bit Windows applications and a 32-bit value in 32-bit Windows applications. An unsigned short is a 16-bit value in both environments. Basically a WORD is now defined to be a 16-bit unsigned value, always. The long-obsolete definition of WORD caused WORD variables to change size based on the compiler used.

The typedef UINT represents an unsigned int value, in other words, an unsigned integer that is the natural size used by the processor. You define a variable of type UINT when you want a "generic" unsigned value that is efficiently used by the processor but where a 16-bit value would suffice if that is what the processor best uses.

The new types LPWSTR represent pointers to Unicode strings. Unicode is a 16-bit character code that allows you to intermix a variety of international languages in your application. Unicode is currently supported only on Windows NT. In fact, all of Windows NT internally is implemented in terms of Unicode. A string may be either an 8-bit (ANSI/ISO) character string or a Unicode string, depending on whether the UNICODE symbol is defined at compile time. The definitions of LPTSTR and LPCTSTR change depending on whether this symbol is defined. If you are compiling for Unicode, they will be Unicode strings; otherwise, they will be ordinary 8-bit character strings. If you use LPTSTR and LPCTSTR in your code and follow some other conventions, a flip of a compiler switch will allow you to compile either a conventional 8-bit character set application for Windows 95 or Windows NT or a Unicode application for Windows NT. However, you must be careful how you define your strings and how you compute their lengths, which we do not go into here (but we discuss in great detail in Chapter 7). But most Win32 API calls that want strings specify LPTSTR and LPCTSTR parameters, so if you expect to eventually support Unicode, you should read the relevant documentation carefully and use these declarations. In Windows 95 and Windows NT 8-bit character mode, LPTSTR is exactly LPSTR and LPCTSTR is exactly LPCSTR. If you were to use the underlying wchar_t declaration, you would be committed to using Unicode. The definitions LPTSTR and LPCTSTR give you the flexibility of deferring this decision. All of our example code is Unicode-aware, and much of it has been compiled and tested using Unicode.

Getting a Handle on Handles

Another important data type defined by windows.h is that of a handle. A handle to a datum is used to reference the datum much like a pointer. Unlike a pointer, a handle may not be dereferenced. Additionally, a handle is not a "pointer to a pointer" as it is on other systems. A Windows handle basically is a claim check or token. At a theater or restaurant, you might give your coat or hat to a hat-check person. In return, you receive a claim check that you later use to retrieve your belongings. You have no idea what the information on the claim check means or how it relates to where your belongings are stored. You simply provide the claim check when required, and your belongings are returned.

Handles are used in the same way. For example, when you call the CreateWindow function, it returns a "window handle" that identifies the created window. You use this window handle whenever you ask Windows to perform some action on behalf of that window. In addition, whenever Windows sends your window function a message about a window, the window handle is included as part of the message to identify the window to which the message applies. You haven't the faintest idea what the internal representation of the handle means, but you don't need to know. You provide it when required, and Windows locates the data with which it is associated.

Handles are used throughout Windows. They provide a way to reference items that are managed by the operating system. Handles remain constant where pointers would be meaningless (because the operating system is in a separate address space) and could become invalid. Most Windows objects are identified by a handle. There are handles to windows; to resources such as strings, icons, menus, and cursors; to instances of a -program; to graphical objects such as pens, fonts, brushes, and bitmaps; to device contexts; to regions; to color palettes; and even to dynamically allocated memory. Table 1.5 lists the various types of handles defined by windows.h.

In Win16, a handle was always 16 bits; in Win32, a handle is always 32 bits. If you are porting code from Win16, beware of a tendency of programmers to pack two handles in a single long word or to combine a handle and some other 16-bit quantity into a 32-bit long word. There are other important implications of this change that result in incompatible messages between Win16 and Win32, which we point out as they occur.

Table 1.5: Types of handles defined by windows.h
windows.h typedef Handle Type windows.h typedef Handle Type
HACCEL Accelerator HINSTANCE Program instance
HBITMAP Bitmap (GDI)) HKEY Registry key
HBRUSH Brush (GDI) HLOCAL (obsolete) Local memory
HCURSOR Cursor HMENU Menu
HDC Device context HMETAFILE (obsolete) Metafile
HDRVR Device driver HMODULE Module
HDWP Deferred window position HPALETTE Palette (GDI)
HENHMETAFILE Enhanced metafile HPEN Pen (GDI)
HFONT Font (GDI) HRGN Region (GDI)
HGDIOBJ General GDI Object HRSRC Resource
HGLOBAL Global memory HSTR String for OLE/DDE
HHOOK Hook HTASK Task
HICON Icon HWND Window

Using the Windows C Runtime Libraries and Header Files

Whenever you need to include both Windows header files and standard C library header files, you should include the Windows libraries first. This guarantees that certain symbols that must be defined specifically for Windows, such as NULL, are correctly defined. Microsoft has set its header files up in such a way that this will always work correctly. If you are using other header files or header files for third-party libraries, you should ensure these header files do not force symbols, such as NULL, to have definitions that conflict with the Windows definitions. While NULL is usually handled correctly in third-party software designed for Windows, the inclusion of other C runtime headers from third-party vendors occasionally causes problems if these headers are not "Windows-aware" and do follow the correct conventions.

Using Strict Type Checking

You can take advantage of stricter type checking by defining the symbol STRICT this way before including the windows.h header file:

#define STRICT

When the symbol STRICT is defined, the windows.h header file defines many data types in a more precise way. This alternative definition of various data types causes the C compiler to differentiate data types that are considered identical when STRICT is not defined.

For example, when STRICT is not defined you can pass a handle to a device context (HDC) to a function expecting a handle to a window (HWND) without comment by the compiler. Both handles are typedefed as UINT types, so they do not actually differ in type. When STRICT is defined, the compiler will generate an error because windows.h defines each type as a pointer to different types of imaginary structures. (Structures of these types are never allocated. Only the type definition itself is used.)

This pointer very deliberately occupies the same space as an UINT variable, but the compiler can detect passing one type of pointer to a function expecting a different type. So parameter mismatches that previously were not detected will be when strict type checking is enabled.

However, when strict type checking is enabled, your code can no longer indiscriminately assign variables of different types to each other without the compiler's producing warnings and errors about such usage. A handle to a window (HWND) differs from a handle to a menu (HMENU). If you try to save one in a variable defined to be the other, the compiler will complain about it. Although it's a bit of work to write code to compile without warnings with strict type checking enabled, it's to your benefit to do so. Code that compiles without warnings with strict type checking enabled often contains fewer bugs and is far more portable between 16-bit and 32-bit Windows applications. Often, if you are porting a Win16 application to Win32, you might be well-served to insert the STRICT definition and recompile the Win16 application using a 16-bit compiler. STRICT may catch problems that would interfere with porting.

Naming Conventions

Microsoft suggests that you write Windows applications following its suggested naming conventions. Doing this, it believes, makes your applications easier to understand and to maintain. It recommends specific naming conventions for function names and variable names.

Function Names

Windows functions are named according to a verb-noun model that identifies what the function does (verb) and what the function operates on (noun). Function names begin with an uppercase letter, each word in a function name is individually capitalized, and all words are concatenated (no spaces or underscores separate the words). The example programs in this book use the same naming convention. Some examples of Windows function names include these:

Parameter Names

Windows programmers typically use a naming convention for variables as well. Microsoft has decided that variables should be named in a way that identifies the data type of the variable. Variable names are given a lowercase prefix that indicates the general type of the variable, followed by one or more words describing the variable's contents. Multiple words in a variable name are individually capitalized and concatenated, as in function names. Table 1.6 lists standard prefixes used in Windows programming. When no lowercase prefix is used, a variable normally is a short integer whose name is descriptive.
Somebody has to come out in public and try to restore sanity. I don't like the Hungarian Notation, consider it a grotesque abberation in the history of computing, and have found it to cause more problems than it claims to solve. One advantage of writing a book is that the authors get to express their opinions, and this is mine. It is based on 33 years in the profession, a Ph.D. in computer science, many years of experience in commercial software development, and a more-than-casual study of human cognition and reasoning. The few popular systems that incorporated type information into names that are still with us are FORTRAN and BASIC, and both of those abandoned this technique after a few years of evolution, and for compelling reasons. Hungarian notation originated in the pre-ANSI C compilers where there was no cross-module type checking. In that context, it may have made sense, but its usefulness today is problematic. joseph m. newcomer.

It is worth observing that this naming convention produces very strange problems. For example, you may have a procedure that returns a BOOL result and, following the convention, declare a name to hold its result, such as

BOOL bResult;

But then you discover that the result type must reflect, say, three values, so you change the name to

int bResult;

and instead of assigning TRUE and FALSE you assign RESULT_OK, RESULT_BAD, and RESULT_UNKNOWN. Those reading your code will be surprised to see an "b"-type name assigned non-Boolean values. So you have to change the variable name to something more appropriate, such as fResult.

Another example is that often in Win16, a variable was declared

WORD wSize;

or, more seriously, a name declared in an interface structure:

typedef struct {

WORD wSize;

WORD wCount;

WORD wBits;

int nWidth } FunnyStructure;

when in fact, as you discover in porting, it should have been a UINT. If you change the declaration to a UINT, you have to change the name everywhere to uSize. If you fail to change the name, the whole purpose of the naming convention is defeated. The use of the "w" prefix will mislead you into thinking that you are dealing with a 16-bit quantity, when in fact you are dealing with a 32-bit quantity. Furthermore, if this name is part of a published interface, you may not have the option of changing the name. Thus the published public interface, although it has changed in structure, now has a misleading name. The ultimate absurdity of this naming convention is the wParam parameter, which you would expect from its prefix to be a 16-bit unsigned integer. In Win32, however, it is really a 32-bit unsigned integer and so would have to be named uParam. The "cultural compatibility" that calls for the first parameter to a message to be called "wParam" was more important to retain than was the meaning of the prefix.

Consider further the issue of porting or extension. In the move to a 32-bit platform, the value in the structure, such as wSize, may have to be changed to a 32-bit value. As we pointed out, this should necessitate a change in the name. To do otherwise is misleading and a potential source of major misunderstanding on the part of the user of the structure. But to change the name will obsolete all existing code, thereby rendering it uncompilable. The complementary situation could be that the variable declared as int should have been declared as a short in order to be compatible with earlier file structures. Again, this generates either a misunderstanding or an incompatibility. By eliminating the type from the name, these problems go away. Software evolves. Be prepared for this. Nonetheless, Microsoft has adopted these naming conventions extensively, and you must at least understand how to read them. In some cases, violating the tradition (such as changing the name of the two message parameters from wParam and lParam) will render your code unreadable to experienced Windows programmers. These naming conventions should be used, when at all, very carefully.

Some examples of the names this style generates are as follows: fSuccess, chAnswer, szFilename, wParam, lParam, dwValue, hwndButton, xPos, yPos.

In addition to the wParam problem, we have found numerous instances in which Microsoft does not adhere to their own naming convention, thus leading to type mismatch errors that cause compilations to fail. There are API functions that take structures with names that start with dw that are actually declared as LPVOID, and names that start with lp that are actually DWORDs. This naming convention is at best highly suspect, no matter how much Microsoft believes in it and tries to promote it. With few exceptions, largely those of cultural compatibility (most notably wParam and lParam, and the use of an occasional h-prefix for window handles), we avoid it entirely in our sample code.

Example Code

We have adopted a convention to help you distinguish our functions from Windows functions. We have started every function name in our code with a lowercase letter, except when constrained by the rules of Windows and except for some other very specific exceptions we try to point out when appropriate. Nearly all Windows API functions start with uppercase letters. The purpose of this convention is to save you confusion in looking up a function in the Windows API. If a function name starts with a lowercase letter, it is either a function we have defined or it is part of the "traditional" C runtime library. All of our functions contain at least one embedded uppercase letter. Since we assume you have a background in the C language, we expect that you will recognize most of the C library names.

Diving Right In

Much of the information just presented may tend to scare you away from Windows programming. It shouldn't. Windows programming seems complex because of the tremendous variety and depth in the Windows API. Quite a bit of the information may not soak in until you actually start writing Windows programs. But you'll be a beginner only once. As you get more experience with Windows programming, you can review this chapter and gain more from it than you might have on the first reading. The tables in this chapter are intended to be comprehensive at the risk of including more information than has been presented and explained at this point. We find comprehensive tables to have more long-term reference value than multiple pieces of related information scattered throughout a book.

We've always found it impossible to learn how to program in a new environment by reading about it. So, without further ado, we show you in Chapter 2 how to create a skeletal Windows program that will be the basic structure from which all Windows applications may be developed.

Further Reading

Feuer, Alan, MFC Programming, Addison-Wesley, 1996.

An excellent companion to our book, this book covers how to use the Microsoft Foundation Class library, concentrating on the MFC and C++ aspects. If you're going to program in MFC, you should have this book also. Harbison, Samuel P. III, and Steele, Guy L Jr.., C: A Reference Manual, Fourth edition, Prentice-Hall, 1995. ISBN 0-13-326224-3.

See the review on page 941. Hummel, Robert L., Programmer's Technical Reference: Data and Fax Communication, Ziff-Davis Press, 1993. ISBN 1-56276-077-7.

More information than you could every possibly absorb on the low-level details of modem command strings. A thoroughly researched book, which describes the overlapping and conflicting codes that are used. A readily-accessible reference to the ANSI character set standard. Proceedings of the ACM Conference on the History of Personal Workstations, January 9-10, 1986, Association for Computing Machinery (ACM Inc.). ISBN 0-89791-176-8.

Schildt, Herman, The Annotated ANSI C Standard, Osborne/McGraw-Hill, 1990. ISBN 0-07-881952 -0.

Essential reading for anyone who cares about the formal definition of C. The annotations offer insight into the purpose of a language construct, point out ambiguities, and generally make this book more readable than the raw ANSI/ISO standard. Schulman, Andrew, David Maxey, and Matt Pietrek, Undocumented Windows, Addison-Wesley, 1992. ISBN 0-201-60834-0.

See the review on page 274.

1 The Digital Equipment Corporation PDP-8 was one of the first "personal" computers-a computer that could actually be afforded by, and dedicated to, a single human being. You had to key in the bootstrap loader through the switches, and early PDP-8 programmers can still, today, recite the boot load toggle switch sequence.

2 For an overview of the early history of the personal workstation, you may want to look at the Proceedings of the ACM Conference on the History of Personal Workstations, cited in the "Further Reading" section.

3 There are special kinds of applications, such as console applications and NT System Services, that often run without a GUI. These applications generally do not interact with the user directly. We do not cover these applications in this book.

4 For a clever use of the API to build a DOS or Unix-like shell, you might look at Schulman, Maxey and Pietrek's Undocumented Windows. See the "Further Reading" section in this chapter. However, this book applies only to Windows 3.1. There is a way to program "shell"-style applications and "filters" in a Unix-compatible or DOS-compatible style in Windows NT by using the "console" subsystem.

5 We could claim that the "pure" Win32 API is that collection of API functions implemented in three key Windows libraries: Kernel, User, and GDI. But facilities such as the Common Dialogs and Common Controls are so important that their separate seems more as an historical accident of how Windows evolved. This book will cover aspects of the three core libraries, common dialogs, and common controls, as well as occasional forays to other libraries where they are important.

6 "[The macro] NULL expands to an implementation-defined null pointer constant" (emphasis added). See The Annotated ANSI C Standard, §7.1.6. The full citation is given in the "Further Reading" section.

7 See page 17in Hummel's book on data communication, cited in the "Further Reading" section. The first 32 ASCII characters, character codes 0.31, are NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT, LF, VT, FF, CR, SO, SI, DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN, EM, SUB, ESC, FS, GS, RS, and US.

Copyright © 1996, XYZ Corporation. All rights reserved.