Hey all,
I wrote a sort of wrapper class for creating a skeleton window. I'll post the code first, and then discuss it:
Main.cpp
Code:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "cWindow.h"
//PROTOTYPE FOR OUR MISC. MESSAGE HANDLING FUNCTION
long __stdcall MessageProc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam);
//OUR MAIN ENTRY POINT
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MSG Msg; //RECEIVES OUR MESSAGES
bool bRunning; //KEEPS TRACK OF WHETHER WE'RE STILL RUNNING OR NOT
cWindow* MainWindow; //OUR WINDOW OBJECT
MainWindow = new cWindow; //CREATE THE WINDOW OBJECT
//INITIALIZE OUR WINDOW
bRunning = MainWindow->Init((HBRUSH)GetStockObject(LTGRAY_BRUSH), &hInstance, &MessageProc, CS_OWNDC, WS_POPUP, 200, 300);
if(bRunning == true) //IF WE SUCCEEDED
{
//ACTIVATE THE WINDOW
MainWindow->FocusOnMe();
MainWindow->ShowMe();
MainWindow->UpdateMe();
while(bRunning == true) //MAKE SURE WE'RE STILL RUNNING
{
if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) != 0) //DO WE HAVE ANY MESSAGES WAITING
{
//CHECK WHAT THE MESSAGE IS
//
//IT IS EASIER TO MANIPULATE OUR VARIABLES (bRunning, MainWindow, etc.)
//BY KEEPING THE MESSAGE HANDLER OUT HERE
switch(Msg.message)
{
case WM_KEYDOWN: //A KEY WAS PRESSED
bRunning = false; //EXIT
default:
TranslateMessage(&Msg); //CONVERT OUR VIRTUAL-KEY MESSAGE INTO A CHARACTER MESSAGE
DispatchMessage(&Msg); //SEND THE MESSAGE TO OUR MISC. MESSAGE HANDLER
}
}
else
{
//THERE WAS NO MESSAGE
//
//UPDATE THE WINDOW
MainWindow->UpdateMe();
}
}
}
//DESTROY OUR WINDOW
bRunning = MainWindow->Kill(&hInstance);
//CLEAN UP THE OBJECT
delete MainWindow;
MainWindow = NULL;
return 0;
}
//long __stdcall MessageProc()
//
//THIS FUNCTION HANDLES ANY MESSAGES PASSED TO IT BY DispatchMessage()
//THAT WERE NOT HANDLED IN OUR WinMain() FUNCTION
long __stdcall MessageProc(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg) //CHECK WHAT THE MESSAGE IS
{
case WM_CLOSE: //CLOSE THE WINDOW
PostQuitMessage(0); //LET THE PROGRAM KNOW WE'RE QUITTING
return 0;
case WM_DESTROY: //DESTROY THE WINDOW
PostQuitMessage(0); //LET THE PROGRAM KNOW WE'RE QUITTING
return 0;
}
return(DefWindowProc(hWnd, Msg, wParam, lParam));
}
cWindow.h
Code:
#ifndef CWINDOW_H
#define CWINDOW_H
#include <Windows.h>
#include <String.h>
class cWindow
{
public:
cWindow();
virtual ~cWindow();
//INITIALIZES OUR WINDOW
bool Init(HBRUSH Colour, HINSTANCE *hInstance, long (__stdcall *MP)(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam),
unsigned int ClassStyles, unsigned long WindowStyles, unsigned int Width, unsigned int Height);
//DESTROYS OUR WINDOW
bool Kill(HINSTANCE *hInstance);
//SETS THE FOCUS ONTO OUR WINDOW
void FocusOnMe(void);
//SHOWS THE WINDOW
void ShowMe(void);
//UPDATES THE WINDOW
void UpdateMe(void);
private:
HWND hWnd; //THE HANDLE TO OUR WINDOW
char ClassName[10]; //THE NAME OF OUR CLASS
};
#endif
cWindow.cpp
Code:
#include "cWindow.h"
//cWindow::cWindow() (a.k.a. Constructor)
//
//PARAMETERS: None
//RETURNS: None
//
//DESCRIPTION:
//This function just makes sure our handle starts off as NULL and sets a name for our class
cWindow::cWindow()
{
hWnd = NULL;
strcpy(ClassName, "MyClass");
}
//cWindow::~cWindow() (a.k.a. Destructor)
//
//PARAMETERS: None
//RETURNS: None
//
//DESCRIPTION:
//This function does nothing because it assumes the programmer has already called the cWindow::Kill() function
//to clean everything up.
cWindow::~cWindow()
{}
//bool cWindow::Init()
//
//PARAMETERS:
//The colour for our background
//A pointer to the HINSTANCE generated by our WinMain() function
//A pointer to the misc. message handling function
//The styles for the class
//The sytles for the window
//The width of the window
//The height of the window
//RETURNS:
//Whether or not the window is created with success
//
//DESCRIPTION:
//This function is a wrapper for creating a window in the centre of the screen.
bool cWindow::Init(HBRUSH Colour, HINSTANCE *hInstance, long (__stdcall *MP)(HWND hWnd, unsigned int Msg, WPARAM wParam, LPARAM lParam),
unsigned int ClassStyles, unsigned long WindowStyles, unsigned int Width, unsigned int Height)
{
WNDCLASS WCL; //OUR CLASS OBJECT
unsigned short Registration; //KEEPS TRACK OF THE SUCCESS OF THE RegisterClass() FUNCTION
//INITIALIZE OUR WINDOW CLASS
WCL.cbClsExtra = 0; //SET ASIDE 0 EXTRA BYTES FOR THE CLASS
WCL.cbWndExtra = 0; //SET ASIDE 0 EXTRA BYTES FOR THE WINDOW
WCL.hbrBackground = Colour; //APPLY OUR COLOUR
WCL.hCursor = LoadCursor(NULL, IDC_ARROW); //MAKE OUR CURSOR THE STANDARD ARROW
WCL.hIcon = LoadIcon(NULL, IDI_APPLICATION); //CHOOSE THE STANDARD APPLICATION ICON
WCL.hInstance = *hInstance; //PASS THE HINSTANCE FROM WinMain()
WCL.lpfnWndProc = *MP; //PASS OUR MISC. MESSAGE HANDLER
WCL.lpszClassName = ClassName; //THE NAME OF OUR CLASS
WCL.lpszMenuName = NULL; //NO DEFAULT MENU
WCL.style = ClassStyles; //APPLY OUR CLASS STYLES
Registration = RegisterClass(&WCL); //ATTEMPT TO REGISTER THE CLASS
if(Registration == false)
return false;
//CREATE OUR WINDOW
hWnd = CreateWindow(
ClassName, //OUR CLASS NAME
"Game", //THE TITLE FOR OUR WINDOW
WindowStyles, //APPLY OUR STYLES
(GetSystemMetrics(SM_CXSCREEN) / 2) - (Width / 2), //PLACE IN THE HORIZONTAL CENTRE
(GetSystemMetrics(SM_CYSCREEN) / 2) - (Height / 2), //PLACE IN THE VERTICAL CENTRE
Width, //WIDTH OF THE WINDOW
Height, //HEIGHT OF THE WINDOW
NULL,
NULL,
*hInstance, //OUR HINSTANCE
NULL);
if(hWnd == NULL) //CHECK IF OUR hWnd WAS INITIALIZED
return false;
return true; //SUCCESS
}
//bool cWindow::KILL()
//
//PARAMETERS:
//A pointer to the HINSTANCE from WinMain()
//RETURNS:
//Whether or not everything was removed
//
//DESCRIPTION:
//This function cleans up our window variables
bool cWindow::Kill(HINSTANCE *hInstance)
{
MSG Msg; //CYCLES THROUGH OUR MESSAGES
int Destroyed; //KEEPS TRACK OF SUCCESS FOR DestroyWindow()
int Unregistered; //KEEPS TRACK OF SUCCESS FOR UnregisterClass()
if(hWnd != NULL) //MAKE SURE THERE'S SOMETHING TO REMOVE
{
Destroyed = DestroyWindow(hWnd); //DESTROY THE WINDOW
if(Destroyed != false)
{
//REMOVE ALL MESSAGES
while(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE));
{
DispatchMessage(&Msg);
}
}
hWnd = NULL; //CLEAR THE HANDLE TO OUR WINDOW
}
//UNREGISTER OUR CLASS
Unregistered = UnregisterClass(ClassName, *hInstance);
if((Destroyed == false)||(Unregistered == false)) //IF EITHER FAILED...
return false; //...RETURN FAILURE
return true; //RETURN SUCCESS
}
//void cWindow::FocusOnMe()
//
//PARAMETERS: None
//RETURNS: None
//
//DESCRIPTION:
//Sets the focus to our window
void cWindow::FocusOnMe(void)
{
SetFocus(hWnd);
}
//void cWindow::ShowMe()
//
//PARAMETERS: None
//RETURNS: None
//
//DESCRIPTION:
//Show our window
void cWindow::ShowMe(void)
{
ShowWindow(hWnd, SW_SHOW);
}
//void cWindow::UpdateMe()
//
//PARAMETERS: None
//RETURNS: None
//
//DESCRIPTION:
//Redraws our window
void cWindow::UpdateMe(void)
{
UpdateWindow(hWnd);
}
The problem with trying to make a class for a window (as far as I know) is getting that default message handling function assigned. It can't be a member function of the class since it must be statically declared (this is going on what I heard a couple of years back when I was trying this, so it may sound like I'm talking out of my...especially if you know the real terminology).
Now, the way I usually do the message loop in my programs is like what you see in my WinMain() function. So, all in all, I find the MessageProc() very useless. But, when creating a window, you DO need a MessageProc() function to handle messages (as far as I know). The reason I wrote it like that is so I can manipulate/make calls to my objects (I.e. MainWindow->UpdateMe) or something when necessary, say on a keypress. Hope you get the idea. However, with a bit of tinkering, I'm sure anyone who's written a skeleton window like this will be able to adjust the MessageProc() to handle ALL messages, if that's what they desire.
Note that the MessageProc() is defined in Main.cpp, and the Class uses the prototpye as one of its parameters. I think this is an okay mix, and wouldn't really have any issues if somebody tried to re-write the program from scratch, using the class, since there is only one way (as far as I know) to declare the default MessageProc() and its parameters.
The three "files" above will compile and run as an example of how the class is used.
Feel free to try it out, but more importantly, I'm hoping for some feedback. Are there any aspects that you think can be improved/written better? Any issues? Or just any comments?
Thanks for the read.