Search code examples
c++event-handlingmessagesubclasssubclassing

Subclassed Control only Recieving Parent Messages


I have three controls that sit atop the parent window. One accepts user input, whilst the other displays previous inputs. The third is a button that appends the text from the input edit control to the output control. However, as it is cumbersome to do so, I want to add a handle for the 'enter' key to perform this operation. Currently, I have subclassed the input edit control. However, no matter which configuration of switch statements I implement, I cannot get the 'WM_KeyDown' message to be interpreted from the Input control. It is, however, sent from the parent control if the parent control is in focus. It is not sent from the edit control, even when in focus. I have found several solutions already, but none of them so far work.

Any help would be greatly appreciated!

Win32 C++, Visual Studio 2012

#include <Windows.h>
#include <string.h>
#include <tchar.h>
#include <stdlib.h>
#include <Commctrl.h>

#include "resource.h"
#include "Processing.h"

static TCHAR WinClass[]=_T("SAPA");
static TCHAR WinTitle[]=_T("SAPA");

HWND InText, OutText, Send;
LPWSTR string;

LPTSTR buff = new TCHAR [1024];
LPTSTR Tbuff = new TCHAR [1024];

HINSTANCE hInst;

syscore sysCore;

WNDPROC DefProc;
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
DWORD dwRefData;
UINT uIdSubClass;

int WINAPI WinMain(HINSTANCE hIn, HINSTANCE hpIn, LPSTR CmdLine, int CmdShow)
{
WNDCLASSEX wcex;

wcex.cbSize         =   sizeof(WNDCLASSEX);
wcex.style          =   CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc    =   WndProc;
wcex.cbClsExtra     =   0;
wcex.cbWndExtra     =   0;
wcex.hInstance      =   hIn;
wcex.hIcon          =   LoadIcon(hIn,MAKEINTRESOURCE(APP_ICO));
wcex.hCursor        =   LoadCursor(NULL,IDC_ARROW);
wcex.hbrBackground  =   (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName   =   L"IDC_WINMENU";
wcex.lpszClassName  =   WinClass;
wcex.hIconSm            =   LoadIcon(wcex.hInstance, MAKEINTRESOURCE(APP_ICO));

if (!RegisterClassEx(&wcex))
{
    MessageBox(NULL,_T("RegClassEx Failure"),_T("ERROR"),NULL);
    return 1;
}

hInst=hIn;

HWND hwnd=CreateWindow(WinClass, WinTitle,WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 800,650,NULL,NULL, hIn,NULL);

if (!hwnd)
{
    MessageBox(NULL,_T("CreateWindow Failuer"),_T("ERROR"),NULL);
    return 1;
}

ShowWindow(hwnd, CmdShow);

UpdateWindow(hwnd);

MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

return (int) msg.wParam;

}

LRESULT CALLBACK InEditControlProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
//  if (LOWORD(wp)==IDM_IN)
    switch(msg) {
    case WM_COMMAND:
        if (LOWORD(wp)==IDM_IN)
            if (HIWORD(wp)==WM_KEYDOWN)
                if (HIWORD(lp)==VK_LCONTROL)
                    PostQuitMessage(0);
        break;
        case WM_KEYDOWN: {
        switch (wp) {
            case VK_RETURN: {
                PostQuitMessage(0);
                    //simulate a button press, see below
                   // return 0;//if the procedure responds the action, finish the control procedure
                    break;
                }
                break;
            }
        }
        break;
            //return CallWindowProc(WndProc,hwnd,msg,wp,lp);//WndProc(hwnd,msg,wp,lp);
        default:
        return CallWindowProc(WndProc,hwnd,msg,wp,lp);//WndProc(hwnd,msg,wp,lp);
        //return WndProc(hwnd,msg,wp,lp);
    }
    return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
    int len=0;
    //LPCWSTR buffer=L"";
    switch (msg)
    {
    case WM_CREATE:
        OutText=CreateWindow(L"edit", L"",ES_READONLY|ES_MULTILINE|WS_CHILD|WS_VISIBLE|WS_BORDER|ES_AUTOVSCROLL, 0, 0, 800,550,hwnd,(HMENU)IDM_OUT, NULL,NULL);
        InText=CreateWindow(L"edit", L"",WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 550, 720,50,hwnd,(HMENU)IDM_IN, NULL,NULL);
        Send=CreateWindow(L"button", L"SEND",WS_CHILD|WS_VISIBLE|WS_BORDER, 720, 550, 80,25,hwnd,(HMENU)MSG_SEND, NULL,NULL);
        CreateWindow(L"button", L"CLEAR",WS_CHILD|WS_VISIBLE|WS_BORDER, 720, 575, 80,25,hwnd,(HMENU)IDM_CLEAR, NULL,NULL);

        //DefProc = (WNDPROC)GetWindowLong(hwnd,GWL_WNDPROC);
        //SetWindowLong(hwnd,GWL_WNDPROC,(LONG_PTR)InEditControlProc);
        DefProc=(WNDPROC)SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LONG_PTR)InEditControlProc);
        //SetWindowSubclass(hwnd,InEditControlProc,uIdSubClass,dwRefData);
        ShowWindow(InText,SW_SHOW);
        SetFocus(InText);

        startup(hwnd,OutText,InText,sysCore);
        break;
    case WM_DESTROY:
        SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LONG_PTR)WndProc);
        PostQuitMessage(0);
        break;
    case WM_COMMAND:

        switch (LOWORD(wp))
        {
        case MSG_SEND:
            //TCHAR buff[1024];
            buff = new TCHAR [1024];
            Tbuff = new TCHAR [1024];
            GetWindowText(GetDlgItem(hwnd,IDM_IN),buff,1024);//get text from box, store to buffer
            GetWindowText(GetDlgItem(hwnd,IDM_OUT),Tbuff,1024);//retain window text
            SetWindowText(GetDlgItem(hwnd,IDM_IN),L"");//clear field
            if (buff!=L"")//dont pass if buffer empty
            {
            AppendText(hwnd,buff);
            AppendText(hwnd,L"\r\n");
            }
            handleInput(hwnd,buff,sysCore);
            //SetWindowText(GetDlgItem(hWnd,OFIELD),buff);//set text
            delete buff,Tbuff;
            break;
        case IDM_CLEAR:
            SetWindowText(GetDlgItem(hwnd,IDM_OUT),L"");
            break;
        case IDM_ABOUT:
            MessageBox(hwnd,L"App information",L"About",0);
            break;
        case IDM_EXIT:
            PostQuitMessage(0);
        }

    default:
        return DefWindowProc(hwnd, msg,wp,lp);
        break;
    }
    return 0;
}

Solution

  • Ok, after a few days of looking around, I figured it out, much in part to learning to use the spy++ utility.

    I found that after subclassing, adding the line

    if(msg.message==WM_KEYDOWN)
         SendMessage(hwnd,msg.message,msg.wParam,msg.lParam);
    

    in the message pump after Translate/DispatchMessage allows these custom signals to be routed into the correct procedure.