Implementing callback function for dialog-based application

3k views Asked by At

I was reading a very old tutorial on how to create a dialog-based GUI application in Visual C++ (source - in portuguese). Based on my weak knowledge on WinAPI programming I decided to modify the proposed code mainly to achieve two things:

1) Consistency with Unicode programming standard (using wWinMain and MessageBoxW instead of WinMain and MessageBox/MessageBoxA for instance). Such "standard" seems to be enforced all around. Example: this question (comments)

2) Consistency with the model presented by Visual Studio when creating a non-empty Win32 Project. I did that and noticed that:

  • The callback function for the "About" dialog box is of type INT_PTR instead of BOOL;
  • The WndProc function returns 0, instead of TRUE (which is 1) as is in the tutorial;
  • The switch statement for the msg variable defaults to return DefWindowProc() instead of FALSE;
  • WM_CLOSE handling is not specified (I guess dialogs have default handling for some events).

_
As a result, there's a weird behavior in which the MessageBox entitled Confirm is out-of-focus - i.e I can't click the OK and Cancel buttons.

Question: Am I correct in my assumption that the template code generated by Visual Studio is more correct than the code in the tutorial, which just doesn't seem trustable to me? If yes, did I forget anything? And what's wrong with my code, why can't I click the Messagebox buttons?

My final code follows:

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "resource.h"

INT_PTR CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            if (MessageBoxW(hWnd, L"Close?", L"Confirm", MB_OKCANCEL) == IDOK)
                DestroyWindow(hWnd);
            break;
        // more code to place here
        }
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
    int ret = DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_DIALOG1), NULL, WndProc);

    return 0;
}

For completeness, below is the code based on the tutorial, which actually works fine:

BOOL CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
        return TRUE;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            if (MessageBoxW(hWnd, L"Close?", L"Confirm", MB_OKCANCEL) == IDOK)
                DestroyWindow(hWnd);
            break;
        }
        break;
    default:
        return FALSE;
    }

    return TRUE;
}
1

There are 1 answers

0
Marc.2377 On BEST ANSWER

So, I got it. The code created by the Visual Studio wizard indeed demonstrates correct implementation of the Windows API.

Points to note:

  • A dialog box procedure should not call DefWindowProc(). That's why the MessageBox is not working, as noted by Hans Passant in the comments.

  • The purpose of the WndProc() function generated by VS is to process messages for the main application window, as its name suggests. There is another function, About(), which handles messages for the "About" dialog box.

  • The return type for the dialog box procedure is INT_PTR, and that's improvement over BOOL because it addresses platform differences by having a different size in a 64-bit environment, avoiding portability issues.

  • The switch block does not need a default clause:

Unlike a window procedure, a dialog box procedure never calls the DefWindowProc function. Instead, it returns TRUE if it processes a message or FALSE if it does not.
(Dialog Box Programming Considerations - MSDN)

  • Dialog boxes have default processing for events:

The (DialogBox) function (...) starts its own message loop to retrieve and dispatch messages for the dialog box.
(DialogBox function - MSDN)

There's no need to handle WM_DESTROY - or even WM_DESTROY as I did. In fact, doing so may lead to problems as seen here.