Programming Windows: Windows Application Basics (Premium)

The benefits of a graphical user interface (GUI) like Windows are well understood today, but this was a hotly contested topic in the mid-1980s. “Real” computers didn’t need no stinkin’ mouse or pretty graphics, critics alleged at the time. But Bill Gates, like Steve Jobs, correctly saw the future for what it was. (Indeed, Jobs was so blown away by the GUI work he saw at Xerox Parc that he completely missed their other innovations in object orientation and networking.) And the PC would inevitably move from the command line environment provided by MS-DOS towards the GUI provided by Windows.

That transition would take several years and several versions of Windows. But whether it was an operating environment on top of MS-DOS, a “thing on a thing,” as it was called in the early days, or a cohesive operating system in its own right, as it is now, Windows has always provided the same basic advantages to both users and application developers: A consistent user experience for all applications, task-switching between multiple running applications, inter-application data exchange capabilities, and device independent access to hardware peripherals like printers and graphics cards.

For developers, in particular, using Windows meant that they never had to create their own user interfaces from scratch; everything from the application windows themselves to the buttons, text boxes, scroll bars, and other objects that appear within them were supplied by Windows. These objects were consistent on all Windows applications, so developers could create their own solutions that looked at home next to the applications that Microsoft included in Windows and worked similarly.

This was all wonderful—still is—but Windows struggled in other areas. It required years of refinement and steady hardware improvements before it was efficient for most users. It was large and resource hungry, and while early versions could run on an 8086-based PC with two floppy drives and just 320 KB of RAM, an 80286 tied to an expensive hard drive and 640 KB or more of RAM was far more realistic.

Microsoft C ad in Byte Magazine in 1986.

For the budding Windows application developer, however, Windows was even more challenging.

“The real problem with Windows is that it’s hard to write programs for,” Byte’s Jerry Pournelle sagely and correctly noted in the March 1989 issue of that publication.

He was right. In those years, developers needed Microsoft C or another compatible C compiler plus Microsoft’s Windows Software Development Kit (SDK) and a strong command of the C programming language. They also needed to figure out this crazy new Windows API that Microsoft created. And while the Windows SDK came with a reference guide to the roughly 450 functions that developers could use, it contained virtually no tutorials.

Microsoft advertises the Windows SDK in Byte Magazine in 1986.

Help would come eventually in the form of Charles Petzold’s classic book Programming Windows. But the first edition of Programming Windows wasn’t published until 1988, by which time Windows 2.0 had shipped; the first version of the book was thus current for the Windows 2.x releases, including Windows 2.0 and Windows/386. And it was still a pretty dense topic, and quite a departure from the simpler but more limited MS-DOS application development model.

Put simply, there was a lot to learn.

Under Windows, applications didn’t take over the screen, let alone the entire system. Instead, they ran in windows alongside other application windows and competed for the user’s attention and the system’s resources. And an improperly written Windows application could easily steal share of both by overwhelming the cooperative (“non-preemptive”) multitasking model used by early Windows versions.

At its heart, a Windows application was then, as it is now, an object that processes events. Some events, like keystrokes and mouse clicks, came from the user. Others came from Windows. These events could happen at any time, of course. For example, an application window might sit unused for hours, but a user could sit down and click on it at any time. So, Windows had a system of messages by which it alerted applications of events. The click was an event. So was the user resizing the application window. Or selecting some button or other control it contains.

Given this, every Windows application written to the Windows API had to contain two elements. An entry point analogous to the main() function you saw in Programming Windows: Hello, World (Premium) in which the application window and its user interface is defined and created. And a window function that runs whenever Windows sends the application a message.

In Windows, the application entry point is called WinMain(), not main(). And the window function can be called whatever you like; Petzold used WndProc. In 1988, a basic Windows application written in C took this form, roughly:

#include <windows.h>

int PASCAL WinMain(// parameters)
{
    // Register the window class
    // Create the application window
    // Run the message loop
}

long FAR PASCAL WndProc(// parameters)
{
    // loop that only handles the messages you wish to respond to
}

It’s obviously a lot more complicated than that, but that’s the basic gist of it. And this system remains unchanged today, over 30 years later. The basic structure of a Windows application written in C/C++ today looks nearly identical:

#include <windows.h>

int WINAPI wWinMain(// parameters)
{
    // Register the window class
    // Create the application window
    // Run the message loop
}

LRESULT CALLBACK WndProc(// parameters)
{
    // loop that only handles the messages you wish to respond to
}

A couple of notes about the code differences between 1988 and 2019.

The return type (int, for integer) of the WinMain/wWinMain function has never changed. If the application ended with no errors, this function would return a 0 to denote the success. Otherwise, it would return some integer value representing the type of error it encountered. This hasn’t changed over the years. (What’s “wWinMain”? it’s just good ole WinMain but with Unicode-formatted string parameters; WinMain uses ANSI parameters. It’s just more modern, basically.)

That reference to PASCAL between the int return type and the name WinMain in the original version of this code indicated that this function utilized a Pascal-style calling sequence that placed the function’s parameters (not shown here) on the stack in the reverse order that is typical in C. Why did it do that? It was a byproduct of IBM’s decision to use the 8088 on the first PC; the Pascal-style calling sequence was more space-efficient on early x86 chipsets, and on the constrained hardware of the 1980s, every little bit of space saved was beneficial. The PASCAL data type was not part of the C programming language and was specific to early versions of Microsoft C. This is one of the things I was referring to earlier when I wrote that Windows development required Microsoft C or another “compatible” C compiler.

By the way, that 1988 version of the code would remain essentially unchanged over the first three editions of Petzold’s Programming Windows tome, which covered Windows 2.x, Windows 3.0, and Windows 3.1 respectively. By the time the fourth edition arrived, covering Windows 95, the style we see today had emerged. Credit the move to 32-bits and the end of our segmented memory nightmare. Thanks again, IBM! (And that WINAPI data type? As it turns out, functions in modern Windows applications still require a special calling sequence and that’s just a more modern way of expressing that.)

As for the window function, it returned a long, which I believe in the 16-bit world mapped to a 32-bit or “long integer” value. (It still returns the same type of value today; LRESULT is really just a long.) And it also used the Pascal-style calling sequence for efficiency reasons. But there was an additional FAR directive in there, too. That indicates that calling the window function requires a 32-bit address rather than a 16-bit address. So, it is another byproduct of IBM’s decision to use the x86 processor family which, in those early days, only supported a segmented memory architecture and not the linear, flat model we’ve used since the 80386. It’s no longer required today, of course.

Related to this, Windows applications have always utilized a system of dynamic linking by which they call functions in external library files called Dynamic Link Libraries (DLLs) at runtime. This is different from how traditional C programs work. Remember the tiny hello, world application we discussed in Programming Windows: Hello, World (Premium)? When that program is compiled, it is statically linked with a header file called stdio.h (for “standard input and output”), so the resulting object and executable files essentially contain the results of the code from both hello.c and stdio.h, the latter of which contains the definition of the C language function printf().

Windows applications have always worked differently and in the early days, the Windows SDK provided a new, Windows-specific linker that replaced the version that came with Microsoft C. This Windows-specific linker understood that a Windows application could call functions that would not fully resolve at link time. Instead, they would resolve dynamically, when the application ran. (Remember, too, that the user might start multiple instances of the same EXE.) And yes, we can tie this system—still used by modern Windows versions today—back to IBM’s decision to use the x86 processor platform and its limited, segmented memory model of the day. These early PCs were simply too resource constrained for Windows applications to work otherwise. And this dynamic linking system, combined with the memory management functionality in Windows (yet another feature a “compatible” C compiler needed to support) allowed Windows and its applications to “use” more RAM and other resources than were physically available in any given PC.

Looking back at the code snippets above, the message processing loop in the window procedure was implemented in such a way that the developer could scan through the messages to which the application would respond. All other messages were sent to a default window procedure which would carry out the appropriate default action. This system, too, has not changed since the first version of Windows. (Only the sophistication of the underlying platform has changed.)

So, if we build it out a bit more than what we see above, the message processing loop inside the window procedure looked/looks something like the following:

switch (message)
{
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_OTHER_MESSAGE:
        // do something
    case WM_OTHER_MESSAGE:
        // do something
    case WM_OTHER_MESSAGE:
        // do something
    default:
        return DefWindowProc(// parameters);
}

The C switch statement was an efficient way to run code based on which message the application received. The first case, and it was one that all Windows applications handled, ensured that the application was closed correctly, and upon doing so, would return a 0 (“success”) as its exit value. The others would be specific to the application.

So that’s a Windows application in a nutshell: You define and then create at least one window, which will have some form of user interface, and then you set up a message loop and define which messages the application will respond, and how. This loop runs until the application is closed.

“You’ve learned about windows,” Mr. Petzold writes at the end of the “welcome” chapter of OS/2 Presentation Manager Programming (2nd edition, 1994). (OS/2 software development, as I’ll discuss soon, was very similar to Windows programming thanks to a shared heritage.) “You’ve learned about messages. That’s it. Everything that follows is just detail.”

Simple, right?

OK, not at all. And just wait until you see how complicated even the most basic “Hello, Windows” application looks when created with C and the Windows API. As it turns out, writing and maintaining a truly useful and full-featured application with this system wasn’t just daunting, it was a nightmare, and that was well before the modern age of high-DPI displays, advanced power management, and broadband Internet connectivity.

This was a system that would never scale to meet the needs of the future. And that explains why Microsoft and other software development tool makers literally spent all of the 1990s trying to make it easier to create ever more sophisticated Windows applications using other languages, frameworks, and environments.

Gain unlimited access to Premium articles.

With technology shaping our everyday lives, how could we not dig deeper?

Thurrott Premium delivers an honest and thorough perspective about the technologies we use and rely on everyday. Discover deeper content as a Premium member.

Tagged with

Share post

Thurrott