代码之家  ›  专栏  ›  技术社区  ›  Interminable

如何正确处理来自MSFTEDIT_CLASS(RichEdit)控件的Windows消息?

  •  0
  • Interminable  · 技术社区  · 11 年前

    更新:根据请求,我已经添加了所有用于创建Window及其RichEdit控件的代码。

    我正在尝试为用作另一个窗口的子窗口的RichEdit控件处理窗口消息。

    现在,除了我自己的控件外,我确实有RichEdit控件在工作 WndProc 。问题是,当我设置 wc.lpszClassName = MSFTEDIT_CLASS; 以便匹配 lpClassName 用于 CreateWindowEx() ,RichEdit控件的内容不再显示为绘图(即文本等),但是,它的WndProc函数可以处理消息。

    窗口的创建:

    首先是构造函数:

    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));
    }
    

    然后 Create() 功能:

    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);
        }
    }
    

    消息过程:

    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);
    } 
    

    除了不使用 窗口过程 我已经定义了。

    我不确定我在这里做错了什么,也不确定如何正确解决这个问题。

    编辑: 根据答案和评论,我已将代码恢复为仅使用 Window 类,其中包含一个RichEdit控件,该控件是这样创建的:

    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);
    }
    

    如何在Window的MsgProc中处理来自此控件的消息?

    3 回复  |  直到 11 年前
        1
  •  1
  •   Community TheSoundDefense    7 年前

    使用默认类名创建富编辑控件窗口时( MSFTEDIT_CLASS ),所有消息都将发送到其父窗口。由于您不是父窗口,因此无法处理这些消息。

    因此,您需要对控件进行子类化,替换您的 拥有 将被直接调用的窗口过程,而不允许将消息传递给父级。这很简单;我以前在中讨论过 this answer for a regular edit control 修改后的示例代码如下所示:

    // 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);
    }
    

    创建控件时:

    // Create the rich edit control
    HWND hWnd = CreateWindowEx(...)
    
    // Subclass it.
    wpOldRichEditProc= (WNDPROC)SetWindowLongPtr(hWnd,
                                                 GWLP_WNDPROC,
                                                 (WNDPROC)CustomRichEditProc);
    

    您还需要确保 未分类 无论何时控件被销毁。另一个示例演示了这样做是为了响应父窗口接收到的消息,但这在您的情况下不起作用,因为您没有收到父窗口的消息。相反,您需要从控件中删除子类,以响应它自己的子类 WM_NCDESTROY 消息:

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

    或者,公共控件库的第6版引入了一种新的、不易出错的子类化方法,使用 a set of utility functions 。(关键功能实际上在早期版本中就存在,但没有记录。)考虑到您无法控制实际拥有窗口的流程,这可以说是首选方法。

    这两种方法都有演示 here on MSDN .

    当然,您不必只对单个控件进行子类化。您还可以注册一个自定义窗口类,该类的行为与内置的富编辑控件相同,但仍然可以让您首先查看该类的窗口接收到的消息。我无法从这个问题中判断这是否有必要;它 声音 就像你只关心一个控件一样。

        2
  •  1
  •   Stuart    11 年前

    您说最初的问题是您的父窗口没有从RichEdit控件获取通知消息。您是否向RichEdit控件发送了EM_SETEVENTMASK消息?如果不这样做,RichEdit控件将不会向其父窗口发送某些通知消息。看见 EM_SETEVENTMASK message .

        3
  •  0
  •   Mike Weir    11 年前

    你能展示你的代码吗 wc 结构和窗口的创建?我敢肯定,您不希望主窗口与富编辑控件具有相同的类——这就是我目前正在阅读的内容。

    我甚至不知道你为什么有 WNDCLASSEX 应用于富编辑控件。

    我的建议是,您可以简化事情,并在创建富编辑控件后将其“子类化”,使用 SetWindowLong() 具有 GWL_WNDPROC 给你 EditControl::WndProc .