Hi

I have a memory leak somewhere in this code but I have no idea where. Can anybody help me out ?

#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#include <fstream>
#include <time.h>
#include <cstring>
#include "resource.h"

using namespace std;

const char cn[] = "SameGame"; // ClassName

HBITMAP BLUEBMP		= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BLUE1));
HBITMAP GREENBMP	= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_GREEN1));
HBITMAP REDBMP		= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_RED1));
HBITMAP YELLOWBMP	= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_YELLOW1));
HBITMAP BLUEBMP_S	= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BLUE2));
HBITMAP GREENBMP_S	= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_GREEN2));
HBITMAP REDBMP_S	= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_RED2));
HBITMAP YELLOWBMP_S	= LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_YELLOW2));

#define EMPTY		0

#define IS_SELECTED(B)	(B > 4)
#define IS_EMPTY(B)		(B == EMPTY)

#define SELECTED	8 // 1000
#define BLUE		1 // 0001
#define GREEN		2 // 0010
#define RED			3 // 0011
#define YELLOW		4 // 0100

int width	= 15; // Grid Width
int height	= 15; // Grid Height

#define ballSize	30 // Ball Diameter

#define ID_DELAY	1
#define ID_DELAY2	2

int score = 0;
int tempScore = 0;

bool MEGASHIFT = 0;

unsigned char grid[100][100];

HPEN pen = CreatePen(PS_SOLID , 0 , RGB(255,255,255));
HPEN panel = CreatePen(PS_SOLID , 0 , RGB(0,0,0));
HDC			hdc;
HBITMAP		bmp;
BITMAP		bm;
PAINTSTRUCT	ps;
HDC			_hdc;
HDC			_hdcMem;
HBITMAP		hbmOld;

int randNum;
int RandomNumber(int high, int low) {
	return ((rand() % high - low) + low + 1);
}

int bi; // Ball Index
int bc; // Ball count

int TB; // SHIFTING TOP BALL
int BB; // SHIFTING BOTTOM BALL
int RB; // SHIFTING RIGHT BALL
int LB; // SHIFTING LEFT BALL

UINT _w = width;
UINT _h = height;

char *_score = new char();
char *_tempscore = new char();

bool idg = 0;

void (*delayFunction)(HWND);

int GETINTLENGTH(int num) {
	int curMax = 10;
	int noOfChs = 1;
	while (num > curMax) {
		curMax *= 10;
		noOfChs++;
	}
	return noOfChs;
}

char *INTTOCHARP(int num) {
	char *val = new char();
	_itoa_s(num, val, GETINTLENGTH(num)+1, 10);
	return val;
}
int GET_TRUE_COL(int ball) {
	return ball ^ SELECTED;
}
void REMOVEALLSELECTED() {
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			if (IS_SELECTED(grid[y][x])) {
				grid[y][x] = EMPTY;
			}
		}
	}
}
void UNSELECTALL() {
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			if (IS_SELECTED(grid[y][x])) {
				grid[y][x] ^= SELECTED;
			}
		}
	}
}
void REPAINT(HWND hwnd) {
	InvalidateRect(hwnd, NULL, 1);
}
void NEWGAME(HWND hwnd) {
	srand((int)time(NULL));
	score = 0;
	tempScore = 0;
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			randNum = RandomNumber(4,0);
			grid[y][x] = randNum;
		}
	}
	REPAINT(hwnd);
}
void FLOOD(int x, int y, int col) {
	grid[y][x] |= SELECTED;
	bc++;
	if (x >= 1)			 if (col == (int) grid[y][x-1] && int( grid[y][x-1] ) ^ (int) col == SELECTED) FLOOD(x-1, y, col);
	if (x <= width-2)	 if (col == (int) grid[y][x+1] && int( grid[y][x+1] ) ^ (int) col == SELECTED) FLOOD(x+1, y, col);
	if (y >= 1)			 if (col == (int) grid[y-1][x] && int( grid[y-1][x] ) ^ (int) col == SELECTED) FLOOD(x, y-1, col);
	if (y <= height-2)	 if (col == (int) grid[y+1][x] && int( grid[y+1][x] ) ^ (int) col == SELECTED) FLOOD(x, y+1, col);
}
void SHIFTALLDOWN() {
	for (int y = height - 2; y >= 0; y--) {
		for (int x = 0; x < width; x++) {
			TB = grid[y][x];
			BB = grid[y+1][x];
			if (IS_EMPTY(BB) && (!IS_EMPTY(TB))) {
				grid[y+1][x] = grid[y][x];
				grid[y][x] = EMPTY;
				x--;
				if (y+1 != height-1) y++;
				else y = height-2;
			}
		}
	}
}
void SHIFTALLRIGHT() {
	for (int y = 0; y < height; y++) {
		for (int x = width - 2; x >= 0; x--) {
			LB = grid[y][x];
			RB = grid[y][x+1];
			if (IS_EMPTY(RB) && (!IS_EMPTY(LB))) {
				grid[y][x+1] = grid[y][x];
				grid[y][x] = EMPTY;
				y--;
				x = width - 2;
			}
		}
	}
}
bool ADDNEWCOLUMNS() {
	bool anyNew = 0;
	for (int x = 0; x < width; x++) {
		if (IS_EMPTY(grid[height-1][x])) {
			anyNew = 1;
			int rand = RandomNumber(height + 1, 1);
			int rand2;
			for (int y = height - 1; y >= height - (rand - 1); y--) {
				rand2 = RandomNumber(4, 0);
				grid[y][x] = rand2;
			}
		}
	}
	return anyNew;
}
bool CHECKFORFINISH() {
	bool fin = 1;
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			if (IS_EMPTY(grid[y][x])) continue;
			if (!IS_EMPTY(grid[y][x+1]) && x < width - 2) if (grid[y][x] == grid[y][x+1]) return 0;
			if (!IS_EMPTY(grid[y][x-1]) && x > 1) if (grid[y][x] == grid[y][x-1]) return 0;
			if (!IS_EMPTY(grid[y+1][x]) && y < height - 2) if (grid[y][x] == grid[y+1][x]) return 0;
			if (!IS_EMPTY(grid[y-1][x]) && y > 1) if (grid[y][x] == grid[y-1][x]) return 0;
		}
	}
	return 1;
}
void COUNTBALLS(HWND hwnd) {
	int ballcount[4]; // Red, Green, Blue, Yellow
	for (int i = 0; i < 4; i++) ballcount[i] = 0;
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			switch (grid[y][x]) {
				case RED: ballcount[0]++; break;
				case GREEN: ballcount[1]++; break;
				case BLUE: ballcount[2]++; break;
				case YELLOW: ballcount[3]++; break;
			}
		}
	}
	string txt;
	txt  = "Reds:\t\t";
	txt += INTTOCHARP(ballcount[0]);
	txt += "\nGreens:\t\t";
	txt += INTTOCHARP(ballcount[1]);
	txt += "\nBlues:\t\t";
	txt += INTTOCHARP(ballcount[2]);
	txt += "\nYellows:\t\t";
	txt += INTTOCHARP(ballcount[3]);
	MessageBox(hwnd, (char *) txt.c_str(), "Ball count", MB_OK);
}
HBITMAP GETBALLBMP(UINT ball) {
	switch (ball) {
		case IDB_BLUE1:		return BLUEBMP;		break;
		case IDB_GREEN1: 	return GREENBMP; 	break;
		case IDB_RED1: 		return REDBMP; 		break;
		case IDB_YELLOW1: 	return YELLOWBMP; 	break;
		case IDB_BLUE2: 	return BLUEBMP_S; 	break;
		case IDB_GREEN2: 	return GREENBMP_S; 	break;
		case IDB_RED2: 		return REDBMP_S; 	break;
		case IDB_YELLOW2: 	return YELLOWBMP_S; break;
	}
	return NULL;
}
UINT GETBALLINDEX(int ball, bool selected) { // Retrieves Ball ID
	bi = ball ^ (selected ? SELECTED : 0);
	switch (bi) {
		case BLUE:					return IDB_BLUE1;	break;
		case GREEN:					return IDB_GREEN1;	break;
		case RED:					return IDB_RED1;	break;
		case YELLOW:				return IDB_YELLOW1;	break;
		case BLUE	| SELECTED:		return IDB_BLUE2;	break;
		case GREEN	| SELECTED:		return IDB_GREEN2;	break;
		case RED	| SELECTED:		return IDB_RED2;	break;
		case YELLOW	| SELECTED:		return IDB_YELLOW2;	break;
	}
	return EMPTY;
}

void CLIENTRESIZE(HWND hWnd, int nWidth, int nHeight) {
  RECT rcClient, rcWindow;
  POINT ptDiff;
  GetClientRect(hWnd, &rcClient);
  GetWindowRect(hWnd, &rcWindow);
  ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
  ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
  MoveWindow(hWnd,rcWindow.left, rcWindow.top, nWidth + ptDiff.x, nHeight + ptDiff.y, TRUE);
}

void DRAWSTATUS(HDC hdc) {
	_itoa_s(score, _score, 10, 10);
	TextOut(hdc, 10, 3 + ballSize * height, "Score:", 6);
	TextOut(hdc, 70, 3 + ballSize * height, _score, GETINTLENGTH(score));
	if (width < 11) return;
	_itoa_s(tempScore, _tempscore, 10, 10);
	TextOut(hdc, 150, 3 + ballSize * height, "Selection value:", 16);
	TextOut(hdc, 270, 3 + ballSize * height, _tempscore, GETINTLENGTH(tempScore));
}
void StartDelay(HWND hwnd, int dur, int id) {
	idg = 1;
	SetTimer(hwnd, id, dur, NULL);
}
void EndDelay(HWND hwnd, int id) {
	KillTimer(hwnd, id);
}
void FuncAfterDelay(HWND hwnd, void (*f)(HWND), int dur, int id) {
	delayFunction = f;
	StartDelay(hwnd, dur, id);
}
//
int CALLBACK Dimensions(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	UNREFERENCED_PARAMETER(lParam);
	switch (message) {
		case WM_INITDIALOG:
			{
				SetDlgItemText(hDlg, IDC_WIDTH, INTTOCHARP(width));
				SetDlgItemText(hDlg, IDC_HEIGHT, INTTOCHARP(height));
				return (INT_PTR)TRUE;
			}
		case WM_COMMAND:
			{
				switch (LOWORD(wParam)) {
					case IDOK:
						{
							_w = GetDlgItemInt(hDlg, IDC_WIDTH, 0, 0);
							_h = GetDlgItemInt(hDlg, IDC_HEIGHT, 0, 0);
							_w = _w > 40 ? 40 : _w < 5 ? 5 : _w;
							_h = _h > 30 ? 30 : _h < 5 ? 5 : _h;
							EndDialog(hDlg, LOWORD(wParam));
							return IDOK;
						}
						break;
					case IDCANCEL:
						{
							EndDialog(hDlg, LOWORD(wParam));
							return IDCANCEL;
						}
						break;
				}
			}
			break;
		}
	return (INT_PTR)FALSE;
}
void SHIFTR(HWND hwnd) {
	SHIFTALLRIGHT();
	REPAINT(hwnd);
	EndDelay(hwnd, ID_DELAY2);
	if (CHECKFORFINISH()) {
		MessageBox(hwnd, "Finished!", "End", MB_OK);
		if (MessageBox(hwnd, "Do you want to start a new game?",
			(char*) L"", MB_YESNO | MB_ICONWARNING) == IDYES) {
			CLIENTRESIZE(hwnd, width * ballSize, 23 + (height * ballSize));
			NEWGAME(hwnd);
		}
	}
	idg = 0;
}
void SHIFT(HWND hwnd) {
	SHIFTALLDOWN();
	if (MEGASHIFT) {
		while (ADDNEWCOLUMNS());
		REPAINT(hwnd);
		FuncAfterDelay(hwnd, SHIFTR, 100, ID_DELAY2);
	}
	REPAINT(hwnd);
	score += tempScore;
	tempScore = 0;
	if (!MEGASHIFT) idg = 0;
	EndDelay(hwnd, ID_DELAY);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg) {
		case WM_TIMER:
			{
				delayFunction(hwnd);
			}
			break;
		case WM_COMMAND:
			{
			switch (LOWORD(wParam)) {
				case ID_OPTIONS_DIMENSIONS:
					{
						int ret = (int) DialogBox(NULL, MAKEINTRESOURCE(IDD_DIMENSIONS), hwnd, Dimensions);
						if (ret == IDOK) {
							if (MessageBox(hwnd, "Do you want to start a new game?",
								"Warning", MB_YESNO | MB_ICONWARNING) == IDYES) {
								CLIENTRESIZE(hwnd, _w * ballSize, 23 + (_h * ballSize));
								width = _w;
								height = _h;
								NEWGAME(hwnd);
							}
						}
					}
					break;
				case ID_OPTIONS_MEGASHIFT:
					{
						if (MessageBox(hwnd, "Do you want to start a new game?",
							"Warning", MB_YESNO | MB_ICONWARNING) == IDYES) {
							NEWGAME(hwnd);
							if (!MEGASHIFT) {
								SetWindowText(hwnd, "SameGame - Megashift");
								MEGASHIFT = 1;
							} else {
								SetWindowText(hwnd, "SameGame");
								MEGASHIFT = 0;
							}
						}
					}
					break;
				case ID_GAME_NEWGAME:
					{
					NEWGAME(hwnd);
					}
					break;
				case ID_GAME_EXIT:
					{
					DestroyWindow(hwnd);
					}
					break;
				case ID_HELP_ABOUT:
					{
					MessageBox(hwnd, "William Hemsworth\n2008 March 7",
							  "About", MB_OK | MB_ICONINFORMATION);
					}
					break;
				case ID_HELP_RULES:
					{
					MessageBox(hwnd,
"          Same Game\n\n\
The aim of the game is to try and get the\n\
highest score possible and to do that you\n\
need to try and gather as many same coloured balls\n\
together to retrieve the highest amounts of\n\
points once clicked.", "Rules", MB_OK);
					}
					break;
				case ID_TOOLS_COUNTCOLOURS:
					{
						COUNTBALLS(hwnd);
					}
					break;
				}
			}
			break;
		case WM_KEYDOWN:
			{
				switch (LOWORD(wParam)) {
					case 112:
						{
							try {
								COUNTBALLS(hwnd);
							} catch (void *v) {
							}
						}
						break;
					case 113:
						NEWGAME(hwnd);
						break;
					case 27:
						DestroyWindow(hwnd);
						break;
				}
			}			
			break;
		case WM_LBUTTONDOWN:
			{
				if (idg) break; // If in a delay
				bc = 0;
				POINT pt;
				GetCursorPos(&pt);
				ScreenToClient(hwnd, &pt);
				int x = pt.x / ballSize;
				int y = pt.y / ballSize;
				if (IS_EMPTY(grid[y][x])) {
					tempScore = 0;
					UNSELECTALL();
					REPAINT(hwnd);
					break;
				}
				bool alreadySelected = IS_SELECTED((int)grid[y][x]);
				if (!alreadySelected) {
					UNSELECTALL();
					FLOOD(x,y,(int)grid[y][x]);
					tempScore = ((bc*bc)+(3*bc)+4);
					if (bc==1) {
						UNSELECTALL();
						tempScore = 0;
						REPAINT(hwnd);
						break;
					}
					REPAINT(hwnd);
				}
				else {
					REMOVEALLSELECTED();
					PlaySound((char*)IDR_POP,NULL,SND_RESOURCE|SND_ASYNC); 
					REPAINT(hwnd);
					FuncAfterDelay(hwnd, SHIFT, 100, ID_DELAY);
				}
			}
			break;
		case WM_CLOSE:
			{
			DeleteObject(BLUEBMP);
			DeleteObject(GREENBMP);
			DeleteObject(REDBMP);
			DeleteObject(YELLOWBMP);
			DeleteObject(BLUEBMP_S);
			DeleteObject(GREENBMP_S);
			DeleteObject(REDBMP_S);
			DeleteObject(YELLOWBMP_S);
			DeleteObject(pen);
			DeleteObject(panel);
			DeleteObject(hdc);
			DeleteObject(bmp);
			DeleteObject(_hdc);
			DeleteObject(_hdcMem);
			DeleteObject(hbmOld);
			DestroyWindow(hwnd);
			}
			break;
		case WM_ERASEBKGND:
			{
				hdc = GetDC(hwnd);
				SelectObject(hdc , pen);
				for (unsigned int y = 0; y < (unsigned int) height; y++) {
					for (unsigned int x = 0; x < (unsigned int) width; x++) {
						if (!IS_EMPTY(grid[y][x])) continue;
						Rectangle(hdc, x * ballSize, y * ballSize,
							(x * ballSize) + ballSize, (y * ballSize) + ballSize);
					}
				}
				SelectObject(hdc , panel);
				Rectangle(hdc, 0, height * ballSize, width * ballSize, 23 + (height * ballSize));
				MoveToEx(hdc, 0, height * ballSize, NULL);
				LineTo(hdc, ballSize * width, ballSize * height);
				DRAWSTATUS(hdc);
			}
			break;
		case WM_PAINT:
			{
				_hdc = BeginPaint(hwnd, &ps);
				_hdcMem = CreateCompatibleDC(_hdc);

				for (unsigned int y = 0; y < (unsigned int) height; y++) {
					for (unsigned int x = 0; x < (unsigned int) width; x++) {
						if (IS_EMPTY(grid[y][x])) continue;
						bmp = GETBALLBMP(GETBALLINDEX(grid[y][x], 0));
						hbmOld = (HBITMAP) SelectObject(_hdcMem, bmp);
						GetObject(bmp, sizeof(bm), &bm);
						BitBlt(_hdc, x * ballSize, y * ballSize, bm.bmWidth, bm.bmHeight, _hdcMem,0, 0, SRCCOPY);
					}
				}
				SelectObject(_hdcMem, hbmOld);
				DeleteDC(_hdcMem);
				EndPaint(hwnd, &ps);
			}
			break;
		case WM_CREATE:
			{
				CLIENTRESIZE(hwnd, width * ballSize, 23 + (height * ballSize));
				NEWGAME(hwnd);
			}
			break;
		case WM_DESTROY:
			{
				PostQuitMessage(0);
			}
			break;
		default:
			return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, char *cmdl, int nShowCmd) {
	WNDCLASSEX wc;
	HWND hwnd;
	MSG msg;
	wc.cbClsExtra = 0;
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.cbWndExtra = 0;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH) CreateSolidBrush(RGB(255,255,255));
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_ICON2));
	wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_ICON2));
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = cn;
	wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
	wc.style = 0;
	if (!RegisterClassEx(&wc)) {
		MessageBox(NULL, (char*)L"Error, coulden't register window class", (char*)L"Error", MB_OK);
		return 1;
	}
	hwnd = CreateWindowEx(WS_EX_APPWINDOW,
						  cn,
						  "SameGame",
						  WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
						  300, 200, width * ballSize, height * ballSize,
						  NULL, NULL, hInstance, NULL);
	if (hwnd == NULL) {
		MessageBox(NULL, (char*)L"Error, coulden't create the window", (char*)L"Error", MB_OK);
		return 1;
	}
	ShowWindow(hwnd, nShowCmd);
	UpdateWindow(hwnd);
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return (int) msg.wParam;
}

Here is the link to download the poject.
http://www.2shared.com/file/3022171/f34d7237/SameGame.html

Recommended Answers

All 11 Replies

>>txt += INTTOCHARP(ballcount[0]);
In COUNTBALLS() -- INTTOCHARP() allocates memory with new but is never deleted.

Thanks for finding it!, spent ages looking.

Actually, that was a problem but its not the main memory leak. I think it takes place in WM_LBUTTONDOWN but I cant find exactly where abouts.

variables _score and _tempscore -- why use new to allocate only a single character? That seems like such as misuse of dynamic memory allocation. And those two variables appear to be used without ever initializing them to anything. Is there code that you didn't post that actually sets them to something ?

I used them to turn the score (int) to a char array in this function, its in the code.
void DRAWSTATUS(HDC hdc)

Because of the way how you use _itoa_s(...) and the related buffers, _score, _tempscore and val, the program runs currently on good luck.
You could change all the _itoa_s() calls to use a single array of chars of sufficient size,
i.e.

static char itoa_conv_buff[MAX_BUF_LEN_HERE];
_itoa_s(num, itoa_conv_buff, MAX_BUF_LEN_HERE, 10);

If I open up task manager and view the memory that SameGame is using, everytime a selection of balls are clicked, the memory usage goes up by 4-6k until it is eventually quite high. It slows down windows alot after a while.

thanks mitrmkar, I did what you sead but I am still leaking memory =[

I am still leaking memory =[

The _itoa_s() did not actually have much to do with the memory leaking, it was just a safety measure so that your program will not crash.

About the memory leaks, I think there's at least two places where it happens

1) For each SetTimer() call there should to be counterpart KillTimer() to use the timer resources properly.
2) You are not calling ReleaseDC(hwnd, hdc) in your WM_ERASEBKGND handler, i.e. every hdc you get using GetDC() is to be released by ReleaseDC(hwnd, hdc).

Im quite sure its not the timers because It was leaking memory before I put those in so Im going to try your second point first.
Thanks :)

Thanks mitrmkar!, releasing that dc fixed the problem.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.