Continuing With Charles Petzold's Programming Windows 5th Edition

I've been learning the Windows API for a couple of weeks now thanks to the help of Charles Petzold's book. Before I started this book I did my research and a lot of people had recommended this book despite its age (17 years old!).

I did have a few issues regarding the syntax with certain examples in terms of stuff just being simply outdated, but it wasn't anything major and I was able to correct the outdated stuff with a simple google search to find a more modern solution. Currently I'm at Chapter 3, where we create our first HELLOWIN program. It's a simple program that creates a window, displays some text in it and plays a sound file (.wav format). When I copied the code I couldn't get it to work correctly. The compiler wasn't showing any error messages, I was just getting a blank screen.

So after searching the web for a bit I found another simple window program example that I'm assuming is slightly more modern. In the end I ended up in a way, merging the two programs together to get the first one to run correctly.

Source Code of Charles Petzold HELLOWIN program:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     static TCHAR szAppName[] = TEXT ("HelloWin") ;
     HWND         hwnd ;
     MSG          msg ;
     WNDCLASS     wndclass ;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;

     if (!RegisterClass (&wndclass))
     {
          MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                      szAppName, MB_ICONERROR) ;
          return 0 ;
     }
     
     hwnd = CreateWindow (szAppName,                  // window class name
                          TEXT ("The Hello Program"), // window caption
                          WS_OVERLAPPEDWINDOW,        // window style
                          CW_USEDEFAULT,              // initial x position
                          CW_USEDEFAULT,              // initial y position
                          CW_USEDEFAULT,              // initial x size
                          CW_USEDEFAULT,              // initial y size
                          NULL,                       // parent window handle
                          NULL,                       // window menu handle
                          hInstance,                  // program instance handle
                          NULL) ;                     // creation parameters
     
     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;
     
     while (GetMessage (&msg, NULL, 0, 0))
     {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
     }
     return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     HDC         hdc ;
     PAINTSTRUCT ps ;
     RECT        rect ;
     
     switch (message)
     {
     case WM_CREATE:
          PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
          return 0 ;
          
     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
          
          GetClientRect (hwnd, &rect) ;
          
          DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
                    DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
          
          EndPaint (hwnd, &ps) ;
          return 0 ;
          
     case WM_DESTROY:
          PostQuitMessage (0) ;
          return 0 ;
     }
     return DefWindowProc (hwnd, message, wParam, lParam) ;
}


Source Code for the Newer Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <windows.h>
#include <windowsx.h>

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lparam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCMdLine, int nCmdShow){

 // the handle for the window, filled by a function
 HWND hWnd;
 // this structure holds information for the window class
 WNDCLASSEX wc;

    // clear out the window class for use
    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    // fill in the structure with the needed information
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW; // style of window
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); // mouse cursor style
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // the color of the window
    wc.lpszClassName = "WindowClass1"; // name of the window class

    // register the window class
    RegisterClassEx(&wc);

    // create the window and use the result as the handle
    hWnd = CreateWindowEx(NULL,
                          "WindowClass1", // name of the window class
                          "Our First Windowed Program", // title of the window
                          WS_OVERLAPPEDWINDOW, // window style
                          300, // x-position of window
                          300, // y-position of window
                          500, // width of the window
                          400, // height of the window
                          NULL, // we have no parent window
                          NULL, // we aren't using menus
                          hInstance, // application handle
                          NULL); // used with multiple windows

    // display the window on screen
    ShowWindow(hWnd, nCmdShow);

    // enter the main loop:

    // this structure holds Windows event messages
    MSG msg;

    // wait for the next message in the queue, store the result in 'msg'
    while(GetMessage(&msg, NULL, 0, 0)){

        // translate keystroke messages into the right format
        TranslateMessage(&msg);

        // send the message to the WindowProc function
        DispatchMessage(&msg);
    }

        // return this part of the WM_QUIT message to Windows
        return msg.wParam;
}

    // this is the main message handler for the program
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){

        // sort through and find what code to run for the message given
        switch(message){

            // this message is read when the window is closed
            case WM_DESTROY:
                {
                // close the application entirely
                PostQuitMessage(0);
                return 0;
                } break;
        }

    // handle any message the switch statement didn't
    return DefWindowProc (hWnd, message, wParam, lParam);

    }


The main notable changes I had to make was:

*Use the ZeroMemory() function. I'm assuming this function frees up enough memory to use the WNDCLASS.

*Use the RegisterClass() function. This seems like an incredibly important function, not sure why it was omitted in Charles Petzold's book.

*Remove if(!RegisterClass(&wndclass)). I had to remove that whole if statement, or else it was always displaying that if statement, and never my actual window program. I also tried putting RegisterClass() right before the if statement but it still wasn't working.

*The cases in the switch statement did not have break;. I'm guessing that early versions of C did not support break. Also it has no default which I find strange.

So I'm thinking to myself is it worth while to continue with this book? I really have been enjoying it this far. I have found few other resources on WIN API that go over as much detail as this book, but if I keep running into problems like forgetting to register the class then I feel like I'm setting myself up for failure. Especially when the examples get more complex and I can't just google the source code for another example that does the same thing. It seems that he has a 6th edition that goes over Windows 8 stuff like touch screen and what not, but it uses a lot of other languages which I'm not too interested in.

*Use the RegisterClass() function. This seems like an incredibly important function, not sure why it was omitted in Charles Petzold's book.

What do you mean by "omitted"? That function is used in the Petzold version you posted. It's your "found" function that doesn't use this function, but instead uses RegisterClassEx() a function which Petzold recommends against (page 427 of said book) because it has loss of intelligence compared to RegisterClass().


*Remove if(!RegisterClass(&wndclass)). I had to remove that whole if statement, or else it was always displaying that if statement, and never my actual window program. I also tried putting RegisterClass() right before the if statement but it still wasn't working.

Since you're not using the same wndclass then you need to remove or replace all occurances of RegisterClass() with RegisterClassEx().


*The cases in the switch statement did not have break;. I'm guessing that early versions of C did not support break. Also it has no default which I find strange.

No all versions of C have both break and default statements. The first program doesn't require the break statements because the author is using return instead. The default statement has always been optional and can be omitted if the case statements cover all possible cases.

So I'm thinking to myself is it worth while to continue with this book? I really have been enjoying it this far. I have found few other resources on WIN API that go over as much detail as this book, but if I keep running into problems like forgetting to register the class then I feel like I'm setting myself up for failure.

That book is considered the definitive reference for the Windows API using the C language. As already stated the Author didn't "forget" to register the class the if statement you tried to remove contained that function call.

*Use the ZeroMemory() function. I'm assuming this function frees up enough memory to use the WNDCLASS.

Why are you assuming anything? This function has documentation that tells you exactly how it works. You really need to read the documentation for the functions you're going to use, rather than blindly throwing code into your program. By the way this function doesn't free up any memory, it just fills the stated memory region with zeros.


Now since I don't do Windows I don't know how relevant the Win32 API is to today's operating systems so you may want to do some research on the newer Windows operating systems. Really if you want to do Windows programming you would probably be better off using something like C# using .Net instead of C and the Win32 API.


In addition to what jlb said, I'm not sure what the problem is. The original code compiles and works correctly in the latest version of VC++, as I'm certain it will in previous versions.

If RegisterClass is returning something unexpected, you should probably try to figure out why that is rather than change code randomly.

Thanks for the quick replies guys!
@jlb When you say that the RegisterClass() function is used in Petzold's version do you mean it's being used in if (!RegisterClass()). The way I interpreted this if statement was if RegisterClass() failed, execute the MessageBox() function saying "This program requires Windows NT!" So from this understanding RegisterClass() is never called?

@cire Yes the code compiles and works for me too, problem is I always get the MessageBox function popping up saying "This program requires Windows NT!".
This is embarrassing but today when I copy and pasted Petzold's version of the code everything worked perfectly. I guess yesterday while writing the program I must have made a careless mistake. Sorry for wasting your time guys!
Don't take this the wrong way, but your comments suggest your understanding of programming in general (and C++ or C in particular) is not very well developed.

So from this understanding RegisterClass() is never called?

Functions can return values. RegisterClass is returning a value. If it didn't return a value and an invocation of the function was isolated in an if condition, you would receive a compiler error. The fact that the body of the if is entered means that: RegisterClass was called and returned a value (0) that caused the code in the curly braces to be executed. If, instead of randomly changing code one were to visit the documentation and research the issue, one might gain a better understanding of what's happening.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms633586%28v=vs.85%29.aspx

Pay particular attention to the Return value documentation.
Thanks for clarifying the issue for me cire. I am by no means considering myself an expert in c, my apologies if I came off that way, I very much so understand that I'm a complete beginner. So from reading your post and the MSDN documentation if(!RegisterClass(&wndclass)) pretty much means execute RegisterClass() and if it happens to return a value of 0, proceed to run the code in the curly braces, if it succeeded it returns an ATOM which can be used to identify the class being registered?
Last edited on
Topic archived. No new replies allowed.