WM_PAINT Slowdown Issues With RichEdit

Started by
8 comments, last by Psychopathetica 10 years, 8 months ago

I created a Win32 window with a child window using C++ 2010. In my child window I have a richedit control. It works but there is one problem. When I want to use the WM_PAINT message in the parent window, even if I leave it empty with a breakpoint, it seems to slow down the program only its not really. Only as I try to type in the RichEdit control, it makes it feel very unresponsive. But if I comment out the WM_PAINT: and break, it works as it should. I need the WM_PAINT message to eliminate flicker in the border when you drag the child window using InvalidateRect(hChildWnd, NULL, FALSE); and quite possibly other things if I need to. How can I allow the WM_PAINT message without slowing down the RichEdit control when I type. Thanks in advance and heres the code I currently have:

#include <windows.h>
#include <Richedit.h>

HWND hWnd;
HWND hChildWnd;
HWND hRichEdit;
MSG msg;
WNDCLASSEX wc;
WNDCLASSEX wcChild;
HBRUSH brush;
HINSTANCE hChildInstance = NULL;

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWindowProcedure (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow);

#define ID_FILE_EXIT 1000

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
    wc.lpfnWndProc   = WindowProcedure;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "Window";
    wc.hIconSm       = NULL;
    RegisterClassEx(&wc);
    hWnd = CreateWindowEx (0, "Window", "Parent Window", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, hWnd, NULL, hInstance, NULL);
    
    wcChild.cbSize        = sizeof(WNDCLASSEX);
    wcChild.style         = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
    wcChild.lpfnWndProc   = ChildWindowProcedure;
    wcChild.cbClsExtra    = 0;
    wcChild.cbWndExtra    = 0;
    wcChild.hInstance     = hChildInstance;
    wcChild.hIcon         = NULL;
    wcChild.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wcChild.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
    wcChild.lpszMenuName  = NULL;
    wcChild.lpszClassName = "ChildWindow";
    wcChild.hIconSm       = NULL;
    RegisterClassEx(&wcChild);
    hChildWnd = CreateWindowEx (0, "ChildWindow", "Child Window", WS_OVERLAPPEDWINDOW | WS_CHILD | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, hWnd, NULL, hChildInstance, NULL);

    LoadLibrary("riched32.dll");
    hRichEdit = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", WS_VISIBLE | WS_CHILD | WS_VSCROLL | ES_MULTILINE | ES_LEFT | ES_NOHIDESEL | ES_AUTOVSCROLL, 0, 0, 640, 480, hChildWnd, NULL, hChildInstance, NULL);
    if( !hRichEdit ) MessageBox(hWnd,"Couldnt create rich textbox", "Message", MB_OK);

    CHARFORMAT2 cf;
    cf.cbSize = sizeof(CHARFORMAT2);
    cf.dwMask = CFM_COLOR | CFM_BACKCOLOR;
    cf.crTextColor = RGB(255, 255, 255);
    cf.crBackColor = RGB(0 ,0, 0);
    cf.dwEffects = 0 ;
    SendMessage(hRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf );
    SendMessage(hRichEdit, EM_SETBKGNDCOLOR,0, RGB(0, 0, 0));

    ShowWindow (hWnd, SW_SHOWMAXIMIZED);
    ShowWindow(hRichEdit, nCmdShow);
    SetFocus(hRichEdit);

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

    return msg.wParam;
}

LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CREATE:
        {
            HMENU hFile = CreateMenu();
            HMENU hExit = CreateMenu();
            AppendMenu(hFile, MF_POPUP, (UINT_PTR)hExit, "File");
            AppendMenu(hExit, MF_STRING, ID_FILE_EXIT, "Exit");
            SetMenu(hWnd, hFile);
            break;
        }
        case WM_COMMAND:
        {
            switch(LOWORD(wParam))
            {
                case ID_FILE_EXIT:
                {
                    DestroyWindow(hWnd);
                    return(0);
                    break;
                }
            }
            break;
        }
        case WM_DESTROY:
            PostQuitMessage (0);
            break;
        case WM_PAINT: //THIS IS WHERE THE PROBLEM IS!!!
            InvalidateRect(hChildWnd, NULL, FALSE);
            break;
        default:
            return DefWindowProc (hWnd, msg, wParam, lParam);
    }
    return 0;
}

LRESULT CALLBACK ChildWindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        default:
            return DefWindowProc (hWnd, msg, wParam, lParam);
    }
    return 0;
}
Advertisement
Have you tried setting WS_CLIPCHILDREN on the "child" window?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Yea it doesnt work. I even removed the CS_VREDRAW | CS_HREDRAW from both windows since I ran into this article: http://www.catch22.net/tuts/flicker-free-drawing and it still flickers when I drag the child window. But what is making me scratch my head is the fact that WM_PAINT with the break or even return 0 causes slowdown when I type. And after about a dozen characters later it nearly doesnt respond at all. The only thing thats very responsive then is dragging the window and exiting out. Thats what I dont understand.


Flicker in Windows can sometimes be tricky, I have been dealing with it quite a lot at work latly, when we decided to eliminate all flicker in an app. The key is to never paint the same pixel twice during one WM_PAINT (yes, double buffering is a common solution for this). If you can describe in more detail what kind of flicker you ar experiencing, maybe I can help.

Remove your WM_PAINT handler entirely, and we'll work from there.

EDIT:

Since I'm lying here with a broken leg and nothing better to do, I pasted your code into VS. I think I know whar is causing the flicker. I'll be back after some tests.

I dont know about RichEdit controls, but WM_PAINT is not the only message that signals drawing is needed and if you overdraw everything there you dont need to draw the background first which happens on WM_ERASEBKGND . You call InvalidateRect which causes another WM_PAINT together with some other things, but you never do any painting and you also dont call DefWindowProc which would take care of letting Windows know painting is done, which could cause an endless stream of those messages.

These are some lines cut from a program I wrote a while ago to do the minimum of work needed on those messages:


    case WM_ERASEBKGND:
      {
        // do nothing as its overdrawn anyway through WM_PAINT, possibly reducing flicker
        return 1; // pretend its done
      }
    case WM_PAINT:
      {
        Paint();
        ValidateRect(wnd,NULL);
        return 0;
      }

Have you tried setting WS_CLIPCHILDREN on the "child" window?

It turns out that this is indeed all that is needed, in combination with removing your WM_PAINT handler.

The reason your RichEdit appears to be lagging is because you don't do a "ValidateRect" on your paren'ts "InvalidatedRect" area. You see, WM_PAINT expects you to at least call BeginPaint and EndPaint, even if you don't do any drawing - EndPaint validates the "InvalidatedRect" area.

If you don't validate the invalidated area during WM_PAINT, the windows GUI system will assume that your client area still needs to be painted, and it will keep posting WM_PAINT messages to your message loop (in the case of a modal DialogBox, that's the internal message loop handled by the DialogBox API - or by the CDialog::DoModal wrapper in MFC).

This means that every time there is no input (keyboard/moase) or I/O (newtork, disk, sync etc.) message, your message loop will be continuously processing WM_PAINT messages - they have the lowest priority, so your application/dialog still gets input, but the continuous WM_PAINTing that it does when there's no input will be slowing it down - you should also see your CPU being used accordingly - e.g., 25% CPU used continuously until you close the dialog, if you have a quad-core).

P.S.: Sorry for posting with a bugmenot account - I read these forums daily, and I find many occasions of replying to tricky questions like this one, but I don't want to be bound by an account. :)

I tried the methods shown above but WM_ERASEBKGND makes my black background white and doesnt clear. Instead when I drag the child window it leaves a trail of windows everywhere. I'm sorry if I sound too much of a noob in C++. I'm trying to get away from VB. Been using that language too long. lol

I honestly dont know when to use ValidateRect and InvalidateRect. Should I use ValidateRect before I BeginPaint, in between, or after EndPaint. Should I InvalidateRect before I ValidateRect? I honestly dont know. Thank you guys so much so far but I still get slowdown in this code even with ValidateRect and BeginPaint / EndPaint

#include <windows.h>
#include <Richedit.h>

HWND hWnd;
HWND hChildWnd;
HWND hRichEdit;
MSG msg;
WNDCLASSEX wc;
WNDCLASSEX wcChild;
HBRUSH brush;
HINSTANCE hChildInstance = NULL;
PAINTSTRUCT  PS;
HDC          hDC;

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWindowProcedure (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow);

#define ID_FILE_EXIT 1000

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WindowProcedure;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "Window";
    wc.hIconSm       = NULL;
    RegisterClassEx(&wc);
    hWnd = CreateWindowEx (0, "Window", "Parent Window", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, hWnd, NULL, hInstance, NULL);
    
    wcChild.cbSize        = sizeof(WNDCLASSEX);
    wcChild.style         = 0;
    wcChild.lpfnWndProc   = ChildWindowProcedure;
    wcChild.cbClsExtra    = 0;
    wcChild.cbWndExtra    = 0;
    wcChild.hInstance     = hChildInstance;
    wcChild.hIcon         = NULL;
    wcChild.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wcChild.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
    wcChild.lpszMenuName  = NULL;
    wcChild.lpszClassName = "ChildWindow";
    wcChild.hIconSm       = NULL;
    RegisterClassEx(&wcChild);
    hChildWnd = CreateWindowEx (0, "ChildWindow", "Child Window", WS_OVERLAPPEDWINDOW | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, hWnd, NULL, hChildInstance, NULL);

    LoadLibrary("riched32.dll");
    hRichEdit = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", WS_VISIBLE | WS_CHILD | WS_VSCROLL | ES_MULTILINE | ES_LEFT | ES_NOHIDESEL | ES_AUTOVSCROLL, 0, 0, 640, 480, hChildWnd, NULL, hChildInstance, NULL);
    if( !hRichEdit ) MessageBox(hWnd,"Couldnt create rich textbox", "Message", MB_OK);

    CHARFORMAT2 cf;
    cf.cbSize = sizeof(CHARFORMAT2);
    cf.dwMask = CFM_COLOR | CFM_BACKCOLOR;
    cf.crTextColor = RGB(255, 255, 255);
    cf.crBackColor = RGB(0 ,0, 0);
    cf.dwEffects = 0 ;
    SendMessage(hRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf );
    SendMessage(hRichEdit, EM_SETBKGNDCOLOR,0, RGB(0, 0, 0));

    ShowWindow (hWnd, SW_SHOWMAXIMIZED);
    ShowWindow(hRichEdit, nCmdShow);
    SetFocus(hRichEdit);

    //while (1)
 //   {
 //       if (PeekMessage(&msg,NULL,0,0,PM_REMOVE) > 0)
 //       {
 //           if (WM_QUIT == msg.message) break;
 //           TranslateMessage (&msg);
 //           DispatchMessage (&msg);
 //       }
 //   }

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

    return msg.wParam;
}

LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CREATE:
        {
            HMENU hFile = CreateMenu();
            HMENU hExit = CreateMenu();
            AppendMenu(hFile, MF_POPUP, (UINT_PTR)hExit, "File");
            AppendMenu(hExit, MF_STRING, ID_FILE_EXIT, "Exit");
            SetMenu(hWnd, hFile);
            break;
        }
        case WM_COMMAND:
        {
            switch(LOWORD(wParam))
            {
                case ID_FILE_EXIT:
                {
                    DestroyWindow(hWnd);
                    return(0);
                    break;
                }
            }
            break;
        }
        case WM_DESTROY:
            PostQuitMessage (0);
            break;
        case WM_ERASEBKGND:
          {
            return 1;
          }
        case WM_PAINT:
          {
            ValidateRect(hWnd,NULL);
            hDC = BeginPaint(hWnd, &PS);
            EndPaint(hWnd, &PS);
            return 0;
          }
          break;
        default:
            return DefWindowProc (hWnd, msg, wParam, lParam);
    }
    return 0;
}

LRESULT CALLBACK ChildWindowProcedure (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        default:
            return DefWindowProc (hWnd, msg, wParam, lParam);
    }
    return 0;
}
If you perform these two simple steps, what is not working for you?

1) Completely remove case WM_PAINT from WindowProcedure, what you are doing here is of no use at all. It just causes your slowness.

2) Add style WS_CLIPCHILDREN to hChildWnd, which is the prent of your rich edit. This will cause the window background to not paint itself behind the rich edit (this is what causes your flicker).

Forget ValidateRect, forgt InvalidateRect, forget WM_ERASEBKGND, forget BeginPaint and EndPaint, none of that is relevant to your problem. I tried the above two steps with your code, and it seems to work fine.

Thanks a lot for your help. It works. :)

This topic is closed to new replies.

Advertisement