NO

Author Topic: Sub-classing question  (Read 4140 times)

Offline aj666

  • Member
  • *
  • Posts: 6
Sub-classing question
« on: June 20, 2020, 05:14:03 AM »
Can someone please tell me why this application is not returning correctly from my sub-classed child EDIT control.

The code shown is the bare minimum to demonstrate my problem. The code I am working on has multiple child windows.

My aim in the eventual application is to set conditions in a child EDIT window procedure, then to call a function to re-size multiple child windows.

My aim in the test application is for the child window to re-size when the mouse is clicked in the child window. The re-size only occurs when the mouse is then clicked in the parent window.

The code includes both old and new sub-class methods. These can be switched from one to the other by making changes in four places within the code. The code compiles successfully with both methods and gives the same result with both methods.

I know the application is not returning correctly from the EDIT window because I have found that a symptom of this is the LPCTSTR entry "hwnd_2" not displaying in the EDIT window. Turning sub-classing off by commenting out the calls to the Old_Subclass_hwnd_2 and New_Subclass_hwnd_2 functions will result in "hwnd_2" being displayed.

I am very new to C source code and have only just begun using the Pelles IDE. I obviously do not have the sub-class code correct.

Help would be appreciated please.

Code: [Select]
#include <windows.h>
#include <commctrl.h>


WNDPROC hwnd_2_RtnProc; //Global - Used for old and new sub-classing methods
ULONG_PTR ul_ptr; //Global - Used for the new sub-classing method

int hwnd_2_width = 0; //Global - Used to change the size of hwnd_2
int hwnd_2_height = 0; //Global - Used to change the size of hwnd_2

#define IDC_HWND_2 152


//=============================================================

//Old Subclass procedure CALLBACK
//LRESULT CALLBACK hwnd_2_SubProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)


//New Subclass procedure CALLBACK
LRESULT CALLBACK hwnd_2_SubProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (msg)
{
case WM_LBUTTONUP:

MessageBox(hwnd, "WM_LBUTTONUP", "hwnd_2_SubProc",
MB_OK | MB_ICONINFORMATION);

hwnd_2_width = 100;
hwnd_2_height = 100;

HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);//Not required for the old subclass method

//Old Subclass return
//return CallWindowProc(hwnd_2_RtnProc, hwnd_2, msg, wParam, lParam);

//New Subclass return
return DefSubclassProc(hwnd_2, msg, wParam, lParam);

break;

case WM_SIZE:
{

//Old Subclass return
//return CallWindowProc(hwnd_2_RtnProc, hwnd_2, msg, wParam, lParam);

//New Subclass return
return DefSubclassProc(hwnd_2, msg, wParam, lParam);

}

default:
return DefWindowProc(hwnd, msg, wParam, lParam);

}
return 0;

}


//=============================================================

void Old_Subclass_hwnd_2(HWND hwnd)
{
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
hwnd_2_RtnProc = (WNDPROC)GetWindowLong(hwnd_2, GWLP_WNDPROC);//get the default procedure
SetWindowLong(hwnd_2, GWLP_WNDPROC, (LONG)hwnd_2_SubProc);//set the custom procedure
}

//=============================================================

void New_Subclass_hwnd_2(HWND hwnd)
{
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
SetWindowSubclass(hwnd_2, &hwnd_2_SubProc, 1, (DWORD_PTR)ul_ptr); //Note the first parameter is the window being sub-classed

}

//=============================================================

//Step 7: The main Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
HWND hwnd_2;

hwnd_2 = CreateWindowEx(
WS_EX_CLIENTEDGE,
"EDIT",
"hwnd_2",
WS_CHILD | WS_VISIBLE | WS_SIZEBOX | ES_MULTILINE,
0, 0, 300, 300,
hwnd,
(HMENU)IDC_HWND_2,
GetModuleHandle(NULL),
NULL);

if(hwnd_2 == NULL)

MessageBox(NULL, "hwnd_2 Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);

//Old Subclass hwnd_2
//Old_Subclass_hwnd_2(hwnd);

//New Subclass hwnd_2
New_Subclass_hwnd_2(hwnd);

}
break;

case WM_LBUTTONUP:

MessageBox(hwnd, "WM_LBUTTONUP", "WndProc",
MB_OK | MB_ICONINFORMATION);

HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);

SetWindowPos(hwnd_2, NULL, 0, 0, hwnd_2_width, hwnd_2_height, SWP_NOZORDER);

break;

case WM_CLOSE:
DestroyWindow(hwnd);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;


case WM_SIZE:
{

}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

//=============================================================

//Step 1: The application entry (winmain)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{

// Step 2: Create the main window class
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;

wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName  = NULL;
wc.lpszClassName = "WindowClass_Main";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);


// Step 3: Register the class and check for registration
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Step 4: Create the main Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"WindowClass_Main",
"Test application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 400,
NULL, NULL, hInstance, NULL);

//Step 4: Check that the main window was created
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Step 5: Show the main window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Step 6: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
« Last Edit: June 20, 2020, 03:08:43 PM by frankie »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Re: Sub-classing question
« Reply #1 on: June 20, 2020, 04:55:46 PM »
First of all to avoid problems when compiling for 64bits to retrieve and set windows procedure use the new versions 'GetWindowLongPtr()' and 'SetWindowLongPtr()' instead of 'GetWindowLong()' and 'SetWindowLong()'.
The second point is that you have to pass the subclassed window handle to the original procedure. In your code you try to pass an undefined handle retrieved this way:
Code: [Select]
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);//Not required for the old subclass method

The working code is the following. Define/Undefine the symbol 'SUBCLASS_OLD' to use old or new subclassing method.

Code: [Select]
#include <windows.h>
#include <commctrl.h>

WNDPROC hwnd_2_RtnProc; //Global - Used for old and new sub-classing methods
ULONG_PTR ul_ptr; //Global - Used for the new sub-classing method

int hwnd_2_width = 0; //Global - Used to change the size of hwnd_2
int hwnd_2_height = 0; //Global - Used to change the size of hwnd_2

#define IDC_HWND_2 152

//#define SUBCLASS_OLD

//=============================================================

#ifdef SUBCLASS_OLD
//Old Subclass procedure CALLBACK
LRESULT CALLBACK hwnd_2_SubProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
#else
//New Subclass procedure CALLBACK
LRESULT CALLBACK hwnd_2_SubProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
#endif
{
switch (msg)
{
case WM_LBUTTONUP:
MessageBox(hwnd, "WM_LBUTTONUP", "hwnd_2_SubProc", MB_OK | MB_ICONINFORMATION);
hwnd_2_width  = 100;
hwnd_2_height = 100;
break;

case WM_SIZE:
break;
}
#ifdef SUBCLASS_OLD
//Old Subclass return
return CallWindowProc(hwnd_2_RtnProc, hwnd, msg, wParam, lParam);
#else
//New Subclass return
return DefSubclassProc(hwnd, msg, wParam, lParam);
#endif
}

#ifdef SUBCLASS_OLD

//=============================================================

void Old_Subclass_hwnd_2(HWND hwnd)
{
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
hwnd_2_RtnProc = (WNDPROC)GetWindowLongPtr(hwnd_2, GWLP_WNDPROC); //get the default procedure
SetWindowLongPtr(hwnd_2, GWLP_WNDPROC, (LONG_PTR)hwnd_2_SubProc); //set the custom procedure
}
#else
//=============================================================

void New_Subclass_hwnd_2(HWND hwnd)
{
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
SetWindowSubclass(hwnd_2, &hwnd_2_SubProc, 1, (DWORD_PTR)ul_ptr); //Note the first parameter is the window being sub-classed

}
#endif

//=============================================================

//Step 7: The main Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
HWND hwnd_2;
hwnd_2 = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "hwnd_2",
WS_CHILD | WS_VISIBLE | WS_SIZEBOX | ES_MULTILINE, 0, 0, 300, 300,
hwnd, (HMENU)IDC_HWND_2, GetModuleHandle(NULL), NULL);
if (hwnd_2 == NULL)
MessageBox(NULL, "hwnd_2 Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
#ifdef SUBCLASS_OLD
//Old Subclass hwnd_2
Old_Subclass_hwnd_2(hwnd);
#else
//New Subclass hwnd_2
New_Subclass_hwnd_2(hwnd);
#endif
}
break;

case WM_LBUTTONUP:
MessageBox(hwnd, "WM_LBUTTONUP", "WndProc", MB_OK | MB_ICONINFORMATION);
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
SetWindowPos(hwnd_2, NULL, 0, 0, hwnd_2_width, hwnd_2_height, SWP_NOZORDER);
break;

case WM_CLOSE:
DestroyWindow(hwnd);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

case WM_SIZE:
{
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

//=============================================================

//Step 1: The application entry (winmain)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

// Step 2: Create the main window class
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;

wc.cbSize = sizeof(WNDCLASSEX);
wc.style         = 0;
wc.lpfnWndProc   = WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hInstance;
wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszMenuName  = NULL;
wc.lpszClassName = "WindowClass_Main";
wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

// Step 3: Register the class and check for registration
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Step 4: Create the main Window
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "WindowClass_Main", "Test application",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 400, NULL, NULL, hInstance, NULL);

//Step 4: Check that the main window was created
if (hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Step 5: Show the main window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Step 6: The Message Loop
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline aj666

  • Member
  • *
  • Posts: 6
Re: Sub-classing question
« Reply #2 on: June 22, 2020, 07:38:20 AM »
Than you very much for your help Frankie. The code works much better but I am
 still missing some understanding of sub-classing.
The WM_LBUTTONUP mouse action in the child window does not arrive in the main procedure. When I click in the child window the size variables are updated but I must click a second time in the main window for the child window to re-size. I have read all I can find on this subject and my understanding is that the child window messages should be sent to the main procedure. Are you able to enlighten me on
what I have missed please.
Allan

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Re: Sub-classing question
« Reply #3 on: June 22, 2020, 01:32:47 PM »
Than you very much for your help Frankie. The code works much better but I am
 still missing some understanding of sub-classing.
The WM_LBUTTONUP mouse action in the child window does not arrive in the main procedure. When I click in the child window the size variables are updated but I must click a second time in the main window for the child window to re-size. I have read all I can find on this subject and my understanding is that the child window messages should be sent to the main procedure. Are you able to enlighten me on
what I have missed please.
Allan
Ok what you misunderstood is that the message is sent to the main window, but dispatched to the child window that is the legal recipient, to be handled.
The main window handles only messages intended for it, not all other messages passing for the message-pump code.
Eventually you want re-route the message to the main window as in the updated code below:
Code: [Select]
#include <windows.h>
#include <commctrl.h>

WNDPROC hwnd_2_RtnProc; //Global - Used for old and new sub-classing methods
ULONG_PTR ul_ptr; //Global - Used for the new sub-classing method

int hwnd_2_width = 0; //Global - Used to change the size of hwnd_2
int hwnd_2_height = 0; //Global - Used to change the size of hwnd_2

#define IDC_HWND_2 152

//#define SUBCLASS_OLD

//=============================================================

#ifdef SUBCLASS_OLD
//Old Subclass procedure CALLBACK
LRESULT CALLBACK hwnd_2_SubProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
#else
//New Subclass procedure CALLBACK
LRESULT CALLBACK hwnd_2_SubProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
#endif
{
switch (msg)
{
case WM_LBUTTONUP:
MessageBox(hwnd, "WM_LBUTTONUP", "hwnd_2_SubProc", MB_OK | MB_ICONINFORMATION);
hwnd_2_width  = 100;
hwnd_2_height = 100;
SendMessage(GetParent(hwnd), msg, wParam, lParam);
break;

case WM_SIZE:
break;
}
#ifdef SUBCLASS_OLD
//Old Subclass return
return CallWindowProc(hwnd_2_RtnProc, hwnd, msg, wParam, lParam);
#else
//New Subclass return
return DefSubclassProc(hwnd, msg, wParam, lParam);
#endif
}

#ifdef SUBCLASS_OLD

//=============================================================

void Old_Subclass_hwnd_2(HWND hwnd)
{
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
hwnd_2_RtnProc = (WNDPROC)GetWindowLongPtr(hwnd_2, GWLP_WNDPROC); //get the default procedure
SetWindowLongPtr(hwnd_2, GWLP_WNDPROC, (LONG_PTR)hwnd_2_SubProc); //set the custom procedure
}
#else
//=============================================================

void New_Subclass_hwnd_2(HWND hwnd)
{
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
SetWindowSubclass(hwnd_2, &hwnd_2_SubProc, 1, (DWORD_PTR)ul_ptr); //Note the first parameter is the window being sub-classed

}
#endif

//=============================================================

//Step 7: The main Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
HWND hwnd_2;
hwnd_2 = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "hwnd_2",
WS_CHILD | WS_VISIBLE | WS_SIZEBOX | ES_MULTILINE, 0, 0, 300, 300,
hwnd, (HMENU)IDC_HWND_2, GetModuleHandle(NULL), NULL);
if (hwnd_2 == NULL)
MessageBox(NULL, "hwnd_2 Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
#ifdef SUBCLASS_OLD
//Old Subclass hwnd_2
Old_Subclass_hwnd_2(hwnd);
#else
//New Subclass hwnd_2
New_Subclass_hwnd_2(hwnd);
#endif
}
break;

case WM_LBUTTONUP:
MessageBox(hwnd, "WM_LBUTTONUP", "WndProc", MB_OK | MB_ICONINFORMATION);
HWND hwnd_2;
hwnd_2 = GetDlgItem(hwnd, IDC_HWND_2);
SetWindowPos(hwnd_2, NULL, 0, 0, hwnd_2_width, hwnd_2_height, SWP_NOZORDER);
break;

case WM_CLOSE:
DestroyWindow(hwnd);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

case WM_SIZE:
{
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

//=============================================================

//Step 1: The application entry (winmain)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

// Step 2: Create the main window class
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;

wc.cbSize = sizeof(WNDCLASSEX);
wc.style         = 0;
wc.lpfnWndProc   = WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hInstance;
wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszMenuName  = NULL;
wc.lpszClassName = "WindowClass_Main";
wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

// Step 3: Register the class and check for registration
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Step 4: Create the main Window
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "WindowClass_Main", "Test application",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 400, NULL, NULL, hInstance, NULL);

//Step 4: Check that the main window was created
if (hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}

// Step 5: Show the main window
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

// Step 6: The Message Loop
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
But as a rule of thumb it is not a really big idea to dispatch the messages bumping them forward and back.
Maybe you want to review the design.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline aj666

  • Member
  • *
  • Posts: 6
Re: Sub-classing question
« Reply #4 on: June 28, 2020, 04:41:06 AM »
Thank you again for your help Frankie. Very enlightening. In what I have read about sub-classing, child window messages were sent to the parent window without the need for re-direction and the child window messages were then processed in the parent window procedure. I have been unable to add text to a sub-classed child window or re-size a sub-classed child window from within the child window's own procedure so I accepted what I had read.

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Re: Sub-classing question
« Reply #5 on: June 28, 2020, 03:35:24 PM »
Thank you again for your help Frankie. Very enlightening. In what I have read about sub-classing, child window messages were sent to the parent window without the need for re-direction and the child window messages were then processed in the parent window procedure. I have been unable to add text to a sub-classed child window or re-size a sub-classed child window from within the child window's own procedure so I accepted what I had read.
I'm glad that you accepted the way things has been designated. So you can start looking on advanced topics: notifications.
The parent of a child window could be notified of messages sent to the child through the WM_PARENTNOTIFY message.
This could be helpful, but it's also very tricky. Be careful about which window is parent of what child, and that the notification hasn't been disabled (WS_EX_NOPARENTNOTIFY extended window style set by default in dialog boxes). If you need to to be notified only in the direct parent window, and the notification is in the list of those supported by WM_PARENTNOTIFY message, you can avoid subclassing in theory.
As a hint early versions of MFC subclassed all windows notifying ascending way each parent up to the one that was in need to handle the message...  ::)
« Last Edit: June 28, 2020, 09:16:49 PM by frankie »
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide