Programming Windows: Object-Oriented Programming

Illustration by Robert Tinney, from the March 1989 issue of Byte Magazine

Developers creating Windows API-based applications in the C programming language faced many uphill battles, from the complexity of the unsophisticated flat API itself to the nuances of the event- and message-driven paradigms required by the underlying platform. But the bigger issue, perhaps, was related to scale: Even the simplest Windows API applications required a lot of code—which I think of as “scaffolding”—to perform the simplest of tasks: Define a window. Create a window based on that definition. Start a message loop, looking for events to which to respond. And so on.

Developers typically look to some type of code reuse to make repetitive tasks easier over time. And in those days, code reuse took two forms: Copy and paste, where the C scaffolding for one application was literally copied into the code for a new application so that it could be changed as needed, and function library creation, where a developer could create his own libraries of boilerplate code to hide the scaffolding in sub-files and keep the main program file cleaner and easier to read. Indeed, many early Windows developers would even go far as to strip Microsoft’s massive C header files of functionality not required by their apps to save space, improve performance, and make the resulting collection of source code more manageable.

C is a product of its era, but by the time Microsoft adopted it for Windows, it was already being superseded by an object-oriented extension originally named “C with Classes,” and then C++. This new take on the language adopted the objected-oriented principles discussed below and allowed developers to embrace a more modern development paradigm. But it would be several years before Windows application developers could take advantage of the language improvements in C++, let alone any C++-based libraries that would further reduce the drudgery.

Consider, again, Petzold. In the very first edition of Programming Windows, which was published in 1998, Mr. Petzold referred to “objects” again and again.

“In object-oriented programming,” he wrote, “an ‘object’ is an abstract data type that consists of a data structure and various functions that act on that data structure. Objects receive ‘messages’ that cause them to change (that is, perform operations on) themselves.”

This language should sound familiar. Even in a non-OOP language like C, it is possible to create an object like a window, that consists of a data structure—the window class—and receives messages to which it can respond. You can see the difficulty of doing such a thing in straight C and the Windows API in Programming Windows: Hello, Windows (Premium).

Now, this doesn’t mean that C-based Windows programming was object-oriented. It’s more “object-based,” to reuse a phrase I used to describe Visual Basic, though these two environments couldn’t have been less alike from a practical day-to-day sense. But the rationale was there: When programming for Windows, you were indeed creating and manipulating “objects” like windows, controls, and so on. And that was true whether you were doing it ponderously, with C code, or using drag-and-drop from a control toolbox as with Visual Basic.

There is still some debate as to the OOP-ness, if you will, of these environments. The objects that Petzold referred to again and again as he updated the book through five editions were just objects in a general sense, and not “OOP objects” to use a redundant but necessary phrase. To the Windows API developer, windows, buttons, menu items, other controls were things they created and used. They were objects in the same way that apples or books were objects too Things with names. And attributes. Things that could be defined, created, and manipulated. Referred to. Poked and prodded. Interacted with.

The distinction there may seem like semantics, but it’s tied to the differences between C and C++.

C, like Visual Basic and all other programming languages, is what’s called a structured programming language. That is, it supports structured control flow elements like subroutines—named blocks of code that are executed together before returning control flow back to the point in the code where the subroutine was called—selection (if, then, and else), and repetition (while and for code blocks).

C++, like other OOP languages, is also a structured programming language. That’s why Windows API developers can use it today just like C developers did back in the late 1980s. C++ is, again, an extension (or superset, for the most part) to C. But C++ also supporting OOP, so there are some important differences between the two languages.

At its heart, C++ uses a data type called a class—thus explaining its original name, “C with Classes”—allowing developers to abstract an object’s state and behavior together. If this sounds a lot like Petzold’s description of a C “object” like a window—something that “consists of a data structure—the window class—and receives messages to which it can respond,” that is very much by design. The C++ class is based on the C data type struct. But a C++ class is different from a struct in some major ways related to OOP, the most important of which is abstraction.

That is, structs are public in the sense that their contents are open to any surrounding software code. But a class can include both public and private data and implementation details that are hidden to outside code. In OOP, an object is described an instance of a class that has public interfaces that can be accessed externally, and private data that cannot.

This design provides the start of a new modular programming style that has been refined over the years through the addition of new OOP-based languages—Java, C#, and so on—as well as new interface-based paradigms that have led, in turn, to software componentization. We’ll get to all that at a later date—this evolution was happening throughout the 1990s and into the 2000s, not just at Microsoft but elsewhere—but for purposes of this article, it’s perhaps more helpful to understand the genesis that led to these later innovations.

A C++ class provides encapsulation by hiding private data from outside code. That is, if there is a private, internal variable price that is defined as a floating point number and is set to a value of 5.0, no outside code can even “see” that variable, let alone change its value. But the class might include a public function, changePrice or whatever, that allows outside code to indirectly change the value of that variable. The outside code has no understanding of how the class is structured. It’s a black box. Instead, it can only “see” whatever public interfaces are available. Which include that function changePrice.

OOP languages like C++ also support the notion of polymorphism, where one can create different versions of class-based functions, called member functions, that perform similar operations on different types of data and can even have a different number of arguments (and/or types of arguments). For example, one might create multiple versions of the changePrice function I invented above perform other functions in addition to just changing the price. This can be thought of as overloading, and it also applies to C++ operators like “+”; when you use this operator with two integers, C++ knows to add them together, but when you use it with two strings, it concatenates the two into a single  string.

C++ and other OOP languages also support inheritance, where the developer creates a class that is derived from, or based on, another class, which is called the base class. Imagine you had a base class called vehicle that included basic information about this object type. You could then derive new classes from vehicle like motorcycle, car, truck, and more. And you could further derive new classes from car like coupe, sedan, convertible, and so on. In each case, the derived classes would inherit attributes, data, and member functions from their base class and would also add their own.

And not to get too complex here, but in each case noted above, each derived class would have the ability to call member functions inherited from their base class and/or override them with versions that are specific to that class. This is another form of polymorphism.

OK, this is all very complex, even though I’ve only brushed over the very top of it. But inheritance is used throughout various OOP frameworks, including the Microsoft Foundation Class (MFC) Library, which Microsoft created in C++, and Borland’s Visual Component Library (VCL), which worked with both C++ and Object Pascal. And that makes sense because GUIs really lend themselves well to this kind of abstraction. For example, there is a base toolbar type that has since been derived to create command bar and ribbon classes. (Or whatever.)

If there’s a problem with OOP, and there is, it’s related to overhead. Where Visual Basic spares the developer from having to deal with a lot of the software code that was required in C, true OOP languages like C++ do not. Likewise, OOP frameworks like MFC, which we’ll examine soon, can generate a lot of code for you, which is a nicety. But there is still a lot of code. Indeed, a basic MFC “Hello, world”-type app contains even more code than the C version. If you needed something very basic, C++—or, worse, C++ and MFC—was a bit much.

Put simply, MFC solved some problems, and it was particularly well-suited to certain scenarios, but it ultimately toppled under its own weight as the developer world moved, yet again, to embrace componentization, a more efficient form of OOP that persists to this day.

We’ll get to all that eventually. First, we’ll take a look at MFC, Microsoft’s first legitimate stab at OOP programming for Windows.

 

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