Reputation: 71
So I'm practicing subclassing a predefined window class in Win32 so I can define my own custom message proc for the predefined classes (e.g. making a custom WndProc for a button class) and I got it to work for the most part but I can't get the WM_COMMAND messages to be automatically sent to the new message proc. I can set up a case in the parent window for WM_COMMAND and check the wParam then call the respective new message proc for the child window that sent the WM_COMMAND message but I would prefer for this to be done automatically like all the other commands. As far as I can tell all the other WM_'s I have tried in the custom message proc I defined get passed to it automatically from the parent window's message proc, but the WM_COMMAND messages won't unless I explicitly redirect them. I have a feeling WM_COMMAND messages will always be sent to the parent window when subclassing the way I have it set up, but if someone could explain why this is the case or what I would need to do in order to direct all messages belonging to the button window to the custom WndProc I defined, it would be much appreciated.
Code:
#include <Windows.h>
#include <windowsx.h>
#define IDC_BUTTON 0
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK WndProcButton (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
HWND g_hwndButton = NULL;
WNDPROC g_wndProcButtonOrigianl = NULL;
BOOL g_bSeeingMouse = FALSE;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR szCmdLine, int iCmdShow)
{
static TCHAR szClassName[] = 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 = szClassName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szClassName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szClassName, // 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 WndProcButton (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch( message )
{
case WM_COMMAND:
MessageBox( hwnd, TEXT( "Test box" ), TEXT( "Test box" ), MB_OK );
SetFocus( g_hwndButton );
break;
default:
if( !g_bSeeingMouse && GetCapture() == hwnd )
{
g_bSeeingMouse = TRUE;
SetWindowText( hwnd, L"Ok +mouse" );
}
else if( g_bSeeingMouse && GetCapture() != hwnd )
{
g_bSeeingMouse = FALSE;
SetWindowText( hwnd, L"Ok" );
}
break;
}
return CallWindowProc( g_wndProcButtonOrigianl, hwnd, message, wParam, lParam );
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (message)
{
case WM_CREATE:
g_hwndButton = CreateWindow( L"Button", // predefined class
L"Ok", // button text
( WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON ), // styles
// Size and poition values are given explicitly, because
// the CW_USEDEFAULT constant gives zero values for buttons.
10, // starting x position
10, // starting y position
100, // button width
30, // button height
hwnd, // parent window
(HMENU)IDC_BUTTON, // no menu
(HINSTANCE)GetWindowLongPtr( hwnd, GWLP_HINSTANCE ),
NULL ); // pointer not needed
SetFocus( g_hwndButton );
g_wndProcButtonOrigianl = (WNDPROC)SetWindowLongPtr( g_hwndButton, GWLP_WNDPROC, (LONG_PTR)WndProcButton );
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) ;
}
Upvotes: 3
Views: 1897
Reputation: 596121
By design, WM_COMMAND
and WM_NOTIFY
messages are sent to a control's parent window. There is no way to make the API automatically send them to the control that generated them. The parent window must handle them, or forward them, as needed. There is no avoiding that.
In the VCL framework originally written by Borland and now owned by Embarcadero, it uses a simple system for forwarding such messages. When any parent window receives one of these messages, it increments the message ID by an offset that puts the message into the user-defined range, and then forwards the message to the child HWND specified in the message. So WM_COMMAND
becomes CN_COMMAND
and WM_NOTIFY
becomes CN_NOTIFY
, which the child control's window procedure can look for.
For example:
#define MY_COMMAND (WM_COMMAND + some_offset)
#define MY_NOTIFY (WM_NOTIFY + some_offset)
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
if (lParam != 0)
SendMessage((HWND)lParam, MY_COMMAND, wParam, lParam);
return 0;
case WM_NOTIFY:
SendMessage(((NMHDR*)lParam)->hwndFrom, MY_NOTIFY, wParam, lParam);
return 0;
...
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
LRESULT CALLBACK WndProcButton (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch( message )
{
case MY_COMMAND:
...
case MY_NOTIFY:
...
}
return CallWindowProc( g_wndProcButtonOrigianl, hwnd, message, wParam, lParam );
}
Upvotes: 6