NETRY
NETRY

Reputation: 57

Winapi app freeze while drawing

I'm developing simple winapi application for drawing objects (something like Paint). But I have a problem that when user paint objects several minutes (3-6 as usual) window freeze and not respond even when I click on menu tabs enter image description here

Maybe somebody have same problem or can provide any solution?

#include "stdafx.h"
#include "resource.h"
#include "string.h"
#include "stdio.h"

#define MAX_LOADSTRING 100

/* Global Variables **********************************************************/
HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];    

int X1 = 0;
int Y1 = 0;
int X2 = 0;
int Y2 = 0;
bool isPenDrawing = false;

int lastX = 0;
int lastY = 0;

int startRectX = 0;  // for ellipse and rectangle
int startRectY = 0;

int currentShapeId = 0;

HDC hdc;
HDC memDC;
HDC memDC2;
HBITMAP memBM;
HBITMAP memBM2;
RECT lprect;
HBRUSH Brush;
HGDIOBJ hOldBush;
HPEN Pen;

/* Forward Declarations ******************************************************/
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK    About(HWND, UINT, WPARAM, LPARAM);


int APIENTRY WinMain(HINSTANCE hInstance,    /// MyRegisterClass FUNCTION ALANLOG
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    HACCEL hAccelTable;
            //C: Initialize the global strings.
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_PAINT_BEGINNER, szWindowClass, MAX_LOADSTRING);
            //C: Register the class for the main window of this application.
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX); 

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = (WNDPROC)WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, (LPCTSTR)IDI_PAINT_BEGINNER);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = (LPCSTR)IDC_PAINT_BEGINNER;
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

    RegisterClassEx(&wcex);
            //C: Perform application initialization.
    if (!InitInstance (hInstance, nCmdShow)) 
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_PAINT_BEGINNER);
            //C: Main message pump.
    while (GetMessage(&msg, NULL, 0, 0)) 
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return msg.wParam;
}


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
            //C: Store the instance handle in the global varaible.
   hInst = hInstance;
            //C: Create the mainwindow.
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
   if (!hWnd)
   {
            //C: The main window creation failed.
      return FALSE;
   }
            //C: Display the main window.
   ShowWindow(hWnd, nCmdShow);
            //C: Force the main window to repaint itself.
   UpdateWindow(hWnd);

   return TRUE;
}


LRESULT OnCommand (HWND hWnd, int iID, int iEvent, HWND hWndControl, bool &isHandled);
LRESULT OnLButtonDown (HWND hWnd, UINT nCtrl, UINT x, UINT y);
LRESULT OnMouseMove   (HWND hWnd, UINT nCtrl, UINT x, UINT y);
LRESULT HandleMouseMove(HWND hWnd, UINT nCtrl, int x, int y);
LRESULT GetCurrentShapeId(HWND hWnd);
LRESULT OnLButtonUp   (HWND hWnd, UINT nCtrl, UINT x, UINT y);
LRESULT OnPaint       (HWND hWnd);


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  // handler of all windows commands
{
    Brush = CreateSolidBrush(RGB(255, 255, 255));
    PAINTSTRUCT ps;

    switch (message) 
    {
    case WM_CREATE:
        {
            hdc = GetDC(hWnd);                  // retrieves a handle to a device context (DC) for the client area
            memDC = CreateCompatibleDC(hdc);
            memDC2 = CreateCompatibleDC(hdc);
            GetClientRect(hWnd, &lprect);
            memBM = CreateCompatibleBitmap(hdc, lprect.right, lprect.bottom);
            memBM2 = CreateCompatibleBitmap(hdc, lprect.right, lprect.bottom);
            SelectObject(memDC, memBM);
            SelectObject(memDC2, memBM2);
            FillRect(memDC, &lprect, Brush);
            FillRect(memDC2, &lprect, Brush);
            //C: Set the initial drawing mode.
            HMENU hMenu = ::GetMenu(hWnd);
            HMENU hMenuShapes = ::GetSubMenu(hMenu, 1);
        //  ::CheckMenuRadioItem(hMenuShapes, ID_SHAPE_RECTANGLE, ID_SHAPE_CIRCLE, ID_SHAPE_RECTANGLE, MF_BYCOMMAND);
        }
    case WM_COMMAND:
        {
            int wmId    = LOWORD(wParam); 
            int wmEvent = HIWORD(wParam); 

            bool isHandled = true;
            LRESULT lResult = OnCommand(hWnd, wmId, wmEvent, (HWND)lParam, isHandled);
            if (!isHandled)
            {
                lResult = DefWindowProc(hWnd, message, wParam, lParam);
            }

            return lResult;
        }
        break;
    case WM_CHAR:
        {

        }
        break;
    case WM_LBUTTONDOWN:
        {
            return 0;
        }
        break;
    case WM_MOUSEMOVE:
        {
            int x = LOWORD(lParam);  // get mouse position onclick
            int y = HIWORD(lParam);
            return HandleMouseMove(hWnd, (UINT)wParam, x, y);
        }
        break;
    case WM_LBUTTONUP:
        {
        return 0;
        }
        break;
    case WM_PAINT:
        {
            hdc = BeginPaint(hWnd, &ps);
            BitBlt(hdc, 0, 0, lprect.right, lprect.bottom, memDC2, 0, 0, SRCCOPY);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        {
            //C: Send a shutdown message to the message pump.
            PostQuitMessage(0);
            return 0;
        }
        break;
    case WM_RBUTTONDOWN:
        {
            int x = LOWORD(lParam);  // get mouse position onclick
            int y = HIWORD(lParam);

            currentShapeId = ::GetCurrentShapeId(hWnd);

            if (!isPenDrawing)
            {
                isPenDrawing = true;
                lastX = x;
                lastY = y;
                startRectX = x;
                startRectY = y;
            }
            /*
            char* str1 = new char[50];
            char* tmpBuffer = new char[20];
            itoa(x, tmpBuffer, 10);

            strcpy(str1, "Right button DOWN \n X = ");
            strcat(str1, tmpBuffer);
            strcat(str1, "\nY = ");

            itoa(y, tmpBuffer, 10);
            strcat(str1, tmpBuffer);


            MessageBox(NULL, (LPCSTR)str1, (LPCSTR)"Message Title", MB_OKCANCEL);
            */

            return 0;
        }
        break;
    case WM_RBUTTONUP:
        {
            int x = LOWORD(lParam);  // get mouse position onclick
            int y = HIWORD(lParam);

            if (isPenDrawing)
            {
                BitBlt(memDC, 0, 0, lprect.right, lprect.bottom, memDC2, 0, 0, SRCCOPY);
                isPenDrawing = false;
                ClipCursor(NULL);                       // free cursor
                ReleaseCapture();
                InvalidateRect(hWnd, &lprect, false);
            }

            return 0;
        }
        break;

    }

            //C: Exit.
    return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT HandleMouseMove(HWND hWnd, UINT nCtrl, int x, int y)
{
    if (isPenDrawing)
    {
        Brush = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
        hOldBush = SelectObject(memDC2, Brush);

        switch (currentShapeId) 
        {
        case 0:
            {
                BitBlt(memDC2, 0, 0, lprect.right, lprect.bottom, memDC, 0, 0, SRCCOPY);
                Rectangle(memDC2, startRectX, startRectY, x, y);
                InvalidateRect(hWnd, &lprect, false);
                lastX = x;
                lastY = y;
            }
            break;
        case 1:
            {

                BitBlt(memDC2, 0, 0, lprect.right, lprect.bottom, memDC, 0, 0, SRCCOPY);
                Ellipse(memDC2, startRectX, startRectY, x, y);
                InvalidateRect(hWnd, &lprect, false);
                lastX = x;
                lastY = y;                  
            }
            break;
        case 2:
            {
                MoveToEx(memDC2, lastX, lastY, (LPPOINT)NULL);
                LineTo(memDC2, x, y);
                BitBlt(memDC, 0, 0, lprect.right, lprect.bottom, memDC2, 0, 0, SRCCOPY);
                InvalidateRect(hWnd, &lprect, false);
                lastX = x;
                lastY = y;
                break;
            }
            break;
        case 3:
            {
                BitBlt(memDC2, 0, 0, lprect.right, lprect.bottom, memDC, 0, 0, SRCCOPY);

                MoveToEx(memDC2, startRectX, startRectY, (LPPOINT)NULL);
                LineTo(memDC2, x, y);

                InvalidateRect(hWnd, &lprect, false);
                lastX = x;
                lastY = y;
            }
            break;
        }

        //::ReleaseDC(hWnd, hdc);
    }
    return 0;       
}


LRESULT GetCurrentShapeId(HWND hWnd)
{
    HMENU hMenu         = ::GetMenu(hWnd);
    HMENU hShapeMenu    = ::GetSubMenu(hMenu, 1);

    if (::GetMenuState(hShapeMenu, ID_SHAPE_RECTANGLE, MF_BYCOMMAND) & MF_CHECKED)
    {
        return 0;
    }
    else if (::GetMenuState(hShapeMenu, ID_SHAPE_ELLIPSE, MF_BYCOMMAND) & MF_CHECKED)
    {
        return 1;
    }
    else if (::GetMenuState(hShapeMenu, ID_SHAPE_PEN, MF_BYCOMMAND) & MF_CHECKED)
    {
        return 2;
    }
    else if (::GetMenuState(hShapeMenu, ID_SHAPE_LINE, MF_BYCOMMAND) & MF_CHECKED)
    {
        return 3;
    }
}


LRESULT OnCommand (HWND hWnd, int iID, int iEvent, HWND hWndControl, bool &isHandled)
{
            //C: Parse the menu selections.
    switch (iID)
    {   
    case ID_SHAPE_LINE:
    case ID_SHAPE_PEN:
    case ID_SHAPE_RECTANGLE:
    case ID_SHAPE_ELLIPSE:
        {
            //C: Set the drawing mode.
            HMENU hMenu = ::GetMenu(hWnd);
            HMENU hMenuShapes = ::GetSubMenu(hMenu, 1);
            ::CheckMenuRadioItem(hMenuShapes, ID_SHAPE_RECTANGLE, ID_SHAPE_PEN, iID, MF_BYCOMMAND);
        }
        break;
    case IDM_EXIT: 
        {
            //C: Destroy the window in order to exit the program.
            DestroyWindow(hWnd);
        }
        break;
    default:
        {
            //C: Flag this message as unhandled.
            isHandled = false;
        }
    }
    return 0;
}


LRESULT OnPaint       (HWND hWnd)
{
    PAINTSTRUCT ps;
    HDC         hdc;
    hdc = ::BeginPaint(hWnd, &ps);

    ::EndPaint(hWnd, &ps);

    return 0;   
}

Upvotes: -1

Views: 436

Answers (1)

David Heffernan
David Heffernan

Reputation: 612794

Every single time your window procedure is called you create a brush. Which you then do not destroy. Eventually your exhaust the system resources and that's when your app stops working.

Only create a brush in the handling code for the specific messages that need it. Make sure you destroy the brush when you have finished with it. If you can create the brush once at start up and re-use it for the life of the application, do so.

Upvotes: 2

Related Questions