Windows turns 30 – program like it’s 1985!

Since today, 20th November 2015, marks the 30th birthday of Windows, I thought it appropriate to commemorate its anniversary by programming Windows like it’s 1985. To put that in perspective, these were the days when Miami Vice was the hottest thing on the tube, people dug tunnels under the Berlin Wall and 20 MB was a reasonable size for your hard disk, which you called “Winchester”.

While most developers ignored Windows until version 3 was released in 1990, with good reason, those who didn’t were in for a programming experience that was not for the faint of heart. The “Hello World” program was famously four printed pages long, which rather set the tone. That was however a bit cruel, since a smaller Hello World would have been possible and might have scared fewer programmers away from Windows development. To honour Windows 30th birthday, I’d like to present you with a slightly more manageable Windows Hello World. So, get into the mood with some Dire Straits, Aha, Duran Duran or whatever you were exposed to back then, fire up the Quattro and…

Program like it’s 1985!

1980’s Windows programming entails void pointers, weakly typed wParam and lParam parameters and lots of other goodies that kept programmers of the era awake during endless, nightly debugging sessions. C was the lingua franca for PC programmers and C++ was but a distant dream (the pricey Glockenspiel C++ to C precompiler was by no means for the common programmer). Thus, my example will use C. It will be compilable by Visual Studio 2015 and have a few, but only a few, adjustments necessary for compiling and running in the present day (2015).

The window procedure

Let’s start with the core functionality of our program: displaying our text message in a window. The Windows environment (it’s 1985, so Windows not an OS, but an environment running under MS-DOS) sends events in the form of messages to all open windows. These messages are handled by a window procedure and the message we want to respond to is WM_PAINT. We also want the application to quit when the window is closed (WM_DESTROY), and we’ll send any other messages along to the default window procedure DefWindowProc:

long FAR PASCAL HelloWndProc(HWND hWnd, unsigned message, WORD wParam, LONG lParam)
{
    PAINTSTRUCT paintStruct;
    char *szWindowText = "The 80's called and wanted its monolithic switch/case message parsing back.";

    switch (message) {
    case WM_PAINT:
        BeginPaint(hWnd, &paintStruct);
        TextOut(paintStruct.hdc, 10, 10, szWindowText, strlen(szWindowText));
        EndPaint(hWnd, &paintStruct);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }
    return 0L;
}

paintStruct.hdc is a “device contect”, of which there where very few, and if we didn’t release them we’d run out of them in no time, causing our brand new 8 MHz 80286 PC/AT to crash. In the above code, EndPaint will release the DC. You may ask if it wouldn’t be more tidy to declare paintStruct and szWindowText where they’re used, instead of having them visible in the entire function. Well, it’s 1985 and we can only declare variables at the top of a function. In a real application, this wouldn’t be an issue, since we’d write a separate paint function and call it in the WM_PAINT case.

The window class

Now, we need to tell Windows to send its messages to HelloWndProc. This is done by creating a window class. A class in C? Well, not quite, but the Windows API has a notion of classes nontheless. We create a class and register it with the API like this:

char *szAppName = "MinimalHelloWorld";

int HelloInit(HINSTANCE hInstance)
{
    PWNDCLASS pHelloClass;

    pHelloClass = LocalAlloc(LPTR, sizeof(WNDCLASS));

    pHelloClass->hCursor = LoadCursor(NULL, IDC_ARROW);
    pHelloClass->hIcon = NULL;
    pHelloClass->lpszMenuName = NULL;
    pHelloClass->lpszClassName = szAppName;
    pHelloClass->hbrBackground = GetStockObject(WHITE_BRUSH);
    pHelloClass->hInstance = hInstance;
    pHelloClass->style = CS_HREDRAW | CS_VREDRAW;
    pHelloClass->lpfnWndProc = (WNDPROC)HelloWndProc;

    if (!RegisterClass(pHelloClass))
        return FALSE;

    LocalFree(pHelloClass);
    return TRUE;
}

The most important properties of the window class are the window procedure, lpfnWndProc, and the class name, lpszClassName. The latter will be used when creating windows of this class. Note the lp prefixes, which mean “long pointer” (or “far pointer”) and remind us of x86’s infamous segmented memory, a cruel way of tormenting programmers of days bygone.

Firing it all up: WinMain

The entry point for a Windows program is the WinMain function. One of the arguments supplied to WinMain is hInstance, which represents the current instance of your application. It may seem as you have some degree of type safety, as hInstance is of type HINSTANCE, but that’s actually just an untyped pointer (void *). If you want type safety, you have to wait 6 years for the Borland C++ and Object Windows Library (OWL) combo to be released in 1991. In WinMain, we create a window of the class we just created, receive windows messages and tell the Windows API to dispatch those messages to the appropriate windows procedure:

char *szWindowTitle = "Minimal Hello Windows 1985-style!";

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int cmdShow)
{
    MSG   msg;
    HWND  hWnd;

    if (!hPrevInstance)
        if (!HelloInit(hInstance))
            return FALSE;

    hWnd = CreateWindow(
        szAppName,
        szWindowTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
        );

    ShowWindow(hWnd, cmdShow);
    UpdateWindow(hWnd);

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

The listing in its entirety is available at GitHub. Note the 8 character file name used there, another limitation we enjoyed in the glorious world of 80’s computing.

1985 style Windows code running under Windows 8.1.

1985 style Windows code running under Windows 8.1.

Corresponding WPF code

For comparison, here’s the corresponding F# code for WPF:

[<EntryPoint; STAThread>]
let main argv = 
    let windowTitle = "Minimal Hello Windows 2015-style!";
    let windowText  = "Some progress has been made during the last 30 years.";

    let textBlock = new TextBlock(Text = windowText, Margin = Thickness(10.))
    let win = new Window(Title = windowTitle, Content = textBlock)
    Application().Run(win)

Why I never programmed like it was 1985

Fortunately for me, I never had to program like this. I took up Windows programming after version 3.0, the first Windows version fit for serious use, had been released. Thanks to Borland’s Object Windows Library (OWL), for which I was a beta tester, I only had to dive into the dirty API details when I chose so of my own free will. Which I just did for old times sakes, writing this blog post.

 

  1. No comments yet.

  1. No trackbacks yet.