Trying to get a simple menu to appear

I have been an embedded designer for years and was trying my hand at c++ for windows, just for curiosity purposes and not really for development. I am reading Petzold's 'Programming Windows' book, which is very good. I was looking on his topic of 'Defining a Menu the Hard Way' on page 452, and not use a resource file. I can't get it to work, after getting a simple menu working with a resource file. After creating the menu, I don't see how to incorporate it into the Window, so that it displays. This is a simple 'File', with a popup of 'Open' and 'Exit'. Code is below. How do I tell the window to add the menu?

Sutton


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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "windows.h"

#define IDM_ABOUT   1
#define IDM_EXIT    2
#define IDM_HELP    3
#define IDM_OPEN    4

HINSTANCE hInst;
static WCHAR szTitle[] = TEXT("Point Repeater Analysis");
static WCHAR szAppName[] = TEXT("PointRepeaterAnalysis");
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


int CALLBACK wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int iCmdShow)
{
    WNDCLASS wcex;
    HMENU hMenu, hMenuPopup;
    MSG msg;
    HWND hWnd;

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

    if (!RegisterClass(&wcex))
    {
        MessageBox(NULL,TEXT("Call to RegisterClass failed!"),szAppName,NULL);
        return(1);
    }

    hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, 700, 400, NULL, NULL, hInstance, NULL);
    if (!hWnd)
    {
        MessageBox(NULL, TEXT("Call to CreateWindow failed!"), szAppName, NULL);
        return(1);
    }

    hMenu = CreateMenu();
    hMenuPopup = CreateMenu();
    AppendMenu(hMenuPopup, MF_STRING, IDM_OPEN, TEXT("Open"));
    AppendMenu(hMenuPopup, MF_STRING, IDM_EXIT, TEXT("Exit"));
    AppendMenu(hMenu, MF_POPUP, UINT(hMenuPopup), TEXT("File"));

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}


//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    WCHAR greeting[] = TEXT("Sutton - Point Repeater Analysis");

    switch (message)
    {
        case WM_COMMAND:
            // Parse the menu selections:

            switch (LOWORD(wParam))
            {
                case IDM_OPEN:
                    MessageBeep(0);
                    return(0);

                case IDM_EXIT:
                    SendMessage(hWnd, WM_CLOSE, 0, 0);
                    return(0);
            }
            break;

        case WM_PAINT:
            PAINTSTRUCT ps;
            HDC hdc;
            hdc = BeginPaint(hWnd, &ps);
            TextOut(hdc, 5, 5, greeting, lstrlen(greeting));
            EndPaint(hWnd, &ps);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}
Obviously a Windows question, not a C++ question.
Doesn't Petzold tell you how to do it in the book?
I don't use Windows, but a quick search turns up the SetMenu function:
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setmenu
Maybe you could ask here:
https://www.cplusplus.com/forum/windows/
dutch - Genius. Thank you. And, no, SetMenu() isn't shown in the code.
SetMenu() isn't shown in the code.


It's mentioned in the text. In my version (5th edition), it's mentioned on page 452, last sentence on the first paragraph under the heading 'Defining a Menu The Hard Way'.

However, you'd usually create the menu before creating the main window and use hMenu in CreateWindow() as the 3rd from last param.

So you'd have (not tried):

1
2
3
4
5
6
7
8
9
10
11
12
hMenu = CreateMenu();
hMenuPopup = CreateMenu();
AppendMenu(hMenuPopup, MF_STRING, IDM_OPEN, TEXT("Open"));
AppendMenu(hMenuPopup, MF_STRING, IDM_EXIT, TEXT("Exit"));
AppendMenu(hMenu, MF_POPUP, UINT(hMenuPopup), TEXT("File"));

hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, 700, 400, NULL, hMenu, hInstance, NULL);
if (!hWnd)
{
    MessageBox(NULL, TEXT("Call to CreateWindow failed!"), szAppName, NULL);
    return(1);
}

The Windows API has had changes in the years since Petzold wrote the source code for the 5th edition of his Programming Windows book.

There is (mostly) updated source code available:
https://github.com/recombinant/petzold-pw5e

Windows 98/Me (16-bit) is dead, no need to still support ASCII characters any more. No need to wrap your strings in the TEXT() macro, use the W (Unicode) version of Win API functions when available.

Long pointers are an artifact of 16-bit Windows, Windows is now 32 or 64 bit. PWSTR instead of LPWSTR.

LoadIcon and LoadCursor are deprecated, use LoadImage instead.

With a couple of additional modifications you can have source code that compiles without warnings for 64- and 32-bit.
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include "windows.h"

#define IDM_ABOUT   1
#define IDM_EXIT    2
#define IDM_HELP    3
#define IDM_OPEN    4

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

int CALLBACK wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR lpCmdLine, _In_ int iCmdShow)
{
   WCHAR szTitle[]   = L"Point Repeater Analysis";
   WCHAR szAppName[] = L"PointRepeaterAnalysis";

   WNDCLASSW wc;

   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstance;
   wc.hIcon         = (HICON)   LoadImageW(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
   wc.hCursor       = (HCURSOR) LoadImageW(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
   wc.hbrBackground = (HBRUSH)  (COLOR_WINDOW + 1);
   wc.lpszMenuName  = 0;  // no need to define a class menu just yet, a custom menu will be used
   wc.lpszClassName = szAppName;

   if (!RegisterClassW(&wc))
   {
      MessageBoxW(NULL, L"Call to RegisterClass failed!", szAppName, NULL);
      return 1;
   }

   // create your custom menu
   HMENU hMenu      = CreateMenu();
   HMENU hMenuPopup = CreateMenu();

   // the '&' adds an underscore to the menu item for keyboard access
   AppendMenuW(hMenuPopup, MF_STRING, IDM_OPEN, L"&Open");
   AppendMenuW(hMenuPopup, MF_STRING, IDM_EXIT, L"E&xit");
   AppendMenuW(hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, L"&File");  // the cast is for x86/x64 compatibility

   HWND hWnd = CreateWindowW(szAppName, szTitle, WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, CW_USEDEFAULT, 700, 400,
                             NULL, hMenu,  // create the app's main window with your custom menu
                             hInstance, NULL);

   if (!hWnd)
   {
      MessageBoxW(NULL, L"Call to CreateWindow failed!", szAppName, NULL);
      return 1;
   }

   // register the menu with the app
   SetMenu(hWnd, hMenu);

   ShowWindow(hWnd, iCmdShow);
   UpdateWindow(hWnd);

   MSG msg;

   // Main message loop:
   while (GetMessageW(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessageW(&msg);
   }

   return (int) msg.wParam;  // the cast is for x86/x64 compatibility
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   switch (message)
   {
   case WM_COMMAND:
      // Parse the menu selections:
      switch (LOWORD(wParam))
      {
      case IDM_OPEN:
         MessageBeep(0);
         return 0;

      case IDM_EXIT:
         SendMessageW(hWnd, WM_CLOSE, 0, 0);
         return 0;
      }
      break;

   case WM_PAINT:
   {
      PAINTSTRUCT ps;
      HDC         hdc;
      WCHAR       greeting[] = L"Sutton - Point Repeater Analysis";

      hdc = BeginPaint(hWnd, &ps);
      TextOutW(hdc, 5, 5, greeting, lstrlenW(greeting));
      EndPaint(hWnd, &ps);
      return 0;
   }

   case WM_DESTROY:
      PostQuitMessage(0);
      return 0;
   }

   return DefWindowProcW(hWnd, message, wParam, lParam);
}
You should already be familiar with the original Windows data types.
https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types

64-bit Windows adds several new types and makes some modifications to existing types.
https://docs.microsoft.com/en-us/windows/win32/winprog64/the-new-data-types

Pointers require a bit more care now, with new Win API functions.
https://docs.microsoft.com/en-us/windows/win32/winprog64/rules-for-using-pointers

Even if you don't target for 64-bit Windows it doesn't hurt to write code that will work for either 32- or 64-bit.
Topic archived. No new replies allowed.