0

UPDATE: As requested I have added all of the code I am using to create the Window and its RichEdit control.

I'm trying to handle windows messages for a RichEdit control used as a child of another window.

Now I did have the RichEdit control working with the exception of my own WndProc. The issue is that, when I set wc.lpszClassName = MSFTEDIT_CLASS; so that it matches lpClassName used in CreateWindowEx(), the content of the RichEdit control no longer appears to draw (ie text, etc), however, its WndProc function can then handle messages.

The creation of the window:

First the constructor:

SubWindow::SubWindow(const wchar_t *szAppNameImport)
{
    szAppName = szAppNameImport;

    cfmt = CHARFORMATW();
    hwnd = HWND();
    windowRect = RECT();
    editControlHwnd = HWND();
    wc = WNDCLASSEX();

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_CLASSDC;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = szAppName;
    wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
}

Then the Create() function:

VOID SubWindow::Create(unsigned int window_startX, unsigned int window_startY, unsigned int windowWidthInput, unsigned int windowHeightInput, HWND parent)
{   
    windowRect.left = window_startX;
    windowRect.top = window_startY;

    windowRect.right = windowWidthInput;
    windowRect.bottom = windowHeightInput;

    if(!RegisterClassEx(&wc))
    {
        throw std::exception();
    }

    if((hwnd = CreateWindowEx
        (
        WS_EX_CLIENTEDGE,
        szAppName,
        TEXT("Our classy sub window!"),
        WS_OVERLAPPEDWINDOW| WS_VISIBLE,

        windowRect.left, windowRect.top,
        windowRect.right, windowRect.bottom,
        parent,
        NULL,       
        wc.hInstance,
        NULL))==NULL)
    {
        throw std::exception();
    }

    SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR)this);

    ShowWindow(hwnd, SW_SHOWDEFAULT);
    UpdateWindow(hwnd);
}

WndProc:

LRESULT CALLBACK SubWindow::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    SubWindow *childWindowPointer = (SubWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);

    if(childWindowPointer != NULL)
    {
        if(childWindowPointer->GetEditControl() == hwnd)
            OutputDebugString(L"I SHOULD NOT BE CALLED");

        return childWindowPointer->MsgProc(hwnd, uMsg, wParam, lParam);
    }
    else
    {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

MsgProc:

LRESULT SubWindow::MsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch(uMsg)
    {
    case WM_WINDOWPOSCHANGED:
        {
            GetClientRect(hwnd, &windowRect);
            SetWindowPos(editControlHwnd, NULL, windowRect.left, windowRect.top, windowRect.right, windowRect.bottom, SWP_NOZORDER | SWP_NOACTIVATE);
            return 0;
        }
    case WM_DESTROY:
        {
            OutputDebugString(TEXT("DESTROYING A SUB WINDOW!\n"));
            return 0;
        }

    case WM_PAINT:
        {
            InvalidateRect (hwnd, NULL, FALSE);
            hdc = BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
            return 0;
        }

    case EM_EXSETSEL:
        {
            if(hwnd == editControlHwnd)
            {
                OutputDebugString(L"Text selection changed");
                return 0;
            }
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
} 

The RichEdit control draws and functions perfectly, apparently without issue, with the exception of it not using the WndProc I have defined.

I'm not sure what I'm doing wrong here or how I can correctly resolve this.

EDIT: Based on the answers and comments, I have restored my code to use only a Window class which contains a RichEdit control, created thusly:

void SubWindow::CreateEditControl()
{
    std::wstring initialText = TEXT("TestWindow\r\n");

    LoadLibrary(L"Msftedit.dll");

    GetClientRect(hwnd, &windowRect);
    editControlHwnd = CreateWindowEx(0, MSFTEDIT_CLASS, initialText.data(),
        WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_READONLY | WS_VSCROLL | ES_NOHIDESEL,
        windowRect.left, windowRect.top,windowRect.right,windowRect.bottom,
        hwnd,
        NULL, NULL, NULL);

    cfmt.cbSize = sizeof(CHARFORMAT);
    cfmt.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE;
    cfmt.dwEffects = 0;
    cfmt.yHeight = 160;
    cfmt.crTextColor = RGB(0,0,0);
    wcscpy_s(cfmt.szFaceName, TEXT("Tahoma"));

    SendMessage(editControlHwnd, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cfmt);
}

How do I process the messages from this control in the Window's MsgProc?

Interminable
  • 1,338
  • 3
  • 21
  • 52
  • 1
    Registering your own class with the name `MSFTEDIT_CLASS` effectively replaces the original. Once you've done this, the original class is no longer accessible, and attempting to create a `MSFTEDIT_CLASS` creates your class instead. This is like overwriting one file with another file. It's not clear what your goal is. Do you want to partially replace the rich edit control globally? For just one window? Or are you merely trying to respond to messages *from* the window? – Raymond Chen Aug 24 '13 at 14:13
  • I was trying to create a single `RichEdit` control in one or more windows, but for now it's just the one. – Interminable Aug 24 '13 at 14:22
  • Then just create the standard `MSFTEDIT_CLASS`. Don't try to write your own. Like you said, if you use the standard control, "the RichEdit control draws and functions perfectly." – Raymond Chen Aug 24 '13 at 14:52
  • I've reversed the changes to my code so there is only one class for the Window, which uses the function I've added to my Question above to create the RichEdit control. – Interminable Aug 24 '13 at 15:15
  • "How do I process the messages from this control in the Window's MsgProc?" Why are you trying to process somebody else's messages? Let them process their own messages. – Raymond Chen Aug 24 '13 at 15:55
  • Because I need to be able to handle the `EM_EXSETSEL` message. – Interminable Aug 24 '13 at 16:12
  • Why not use `EN_SELCHANGE`? – Raymond Chen Aug 25 '13 at 04:26

3 Answers3

1

When you create a rich edit control window using the default class name (MSFTEDIT_CLASS), all messages are going to be sent to its parent window. Since you are not that parent window, you are not able to handle those messages.

So you will need to subclass the control, substituting your own window procedure that will be called directly, instead of allowing the messages to be passed on to the parent. That is simple to do; I've discussed it before in this answer for a regular edit control. The altered example code looks like this:

// Stores the old original window procedure for the rich edit control.
WNDPROC wpOldRichEditProc;

// The new custom window procedure for the rich edit control.
LRESULT CALLBACK CustomRichEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        ...
    }

    // Pass the messages you don't process on to the original window procedure.
    CallWindowProc(wpOldRichEditProc, hWnd, msg, wParam, lParam);
}

And when you create the control:

// Create the rich edit control
HWND hWnd = CreateWindowEx(...)

// Subclass it.
wpOldRichEditProc= (WNDPROC)SetWindowLongPtr(hWnd,
                                             GWLP_WNDPROC,
                                             (WNDPROC)CustomRichEditProc);

You will also need to make sure to unsubclass the control whenever it is destroyed. The other example demonstrates doing that in response to messages received by the parent window, but that won't work in your case, since you're not getting messages for the parent window. Instead, you'll need to remove the subclass from the control in response to its own WM_NCDESTROY message:

SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)wpOldRichEditProc);

Or, version 6 of the common controls library introduced a new, less error-prone method of subclassing using a set of utility functions. (The critical functionality was actually there in earlier versions, but it was undocumented.) Considering that you do not have control over the process that actually owns the window, this is arguably the preferred approach.

There is a demo of both approaches here on MSDN.

And of course, you don't have to subclass only individual controls. You can also register a custom window class that behaves the same way as the built-in rich edit control, but still gives you first crack at the messages received by windows of that class. I can't tell from the question whether that's necessary or not; it sounds like you only have a single control you care about.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • The 'original' problem was that my parent window wasn't getting the RichEdit control's messages, this is why I moved the RichEdit control into its own class. – Interminable Aug 24 '13 at 14:09
  • @Interminable I fail to see how you have done that. There is something confusing about `wc.lpszClassName` at the top of the question, but there's no code that goes along with it, so I don't know what it refers to. At any rate, the problem is that you are not calling the default window procedure for the rich edit control. Which means you have subclassed it incorrectly. – Cody Gray - on strike Aug 24 '13 at 14:11
  • I have two wc structures, one in each class. One is in the `Window` class, and one is in the `EditControl` class. As it previously stood, there was no `EditControl` class. Instead I simply had a `CreateEditControl()` function in my `Window` class. And the `Window` class had only one wc structure. If you like I could edit my question with the code I was originally using, before I created an entirely separate `EditControl` class. – Interminable Aug 24 '13 at 14:16
  • I don't know why I would want to see code that differs from what you're actually using..? If you want to register a custom window class that is a subclass of one of the default classes, you will need to call the `GetClassInfoEx` function; I see no evidence in your code that you are doing that. You will *also* need to save a pointer to the original window procedure so that you can call it using `CallWindowProc` like I show in my answer. `DefWindowProc` is only going to call the default window procedure for a basic window; it is not going to call the default *rich edit control* window procedure. – Cody Gray - on strike Aug 24 '13 at 14:24
  • With the additional code in my Question above, would I still need to do what is done in your Answer in order to handle the RichEdit control's messages? I'm afraid part of the issue may be my lack of understanding of the concept of sub-classing in the context of the Windows API, so sorry about that. – Interminable Aug 24 '13 at 15:21
  • Thanks to your clear examples, I have managed to implement this without needing to have an extra `EditControl` class. – Interminable Aug 24 '13 at 17:56
1

You say that the original problem was that your parent window was not getting the notification messages from the RichEdit control. Did you send a EM_SETEVENTMASK message to the RichEdit control? If you don't, the RichEdit control will not send certain notification messages to its parent window. See EM_SETEVENTMASK message.

Stuart
  • 1,428
  • 11
  • 20
0

Can you show your code involving the wc structure and the creation of the window? I'm fairly sure you don't want the main window to have the same class as the rich edit control - and that's what I'm reading so far.

I don't even know why you have a WNDCLASSEX apply to a rich edit control.

My suggestion is that you simplify things and "subclass" the rich edit control after it has been created, using SetWindowLong() with GWL_WNDPROC to your EditControl::WndProc.

Mike Weir
  • 3,094
  • 1
  • 30
  • 46