Winapi 应用程序在绘图时冻结

Posted

技术标签:

【中文标题】Winapi 应用程序在绘图时冻结【英文标题】:Winapi app freeze while drawing 【发布时间】:2014-09-19 19:36:34 【问题描述】:

我正在开发用于绘制对象(类似于 Paint)的简单 winapi 应用程序。 但是我有一个问题,当用户绘制对象几分钟(像往常一样 3-6)窗口时 即使我点击菜单选项卡

也许有人有同样的问题或可以提供任何解决方案?

#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;   

【问题讨论】:

你的调试器没有告诉你程序挂起时在做什么吗? 【参考方案1】:

每次调用您的窗口过程时,您都会创建一个画笔。然后你不要破坏它。最终,您会耗尽系统资源,这就是您的应用停止工作的时候。

只在处理代码中为需要它的特定消息创建一个画笔。确保在完成后销毁刷子。如果您可以在启动时创建一次画笔并在应用程序的整个生命周期内重复使用它,那就这样做吧。

【讨论】:

以上是关于Winapi 应用程序在绘图时冻结的主要内容,如果未能解决你的问题,请参考以下文章

使用 WINAPI 创建的窗口不是绘图对象。有啥问题?

无法使用 WinAPI 显示窗口

创建一个线程使函数休眠而不冻结程序

在线程内更新 pyqtgraph BarGraphItem

SendMessage() WINAPI 在用于连接到 DDE 服务器时挂起

移动窗口时 C++/WinApi 内存使用率上升