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 ofBOOL
; - The
WndProc
function returns 0, instead of TRUE (which is 1) as is in the tutorial; - The
switch
statement for themsg
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;
}
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 overBOOL
because it addresses platform differences by having a different size in a 64-bit environment, avoiding portability issues.The
switch
block does not need adefault
clause:There's no need to handle
WM_DESTROY
- or evenWM_DESTROY
as I did. In fact, doing so may lead to problems as seen here.