Tips On My Win32 Program

Started by
8 comments, last by DudeMiester 21 years, 6 months ago
Here''s a fairly simple C++ Win32 program i just finished writing, and i was wondering if anyone could give me any tips, suggestions and bits of info about it. It consists of the Main.cpp where my main stuff is located, ScreenBuffer.h and ScreenBuffer.cpp which is a wrapper class for DCs, and ThreadStuff.h and ThreadStuff.cpp which has three classes for threads, mutexes and critical sections. Main.cpp
  
#define WIN32_LEAN_AND_MEAN		//Gets rid of rarly used stuff


#include <windows.h>		//Win32 API header file
#include <windowsx.h>		//Win32 API Extensions header file
#include "resource.h"		//Resource IDs, the icon and the cursor that doesn''t work(??)
#include "ScreenBuffer.h"	//My ScreenBuffer class
#include "ThreadStuff.h"	//My Thread classes


//Message Handler prototype

LRESULT CALLBACK MsgHandler(
    HWND hwnd,		//Window Handle

    UINT msg,		//Message Identifier (WM_ constants)

    WPARAM wparam,	//Message parameter

    LPARAM lparam	//Message parameter

);

DWORD WINAPI Raster(LPVOID Param); //Drawing function declaration


HWND hWnd;				//Window Handle

ScreenBuffer Screen;	//Front screenbuffer

ScreenBuffer Canvas;	//Back screenbuffer


//Thread that does the animation,

Thread ThreadRaster(&Raster,			//Drawing function

					CREATE_SUSPENDED);	//Starts off suspended.


//Main Function

int WINAPI WinMain(HINSTANCE hInstance,		//Application Instance

				   HINSTANCE hPrewInstance,	//Instance of calling application

				   LPSTR lpCmdLine,			//Pointer to command line parameters

				   int nShowCmd)			//Info on how to show window (SW_ constants)

{

	WNDCLASSEX TestClass;	//Window Descriptor

	MSG Msg;				//Message variable


	//Fill in Window Descriptor

	TestClass.cbClsExtra=0;										//Extra class info

	TestClass.cbSize=sizeof(WNDCLASSEX);						//Size of window descriptor

	TestClass.cbWndExtra=0;										//Extra window info

	TestClass.hbrBackground=NULL;								//How to paint the background on refresh/repaint

	TestClass.hCursor=LoadCursor(hInstance, "IDC_CURSOR1");		//Application Cursor, doesn''t work for some reason

	TestClass.hIcon=LoadIcon(hInstance, "IDI_ICON1");			//Application Icon

	TestClass.hIconSm=LoadIcon(hInstance, "IDI_ICON1");			//Small icon in top corner

	TestClass.hInstance=hInstance;								//Application Instance

	TestClass.lpfnWndProc=MsgHandler;							//Location of Message Handler

	TestClass.lpszClassName="Test Class";						//Window Name

	TestClass.lpszMenuName=NULL;								//Menu Name, if there is one

	TestClass.style=CS_DBLCLKS|CS_OWNDC|CS_HREDRAW|CS_VREDRAW;	//Info on Window Style (CS_ constants)


	RegisterClassEx(&TestClass);	//Register window


	//Create window, and set hwnd to its handle

	hWnd=CreateWindowEx(NULL,							//Extended window styles (WS_EX_ constants)

						"Test Class",					//Window Name

						"Test Window",					//Window Title

						WS_OVERLAPPEDWINDOW|WS_VISIBLE,	//Window Style (WS_ constants)

						CW_USEDEFAULT, CW_USEDEFAULT,	//Window x,y coordinates

						308, 334,						//Window width and hight in pixels

						NULL,							//Parent window handle

						NULL,							//Menu Handle

						hInstance,						//Application Instance

						NULL);							//Used for making things like MDIs



	Screen.GetBuffer(hWnd);		//Initalize front buffer

	Canvas.CopyBuffer(Screen);	//Make Canvas a copy of Screen


	ThreadRaster.Resume();		//Start drawing animation thread now that everything is initalized


	//Main program loop

	while(true)
	{
		/*
		Prototype for PeekMessage function that checks if there are any pending messages in the message queue
		BOOL PeekMessage(
			LPMSG lpMsg,		Pointer to MSG type variable that contains message info
			HWND hWnd,			Window Handle for the message queue you want to check
			UINT wMsgFilterMin,	First message to check
			UINT wMsgFilterMax,	Last message to check
			UINT wRemoveMsg		Removal flags, takes either: PM_REMOVE to remove the message from the queue after handling or
															 PM_NOREMOVE to keep the message in the queue after handling
		);
		*/
		if(PeekMessage(&Msg, 0, 0, 0, PM_REMOVE))	//Check for messages

		{
			if(Msg.message==WM_QUIT)	//Check if it''s time to quit

				break;	//Leave main loop

			TranslateMessage(&Msg);	//Prepares message for handeling

			DispatchMessage(&Msg);	//Sends the message to the message handler

		}
		else
			WaitMessage();	//Wait for a new message

	}

	DestroyWindow(hWnd);	//Destroy the window

	UnregisterClass("Test Class", hInstance);	//Unregister window class


	return 0;	//End program

}

//Message Handler function

LRESULT WINAPI MsgHandler(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	//Put code to handle anything you want to handle manually here, usually in a big switch statement.

	//Return 0 if message was handled correctly.

	switch(Msg)
	{
	case WM_KEYDOWN:	//If a key was pressed

		switch(wParam)
		{
		case VK_ESCAPE:	//If escape key was pressed

			PostQuitMessage(0);	//End program

			break;
		}
		return 0;
		break;

	case WM_DESTROY:		// any shutdown functions could go here

		PostQuitMessage(0);	//End program

		return 0;
		break;

	case WM_SIZE:
		switch(wParam)
		{
		case SIZE_MAXIMIZED:	//If window is maximized or resized

		case SIZE_RESTORED:
			Screen.Size.right=LOWORD(lParam);						//Set the front buffer''s new size

			Screen.Size.bottom=HIWORD(lParam);
			Canvas.Resize(Screen.Size.right, Screen.Size.bottom);	//Resize the back buffer

			ThreadRaster.Resume();									//Resume drawing if stopped

			break;
		case SIZE_MINIMIZED:	//If window is minimized

			ThreadRaster.Suspend();	//Stop drawing

			break;
		}
		return 0;
		break;

	case WM_PAINT:	//If it''s time to repaint

		PAINTSTRUCT PS;
		BeginPaint(hwnd, &PS);
		//Do any static drawings here

		EndPaint(hwnd, &PS);
		return 0;
		break;
	}

    return DefWindowProc(hwnd, Msg, wParam, lParam); //Send unhandled messages to the default windows Message Handler

}

//Draw frame to back buffer function

void DoFrame()
{
	static int Change=0;	//Direction of lines

	static int Pos=0;		//Position of lines

	static int Change2=0;	//Direction of window

	static double Pos2=0;	//Position of window


	//Collision check lines with window edges

	if(Pos>Canvas.Size.right-100)	//If lines hit the sides

	{
		Change=1;					//Change direction

		Pos=Canvas.Size.right-100;	//Reset position

	}
	else if(Pos>Canvas.Size.bottom-100)	//If lines hit the bottom

	{
		Change=1;					//Change direction

		Pos=Canvas.Size.bottom-100;	//Reset position

	}
	else if(Pos<0)	//If lines hit the top

	{
		Change=0;	//Change direction

		Pos=0;		//Reset position

	}

	if(Pos2>1)	//If window reached max size

	{
		Change2=1;	//Change direction

		Pos2=1;		//Reset position

	}
	else if(Pos2<0)	//If window reached minimum size

	{
		Change2=0;	//Change direction

		Pos2=0;		//Reset position

	}

	for(int i=0; i<100; i++)	//Draw lines to back buffer

	{
		SetPixel(Canvas.hDC, i+Pos, i+Pos, RGB(255, 0, 0)); //Draw first line

		SetPixel(Canvas.hDC, -i+Canvas.Size.right-Pos, i+Pos, RGB(255, 0, 0));	//Draw second line

	}

	//Create and set window''s next shape

	//Create a rectangle the size of the whole window

	HRGN temp=CreateRectRgn(0, 0, Screen.Size.right+8, Screen.Size.bottom+34);
	//Cut out the inside of the window, not the boarders and title bar

	CombineRgn(temp, temp, CreateRectRgn(4, 30, Screen.Size.right+4, Screen.Size.bottom+30), RGN_XOR);
	//Add on the animated circle

	CombineRgn(temp, temp, CreateEllipticRgn(Pos2/4*Screen.Size.right+4,
											 Pos2/4*Screen.Size.bottom+30,
											 Screen.Size.right-Pos2/6*Screen.Size.right+4,
											 Screen.Size.bottom-Pos2/6*Screen.Size.bottom+30), RGN_OR);
	SetWindowRgn(hWnd, temp, true); //Set region to window

	DeleteObject(temp);	//Delete temp


	if(Change==0)	//Check how the lines'' position should change, and change it

		Pos+=4;
	else
		Pos-=4;

	if(Change2==0)	//Check how the window''s shape should change, and change it

		Pos2+=0.02;
	else
		Pos2-=0.02;
}

//Draw function

DWORD WINAPI Raster(LPVOID Param)
{
	int StartTime=0;
	
	//Main drawing loop

	while(true)
	{
		StartTime=GetTickCount(); //Get frame start time


		DoFrame();				//Draw to back buffer

		Canvas.CopyTo(Screen);	//Copy back buffer to front

		Canvas.Erase();			//Clear back buffer

		
		Sleep(33+GetTickCount()-StartTime);		//Wait for next frame

	}

	return 0;
}
  
ScreenBuffer.h
  
#ifndef SCREENBUFFER_H
#define SCREENBUFFER_H

//Include Win32 API headers

#include <windows.h>
#include <windowsx.h>

//Define ScreenBuffer class

class ScreenBuffer  
{
public:
	HDC hDC;			//DC handle

	RECT Size;			//DC size					Note: RECT.Right==Width, RECT.Bottom==Height, RECT.left==X, RECT.top==Y.

	COLORREF BGColour;	//Erase colour

	ScreenBuffer(HWND hWnd, COLORREF BkColour=0);	//Create from window constuctor

	ScreenBuffer(ScreenBuffer& a);					//Copy constuctor

	ScreenBuffer();									//Default constuctor

	BOOL GetBuffer(HWND hWnd, COLORREF BkColour=0);	//Initalize from window

	BOOL CopyBuffer(ScreenBuffer& a);				//Copy from another ScreenBuffer

	~ScreenBuffer();								//Destuctor

	BOOL Resize(int Width, int Height);				//Resize ScreenBuffer

	BOOL CopyTo(ScreenBuffer &a);					//Copy ScreenBuffer contents to another ScreenBuffer

	BOOL Erase();									//Clear ScreenBuffer

};

#endif
  
ScreenBuffer.cpp
  
#include "ScreenBuffer.h"

BOOL ScreenBuffer::Resize(int Width, int Height)
{
	Size.right=Width;	//Set new size

	Size.bottom=Height;
	//Create a new bitmap of specified size, and set it to the DC

	return DeleteObject(SelectObject(hDC, CreateCompatibleBitmap(hDC, Width, Height)));
}

BOOL ScreenBuffer::CopyTo(ScreenBuffer &a)
{
	//Copy DC contents to ScreenBuffer a

	return BitBlt(a.hDC, 0, 0, Size.right, Size.bottom, hDC, 0, 0, SRCCOPY);
}

BOOL ScreenBuffer::Erase()
{
	//Set the DC bitmap to background colour

	return FillRect(hDC, &Size, CreateSolidBrush(BGColour));
}

ScreenBuffer::ScreenBuffer(HWND hWnd, COLORREF BackGroundColour)
{
	hDC=GetDC(hWnd);			//Get the DC from the window

	BGColour=BackGroundColour;	//Set the background colour

	GetClientRect(hWnd, &Size);	//Get the DC''s size

	Size.left=0;				//Reset the position to (0, 0)

	Size.top=0;
}

ScreenBuffer::ScreenBuffer(ScreenBuffer& a)
{
	hDC=CreateCompatibleDC(a.hDC);	//Create a copy of a''s DC

	BGColour=a.BGColour;	//Set the background colour to a''s

	Size.left=a.Size.left;	//Set the DC position and size to a''s

	Size.top=a.Size.top;
	Size.right=a.Size.right;
	Size.bottom=a.Size.bottom;
	//Create a compaible bitmap the same size as a''s, and set it to the DC

	DeleteObject(SelectObject(hDC, CreateCompatibleBitmap(a.hDC, Screen.Size.right, Screen.Size.bottom))); 
}

BOOL ScreenBuffer::GetBuffer(HWND hWnd, COLORREF BackGroundColour)
{
	hDC=GetDC(hWnd);	//Get the DC from the window

	if(hDC==NULL)	//Check if it was successful

		return FALSE;

	BGColour=BackGroundColour;	//Set the background colour

	GetClientRect(hWnd, &Size);	//Get the DC''s size

	Size.left=0;				//Reset the position to (0, 0)

	Size.top=0;
	return TRUE;
}

BOOL ScreenBuffer::CopyBuffer(ScreenBuffer& a)
{
	hDC=CreateCompatibleDC(a.hDC);	//Create a copy of a''s DC

	if(hDC==NULL)	//Check if it was successful

		return FALSE;

	BGColour=a.BGColour;	//Set the background colour to a''s

	Size.left=a.Size.left;	//Set the DC''s position and size to a''s

	Size.top=a.Size.top;
	Size.bottom=a.Size.bottom;
	Size.right=a.Size.right;
	//Create a compaible bitmap the same size as a''s, and set it to the DC

	return DeleteObject(SelectObject(hDC, CreateCompatibleBitmap(a.hDC, Screen.Size.right, Screen.Size.bottom)));
}

ScreenBuffer::ScreenBuffer()
{
	hDC=NULL;		//Set the DC to Null

	Size.top=0;		//Set the DC''s position and size to 0

	Size.left=0;
	Size.right=0;
	Size.bottom=0;
	BGColour=0;		//Set the background colour to 0 or black.

}

ScreenBuffer::~ScreenBuffer()
{
	DeleteDC(hDC);	//Delete the DC

}
  
ThreadStuff.h
  
#ifndef THREADSTUFF_H
#define THREADSTUFF_H

//Include Win32 API headers

#include <windows.h>
#include <windowsx.h>

//Define Thread class

class Thread  
{
public:
	HANDLE hThread;	//Thread handle

	DWORD ThreadID;	//Thread ID

	//Thread constuctor

	Thread(LPTHREAD_START_ROUTINE ThreadFunction, DWORD StartStatus=0, LPVOID Parameter=0, DWORD StackSize=0);
	Thread();	//Default constuctor

	~Thread();	//Thread destuctor

	//Initalize thread

	BOOL Initalize(LPTHREAD_START_ROUTINE ThreadFunction, LPVOID Parameter=0, DWORD StartStatus=0, DWORD StackSize=0);
	DWORD Suspend();	//Suspend thread

	DWORD Resume();		//Resume thread

	void Pause(DWORD Milliseconds);	//Sleep thread

};

//Define Mutex class

class Mutex
{
public:
	HANDLE hMutex;	//Mutex handle

	LPCTSTR Name;	//Mutex name

	Mutex(BOOL StartLocked, LPCTSTR MutexName);	//Mutex constuctor

	Mutex();	//Default constuctor

	~Mutex();	//Mutex destuctor

	BOOL Unlock();									//Unlock Mutex

	DWORD WaitToLock(DWORD Milliseconds=INFINITE);	//Wait for Mutex to unlock, then lock it

	BOOL Open(LPCTSTR MutexName);	//Open this Mutex to another one

};

//Define CriticalSection

class CriticalSection
{
public:
	CRITICAL_SECTION hCriticalSection;	//CriticalSection handle

	CriticalSection();	//CriticalSection constuctor

	~CriticalSection();	//CriticalSection destuctor

	void WaitToLock();	//Wait for CriticalSection to unlock, then lock it

	void Unlock();		//Unlock CriticalSection

};
	

#endif
  
ThreadStuff.cpp
  
#include "ThreadStuff.h"

Thread::Thread(LPTHREAD_START_ROUTINE ThreadFunction, DWORD StartStatus, LPVOID Parameter, DWORD StackSize)
{
	//Create the thread

	hThread=CreateThread(NULL,			//No security atributes (what are they for?)

						 StackSize,		//The stack size

						 ThreadFunction,//Thread''s main function

						 Parameter,		//Fuction parameter

						 StartStatus,	//Start suspended or not

						 &ThreadID);	//Address of where the thread ID will be stored

}

Thread::Thread()
{
	hThread=NULL;	//Set thread handle to nothing

	ThreadID=0;		//Set the thread''s ID to nothing

}

Thread::~Thread()
{
	CloseHandle(hThread);	//Delete the thread

}

DWORD Thread::Suspend()
{
	return SuspendThread(hThread);	//Suspend thread execution

}

DWORD Thread::Resume()
{
	return ResumeThread(hThread);	//Resume thread execution

}

void Thread::Pause(DWORD Milliseconds)
{
	Sleep(Milliseconds);	//Stop the thread excution for givin number of milliseconds

}


Mutex::Mutex(BOOL StartLocked, LPCTSTR MutexName)
{
	Name=MutexName;		//Set the mutex name

	//Create the mutex

	hMutex=CreateMutex(NULL,		//No secutity attibutes (what are they for?)

					   StartLocked, //Initally locked or not

					   MutexName);	//The name the mutex will have

}

Mutex::Mutex()
{
	Name=NULL;		//Set name to nothing

	hMutex=NULL;	//Set the mutex handle to nothing

}

Mutex::~Mutex()
{
	Name=NULL;				//Set the name to nothing

	CloseHandle(hMutex);	//Close the mutex

}

BOOL Mutex::Unlock()
{
	return ReleaseMutex(hMutex);	//Unlock the mutex

}

DWORD Mutex::WaitToLock(DWORD Milliseconds)
{
	//Pause the calling thread''s execution until the mutex unlocks, then lock it.

	//If the givin time limit is reached, then funtion returns with a fail.

	return WaitForSingleObject(hMutex, Milliseconds);
}

BOOL Mutex::Open(LPCTSTR MutexName)
{
	//Make a new mutex, and check if it was a success

	if((hMutex=OpenMutex(MUTEX_ALL_ACCESS, FALSE, MutexName))==NULL)
	{
		Name=NULL;		//If not, set the name to nothing

		return FALSE;	//Return fail

	}
	else
	{
		Name=MutexName;	//Otherwise, set the name

		return TRUE;	//Return success

	}
}


CriticalSection::CriticalSection()
{
	//Create the cirtical section

	InitializeCriticalSection(&hCriticalSection);
}

CriticalSection::~CriticalSection()
{
	//Delete the critical section

	DeleteCriticalSection(&hCriticalSection);
}

void CriticalSection::WaitToLock()
{
	//Suspend calling thread''s execution until the critacal section unlocks, then lock it

	EnterCriticalSection(&hCriticalSection);
}

void CriticalSection::Unlock()
{
	//Unlock the critical section

	LeaveCriticalSection(&hCriticalSection);
}
  
[s] [/s]
I can see the fnords.
Advertisement
Sorry about the length. It''s just that this is my first Win32 program and i really want to find out what i''m doing right and what i''m doing wrong.
[s] [/s]
I can see the fnords.
It looks to me that your screen buffer class is going to leak GDI resources all over the place.

The call to DeleteDC isn''t likely to place nicely with a HDC returned by GetDC - which it appears that might be the case sometimes. Those calls to DeleteObject look a bit funky to me as well. SetPixelV is faster than SetPixel

To check for leaky gdi objects visit fengyuan.com and look for his gdiobj.exe tool - very helpful.

The critical section and mutex classes - I didn''t spot the resource they are meant to protect. Both of those synch objects do essentially the same thing - the primary difference is that a mutex functions across processes, whereas a critical section only functions within the currently running process.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
quote:Original post by DudeMiester
Sorry about the length. It''s just that this is my first Win32 program and i really want to find out what i''m doing right and what i''m doing wrong.


If this is your first Win32 prog, you might want to hold off on the thread stuff all together. Having threads in the mix won''t make it easier to work out the kinks.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Thanks, and the mutex and critical section classes were just there because i felt like doing them, but i never got around to using them, because i''m not sure what i should use them on. Do i use them only when two different threads my be writing to the same global variable, or when any thread is writing to any global variable and there might be another thread reading it?

The deleteobject() calls were to delete the old object that was being using by the DC, then the ones i put in would be deleted automatically when the program ends. At least, that''s what i think would happen, but i''m probably wrong arn''t i.
[s] [/s]
I can see the fnords.
Is the email address you have listed accurate? I''ll send you some references to threads and the like.

You have the general idea behind synch objects correct - but given the headaches that threads can produce, you''ll want to get a better grip on them before using them.

Regarding DeleteObject - and the way you have it wrapping the other functions - that may turn out all right - I didn''t count up the references - but that points out that it''s an unclear spot in the code and as a rule of thumb it''s better to code for clarity first and optimization last.

DelectObject(SelectObject(CreateCompatibleDC

The chain will probably work ok - the last bitmap might not get deleted though - but like I said I didn''t count all of the references.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
I used that program previously mentioned, and i found out that i was making TONS of regions and brushes. I fixed my brush problem though, it was when i used ScreenBuffer::Erase() i needed to delete the brush i created. I think the region problem is in DoFrame() where the region i create to change the window shape isn't getting deleted but i can't see where. Lastly, my real email is dudemiester@rogers.com. Thanks for you help

A minute later...

I just looked at it again, and now i see, the problem is where i create the regions i use in the CombineRgn() calls, but i don't delete them. Also, when i use gdiobj.exe it tells me everything is gone after i close the program.

[edited by - dudemiester on October 2, 2002 6:02:47 PM]

[edited by - dudemiester on October 2, 2002 6:06:04 PM]
[s] [/s]
I can see the fnords.
quote:Original post by DudeMiester
I used that program previously mentioned, and i found out that i was making TONS of regions and brushes.


Um, yeah, that''s what I meant about leaking GDI resources all over the place Don''t feel bad about it though, my first GDI project leaked like a seive too. That''s a great tool as well. Much more helpful than the task manager for checking for gdi leaks.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Thanks for all that stuff, and your help. I fixed all those leaking problems too, but for some reason i have 4 DCs, strange.

[edited by - dudemiester on October 2, 2002 6:31:29 PM]
[s] [/s]
I can see the fnords.
quote:Original post by DudeMiester
Thanks for all that stuff, and your help. I fixed all those leaking problems too, but for some reason i have 4 DCs, strange.

[edited by - dudemiester on October 2, 2002 6:31:29 PM]


Well - you have two screen objects and each of those looks like it has two dcs. Keep at it, I have no doubt you''ll get the hang of it. Enjoy those references too, no doubt they''ll come in handy at some point down the line.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man

This topic is closed to new replies.

Advertisement