我正在为绘图对象(类似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;
}
答案 0 :(得分:2)
每次调用窗口过程都会创建一个画笔。然后你不破坏。最终耗尽系统资源,那就是你的应用程序停止工作的时候。
仅在处理代码中为需要它的特定消息创建画笔。确保在完成后摧毁刷子。如果您可以在启动时创建一次刷子并在应用程序的生命周期中重复使用它,请执行此操作。