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

标签 c++ winapi wndproc richedit

更新:根据要求,我添加了用于创建窗口及其 RichEdit 控件的所有代码。

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

现在,除了我自己的 WndProc 之外,我确实可以使用 RichEdit 控件。问题是,当我设置 wc.lpszClassName = MSFTEDIT_CLASS; 使其与 CreateWindowEx() 中使用的 lpClassName 匹配时, 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);
} 

RichEdit 控件绘制和运行完美,显然没有问题,除了它没有使用我定义的 WndProc

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

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

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 中处理来自该控件的消息?

最佳答案

当您使用默认类名 (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);

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

有两种方法的演示 here on MSDN .

当然,您不必只对单个控件进行子类化。您还可以注册一个自定义窗口类,它的行为方式与内置的 Rich Edit 控件相同,但您仍然可以首先破解该类窗口收到的消息。我无法从问题中判断是否有必要; 听起来您只关心一个控件。

关于c++ - 如何正确处理来自 MSFTEDIT_CLASS (RichEdit) 控件的 Windows 消息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18418897/

相关文章:

c++ - QObject 中不可访问的转换

delphi - 使用Delphi获取默认网关

c# - 有没有办法让我的应用程序将其图标推送到其他应用程序?

c - WM_MOUSEMOVE 消息处理

C++(.cpp 文件和 .h 文件。)拆分代码并添加一个函数,提取 - 容易吗?

C++多态项目从文件中读取

c++ - O(1) std 或 boost 列表连接

c - 在 C 和 C# 应用程序之间进行 IPC 的最快方法是什么?

C++ 在运行时更改 HWND 窗口过程

C# ListView 禁用水平滚动条