Do I have to use global variables?!?

Pages: 12
closed account (3pj6b7Xj)
My Squibbles game is about 90% complete, code wise that is, then I have some debugging to do. Anyway, here is the issue I had last night. To move a snake up, down, left or right, you send the snake a command for example ... snake.move_left() or snake.move_right() ... so I put my key mappings into the window procedure but was faced with a rather interesting scenario.....my snakes were out of scope!! I used all my knowledge of C++ to try to find a way and PREVENT using global variables.....but I ended up making making the Snakes global so I could access them from within the procedure......I guess it can't be too bad, the Snakes are classes and they control their own data but I really hate using global variables, it makes programs unreliable ... got any input for me guys? Game is working fine now but i'm pretty sure there must be something I DON'T KNOW that someone does out there that I can put to use and probably kill the idea of the global variable ....... oh well ... i've done some DirectX programming and there was heavy use of global variables in that, specially with the created device...thanks for your support guys.
Have you tried passing the snakes by reference in your functions?
http://cplusplus.com/doc/tutorial/functions2/

-Albatross
closed account (3pj6b7Xj)
How will that help if i'm trying to command the snakes to change directions from within the window procedure? The snake functions only take in a HDC device which is given through a function called init, the rest of the functions change data within the class. I know how to pass by reference, pointers and other bizarre ways id rather not talk about but that is not the point.

I can't command a change in direction from within the procedure.....at the window procedure, I intercept WM_KEYDOWN i trap my key of choice lets say its the up arrow and then I .... oh wait a minute.....oh darn I forgot all about GetKeyState functions totally over looked that. I can check for key presses at the main loop with that assuming it doesn't slow it down.
closed account (3pj6b7Xj)
goot it solved. can't believe how i over looked that.
GUI programs don't need global variables. None. Nada, Zip. Nichts. I've posted a lot of my code lately. Can you find any global variables in it? If you do let me know and I'll remove it.
closed account (3pj6b7Xj)
The scenario I had was trying to modify a variable from the windows procedure......since variables inside WinMain are out of scope at the Window Procedure, how could you modify them when a user presses a key, etc.

I thought of the idea of using SendMessage with the right parameters and then trapping that message in the program loop and act as required but it seems quite slow. I've seen some of your code...

Lets say for example, I have a variable named

string dogname("pluto");

so now at the windows procedure, I trap WM_KEYDOWN in the window procedure and present a dialog box saying the dog name will be changed...i can't change dogname from the procedure because its out of scope but I CAN change it in WinMain ... however, WinMain some how has no idea that this event occured, only the window procedure knows......unless you send a special message but if i'm going to be sending messages, shouldn't I just trap wm_keydown in the program loop as opposed to the procedure?
closed account (3pj6b7Xj)
This is the reason why I wrote a template class that took a type and stored it in a variable. I called it the LockAfter<> template class...basically you could do this...

LockAfter<HDC> g_WinDC;

Then you assign the value to g_WinDC...

g_WinDC = GetDC(hWnd);

The interesting part is that once g_WinDC is initialized, it can no longer be changed and is locked. That is why I called it LockAfter, because you lock the variable from being modified after you assign a value to it.

For regular global constants like const int flags = 10; it is not needed but for handles like HWND & HDC which can't be initialized before WinMain LockAfter protects the variable from modification by locking it once its initialized......that way if anything tries to modify from anywhere else, it alerts you MessageBox(NULL,"Attempt to modify locked variable!","LockAfter",0);

I still hate globals tho and still interested in your information! The LockAfter template refreshed my memory of operator overloading and use of templates which I rarely use.
Last edited on
Are you familiar with these Window Api Functions...

SetWindowLong()
GetWindowLong()
GetProp()
SetProp()
GlobalAlloc()
GlobalFree()
HeapAlloc()
HeapFree()

The top two are used in conjunction with storing bytes as part of the Window Class Structure itself which you initialize in WinMain(). The .cbWndExtra bytes member allows you to allocate extra bytes in multiples of four bytes which can hold pointers or data itself (pointers to data are a better idea). However, to make use of this facility you must be very good with pointers. And I mean very good.

That is how object oriented programming is done in Win32. That is how data is 'persisted' across invocations of the Window Procedure. You must not have hardly looked at any of the code I posted for you or you would have clearly seen this, as it is a staple of everything I do. The whole purpose of that program I posted right before Christmas was to show how that was done. If you recall, there was a window which displayed mouse coordinates as you moved the mouse, characters as you typed, mouse button presses as you pressed buttons, etc. Now, all those events, i.e., mouse movements, keypresses, button clicks, etc., were picked up in seperate invocations of the window procedure. How do you think they were displayed which they were during a WM_PAINT message - which is a seperate invocation of the window procedure????????? Note I didn't call GetDC to draw onto the window as you appear to be doing (another wrong thing you are doing, I might add as an aside). The data was persisted across invocations of the window procedure by storing the data not on the stack (can't do that - it goes out of scope), but in dynamic heap memory addressable using pointers. Memory was allocated during the WM_CREATE message, used throughout the program, and released at program termination in WM_CLOSE.

Here's a scroll program. There are comments in the code and MessageBoxes explaining it.

//There Are No Global Variables In This Program!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//WinTypes.h
#ifndef WINTYPES_H
#define WINTYPES_H

typedef struct    WindowsEventArguments               
{
 HWND             hWnd;                               
 WPARAM           wParam;                             
 LPARAM           lParam;                             
 HINSTANCE        hIns;                               
}WndEventArgs,    *lpWndEventArgs;


struct EVENTHANDLER
{
 unsigned int    Code;
 long            (*fnPtr)(lpWndEventArgs);
};

#endif  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//Main.cpp
#include      <windows.h>
#include      <tchar.h>
#include      <stdio.h>
#include      <string.h>
#include      "WinTypes.h"
EVENTHANDLER  EventHandler[5];


long fnWndProc_OnCreate(lpWndEventArgs Wea)     //Offset   What's Stored There
{                                               //================================
 unsigned int iLineCount=50,i;                  //0  -  3  iLineCount
 TCHAR szBuffer[80], szNum[16];                 //4  -  7  ptrPtrBuffer
 TCHAR** ptrPtrBuffer=NULL;                     //8  -  11 cyChar
 TEXTMETRIC tm;
 HDC hDC;

 Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;   //Memory will be allocated here for 50 character
 SetWindowLong(Wea->hWnd,0,iLineCount);                //strings, and will be de-allocated in
 MessageBox                                            //fnWndProc_OnClose().  This data will be stored
 (                                                     //in pointers until program close.  This is why
  Wea->hWnd,                                           //you were forced to learn pointers!
  _T("  Am Now In fnWndProc_OnCreate(), And Am Allocating A Buffer Of TCHAR**\r\n \
  Pointers To Hold 50 Character String Pointers.  A Pointer To The\r\n \
  Pointers Will Be Stored In The Window Class Structure."),
  _T("In fnWndProc_OnCreate()"),                       //Storing the pointers in a buffer whose address
  MB_OK                                                //is accessable only through the hWnd of the window
 );                                                    //tightly binds the data to the window in the same
 ptrPtrBuffer=(TCHAR**)GlobalAlloc(GPTR,sizeof(TCHAR*)*iLineCount);
 SetWindowLong(Wea->hWnd,4,(long)ptrPtrBuffer);
 for(i=0;i<iLineCount;i++)                             //manner as private member variables of a class are
 {                                                     //bound to the class.
     _tcscpy(szBuffer,_T("  "));
     _stprintf(szNum,_T("%u"),i);
     _tcscat(szBuffer,szNum);
     _tcscat(szBuffer,_T("  This Is Line #"));
     _tcscat(szBuffer,szNum);
     ptrPtrBuffer[i]=(TCHAR*)GlobalAlloc(GPTR,sizeof(TCHAR)*(_tcslen(szBuffer)+1));
     _tcscpy(ptrPtrBuffer[i],szBuffer);
 }
 hDC=GetDC(Wea->hWnd);
 GetTextMetrics(hDC,&tm);
 SetWindowLong(Wea->hWnd,8,(long)tm.tmHeight);
 ReleaseDC(Wea->hWnd,hDC);

 return 0;
}


long fnWndProc_OnSize(lpWndEventArgs Wea)
{
 int iLinesVisible,iLineCount;
 unsigned int cyChar;
 SCROLLINFO si;

 ZeroMemory(&si, sizeof(SCROLLINFO));
 iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);
 cyChar=(unsigned int)GetWindowLong(Wea->hWnd,8);
 iLinesVisible=HIWORD(Wea->lParam)/cyChar;
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask =   SIF_RANGE|SIF_PAGE;
 si.nMin = 0;
 si.nMax = iLineCount-1;
 si.nPage=iLinesVisible;
 if(si.nMax<0)
    si.nMax=0;
 SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
 if(iLinesVisible<=iLineCount)
    InvalidateRect(Wea->hWnd,NULL,TRUE);

 return 0;
}


long fnWndProc_OnVScroll(lpWndEventArgs Wea)
{
 int iVScrollPos;
 SCROLLINFO si;

 ZeroMemory(&si, sizeof(SCROLLINFO));
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask=SIF_ALL;
 GetScrollInfo(Wea->hWnd,SB_VERT,&si);
 iVScrollPos=si.nPos;
 switch(LOWORD(Wea->wParam))
 {
  case SB_LINEUP:
    if(si.nPos>si.nMin)
       si.nPos--;
    break;
  case SB_PAGEUP:
    si.nPos = si.nPos - si.nPage;
    break;
  case SB_LINEDOWN:
    if(si.nPos<si.nMax)
       si.nPos++;
    break;
  case SB_PAGEDOWN:
    si.nPos = si.nPos + si.nPage;
    break;
  case SB_THUMBTRACK:
    si.nPos=si.nTrackPos;
    break;
 }
 si.fMask = SIF_POS;                           // Set the position and then retrieve it.  Due to adjustments
 SetScrollInfo(Wea->hWnd, SB_VERT, &si, TRUE); // by Windows it may not be the same as the value set.
 GetScrollInfo (Wea->hWnd, SB_VERT, &si);
 if(si.nPos != iVScrollPos)                    // If the position has changed, scroll the window and update it
    ScrollWindow(Wea->hWnd, 0,GetWindowLong(Wea->hWnd,8)*(iVScrollPos-si.nPos), NULL, NULL);

 return 0;
}


long fnWndProc_OnPaint(lpWndEventArgs Wea)
{
 unsigned int iLineCount,i,iStart,iFinish,iLine,iPos;
 TCHAR** ptrPtrBuffer=NULL;
 PAINTSTRUCT ps;
 SCROLLINFO si;
 long cyChar;
 HDC hDC;

 hDC=BeginPaint(Wea->hWnd,&ps);
 iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);
 ptrPtrBuffer=(TCHAR**)GetWindowLong(Wea->hWnd,4);
 cyChar=GetWindowLong(Wea->hWnd,8);
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask  = SIF_POS;
 GetScrollInfo(Wea->hWnd, SB_VERT, &si);
 iPos=si.nPos;
 iStart=ps.rcPaint.top/cyChar;
 iFinish=ps.rcPaint.bottom/cyChar;
 for(i=iStart;i<=iFinish;i++)
 {
     iLine=iPos+i;
     if(iLine<iLineCount)
        TextOut(hDC,0,i*cyChar,ptrPtrBuffer[iLine],_tcslen(ptrPtrBuffer[iLine]));
 }
 EndPaint(Wea->hWnd,&ps);

 return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
 unsigned int iLineCount;
 TCHAR** ptrPtrBuffer=NULL;
 unsigned int i;

 iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);
 ptrPtrBuffer=(TCHAR**)GetWindowLong(Wea->hWnd,4);
 for(i=0;i<iLineCount;i++)
     GlobalFree(ptrPtrBuffer[i]);
 GlobalFree(ptrPtrBuffer);
 MessageBox
 (
  Wea->hWnd,
  _T("Am Now In fnWndProc_OnClose(), And Am De-Allocating Memory"),
  _T("In fnWndProc_OnClose()"),
  MB_OK
 );
 DestroyWindow(Wea->hWnd);
 PostQuitMessage(WM_QUIT);

 return 0;
}


void AttachEventHandlers(void)
{
 EventHandler[0].Code=WM_CREATE,    EventHandler[0].fnPtr=fnWndProc_OnCreate;
 EventHandler[1].Code=WM_PAINT,     EventHandler[1].fnPtr=fnWndProc_OnPaint;
 EventHandler[2].Code=WM_SIZE,      EventHandler[2].fnPtr=fnWndProc_OnSize;
 EventHandler[3].Code=WM_VSCROLL,   EventHandler[3].fnPtr=fnWndProc_OnVScroll;
 EventHandler[4].Code=WM_CLOSE,     EventHandler[4].fnPtr=fnWndProc_OnClose;
}

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0;i<5;i++)
 {
     if(EventHandler[i].Code==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

 return (DefWindowProc(hwnd,msg,wParam,lParam));
}


int WINAPI WinMain(HINSTANCE hIns,HINSTANCE hPrevIns,LPSTR lpszArgument,int iShow)
{
 TCHAR szClassName[]=TEXT("ScrollWindow");              //In WinMain the Message Handlers are
 WNDCLASSEX wc;                                         //attached, a WNDCLASSEX structure
 MSG messages;                                          //is filled out and registered with
 HWND hWnd;                                             //Windows, A CreateWindow() call made
                                                        //to create the main window, and the
 AttachEventHandlers();                                 //program's main message loop entered.
 wc.lpszClassName=szClassName;                          wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=CS_DBLCLKS;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);            wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=12;
 wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindow(szClassName,szClassName,WS_OVERLAPPEDWINDOW|WS_VSCROLL,200,100,300,228,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 UpdateWindow(hWnd);
 while(GetMessage(&messages,NULL,0,0))
 {
       TranslateMessage(&messages);
       DispatchMessage(&messages);
 }

 return messages.wParam;
}
You do have to run the program and study it.

Also realize that WinMain() is running in a message retrieval loop all the time your program is running, and you need to learn the use of the very last parameter of the CreateWindow() call, which is a pointer parameter. Look it up in the docs. With that last very important parameter you can pass data into your WM_CREATE handler. In fact, after a CreateWindow() call data can be passed back to the point of call through that last parameter. And none of this data transfer requires global variables. I've written massive applications with tens of thousands of lines of code and countless windows with no global variables.

Also, its very likely whatever you are doing with handles to device contexts obtained with GetDC() is wrong!
closed account (3pj6b7Xj)
Thanks freddie1, I will run this and study it for as long as I can.
closed account (3pj6b7Xj)
You are correct, I am doing so many wrong things...this is my code for the main on my Squibbles game, brase yourself.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*	Squibbles.cpp ; 23/12/10 14:34
	Copyright (C) 2010 Rafael Perez-Santana

	This is the main entry for the Squibbles game.
*/

#include "CreateApp.h"
#include "TextHdc.h"

/* Game related headers. */
#include "Snake.h"
#include "Snakefood.h"
#include "GameData.h"
#include "LevelData.h"
#include "MenuSystem.h"

#define TOMMY 	0
#define SAMMY	1

/* Namespaces to use. */
using namespace nsCtApp;
using namespace nsTextHdc;

/* Declare the window procedure. */
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

/* Game functions. */
string GameMenu();

/* The following items are global to this application. They were created global
 so that they can be access from within any part in the program. The g_ denotes
 that it is a global variable or object. */
HWND		g_Hwnd;			// Application handle
HDC			g_WinDC;		// Window device-context
GameData  	g_GameData;		// Game information
LevelData  	g_LevelData;	// Level information
TextHdc 	g_HdcOut;		// TextOut utility
Snake 		g_Tommy;		// Snake Tommy
Snake 		g_Sammy;		// Snake Sammy
bool		g_bQuitGame;	// Quits the game.
bool		g_bQuitToMenu;	// Quits to menu.

#define BUTTON_ABOUT	9001

int WINAPI WinMain /* This application starts here. */
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szArgs, int iCmdShow)
{
	/* Create the game application window. */
	CreateApp Window(hInstance,WndProc); // Create window & point to procedure.
	Window.Width(1024); // Width of the window.
	Window.Height(768); // Height of the window.
	Window.Style(WS_OVERLAPPEDWINDOW xMINMAX xRESIZE); // x = what we don't want.
	Window.Center(); // Center the application window on the desktop.
	Window.Title("Squibbles"); // Window title bar text.
	g_Hwnd = Window.Present(); // Show window & store application handle.
	g_WinDC = Window.DC(); // Store window device-context.

	/* Create brushes to use with Squibbles. */
	HBRUSH hbBkColor = CreateSolidBrush(RGB(0,128,192));
	HBRUSH hbTopBox = CreateSolidBrush(RGB(230,230,230));
	HBRUSH hbBottomBox = CreateSolidBrush(RGB(198,198,198));

	HWND AboutButton = CreateWindow /* Create the about button, displays copyright info. */
	("BUTTON","About",WS_CHILD|BS_PUSHBUTTON,880,2,100,26,g_Hwnd,(HMENU)BUTTON_ABOUT,hInstance,NULL);

	/* Enter the master game loop. The loop causes the main menu to become
	 active. This allows the player to choose an option before playing the
	 actual game. *** Client width: 1018 height: 740 */

	g_bQuitGame = false; // To quit master game loop.

	while (!g_bQuitGame)
	{
		g_bQuitToMenu = false; // Allows quitting to menu.
		ShowWindow(AboutButton,SW_HIDE); // Hide the about button.

		/* Player quits game from menu. */
		if (GameMenu() == "   Quit Game     ") break;

		g_HdcOut.WideBox(30,1018,680,hbBkColor); // Paint the playing field.
		g_LevelData.EnableInfo(g_GameData.GetGameLevel()); // Initialize level information.

		if (!g_GameData.StartOver()) /* If not starting over. */
		{
			/* Prepare the playing field. */
			g_HdcOut.DefaultFont();
			g_HdcOut.WideBox(0,1018,30,hbTopBox);
			g_HdcOut.WideBox(710,1018,30,hbBottomBox);
			g_HdcOut.Txt(10,717,"Enter pauses the game, Esc quits to menu.",RGB(0,0,0));

			// Tommy snake color identification.
			HRGN hrTommy = CreateRoundRectRgn(93,4,183,26,5,5);
			FillRgn(Window.DC(),hrTommy,CreateSolidBrush(RGB(255,255,0)));
			DeleteObject(hrTommy);

			// Sammy snake color identification.
			HRGN hrSammy = CreateRoundRectRgn(388,4,478,26,5,5);
			FillRgn(Window.DC(),hrSammy,CreateSolidBrush(RGB(255,255,255)));
			DeleteObject(hrSammy);

			// Display relevant game information.
			string szStrA("Level:     ");
			string szStrB("Tommy  >>> Lives:    Score:          ");
			string szStrC("Sammy  >>> Lives:    Score:          ");
			string szStrD("Squares-Left:");

			g_HdcOut.MakeTransparent();
			g_HdcOut.Txt(10,7,szStrA+szStrB+szStrC+szStrD,RGB(0,0,0));
			g_HdcOut.Txt(320,7,"000000",RGB(198,198,198));
		}

		ShowWindow(AboutButton,SW_SHOW); // Display the about button.

		// Display current game level.
		g_HdcOut.Txt(65,7,iToStr(g_GameData.GetGameLevel()), RGB(255,0,0));
		g_HdcOut.Txt(245,7,iToStr(g_GameData.GetPlayerLives(0)), RGB(255,0,0));

		/* Display the food goal --- Squares-Left. */
		string szFoodGoal(iToStr(g_LevelData.GetFoodGoal())+" ");
		g_HdcOut.Txt(800,7,szFoodGoal,RGB(255,0,0));

		/* Present game level in client area. */
		g_LevelData.Present(g_GameData.GetGameLevel());

		/* Initialize the snakes; Tommy & Sammy */
		g_Tommy.Init(g_LevelData.SnakeX(TOMMY), // Horizontal starting point.
			g_LevelData.SnakeY(TOMMY), // Vertical starting point.
			g_LevelData.SnakeDir(TOMMY), // Starting direction of travel.
			g_GameData.GetSnakeClr(TOMMY)); // Color of the snake.
		g_Sammy.Init(g_LevelData.SnakeX(SAMMY), // Horizontal starting point.
			g_LevelData.SnakeY(SAMMY), // Vertical starting point.
			g_LevelData.SnakeDir(SAMMY), // Starting direction of travel.
			g_GameData.GetSnakeClr(SAMMY)); // Color of the snake.

		/* Snake skill levels */
		if (g_GameData.GameDiff() == 0) { g_Tommy.EasySnake(); g_Sammy.EasySnake(); }
		if (g_GameData.GameDiff() == 1) { g_Tommy.NormalSnake(); g_Sammy.NormalSnake(); }
		if (g_GameData.GameDiff() == 2) { g_Tommy.HardSnake(); g_Sammy.HardSnake(); }
		if (g_GameData.GameDiff() == 3) { g_Tommy.ExpertSnake(); g_Sammy.ExpertSnake(); }

		/* Prepare the first food item on the screen. */
		Snakefood FoodItem; // Create Snake food object.
		FoodItem.Create(15); // Create a snake food item.

		/* Reset the snakes before entering loop! */
		g_Tommy.Reset(); g_Sammy.Reset(); // Reset snakes.
		g_Tommy.ModSnakeStatus("alive"); // Make sure its alive.
		g_Sammy.ModSnakeStatus("alive"); // Make sure its alive.

		/* If in single player mode, Sammy goes to sleep. */
		if (g_GameData.NumOfPlayers() == 1) g_Sammy.ModSnakeStatus("zZzZzZz");
closed account (3pj6b7Xj)
Part 2...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

		while (true) // Main game loop.
		{
			if (g_bQuitToMenu) break; // Quit to menu.

			if (!Window.PumpMessage(PEEKMSG)) // Run the message pump
			{	/* If the window has been commanded to close, confirm. */
				int iMB = MessageBox(g_Hwnd,"Do you really want to quit playing Squibbles?",
					"Squibbles", MB_YESNO | MB_ICONINFORMATION);
				if (iMB == IDYES) { g_bQuitGame = true; break; } /* Quit loop if yes. */
			}

			if (!g_LevelData.GetFoodGoal())
			{	/* If goal reaches zero, no more food. Advance to next level! */
				g_GameData.NextLevel(); /* Advances to next level! */
				g_GameData.StartOver(true); /* Starting over on new level. */
				MessageBox(g_Hwnd,"Congratulations! You advance to the next level!",
					"Squibbles", MB_ICONINFORMATION);
				break; /* Break out of loop. */
			}

			/* Here all information & events pertaining to the created snake(s) is
			 checked and handled. Notice that both Snakes are checked even when in
			 one player mode. In one player mode, Sammy simply goes to sleep. */

			if(g_Tommy.Update())
			{	// If snake returns true, something has found something.
				if (FoodItem.IsFood(g_Tommy.GetCtPixel())) // Is it a food item?
				{	// If so, erase the food item from client area.
					FoodItem.Erase(g_Tommy.GetCtPixel());
					g_LevelData.SubFoodGoal(); // Reduce food count by one
					g_Tommy.ModSnakeStatus("alive"); // Keep the snake alive.
					g_Tommy.Grow(); // Command the snake to grow.
					g_GameData.UpdateScore(TOMMY,100); // Award one-hundred points!
					/* update player score & new food goal. */
					string szSnakeScore(iToStr(g_GameData.GetPlyrScore(TOMMY)));
					g_HdcOut.Txt(320,7,szSnakeScore,RGB(255,0,0));
					string szFoodGoal(iToStr(g_LevelData.GetFoodGoal()));
					szFoodGoal = szFoodGoal + " "; // Add white space at end.
					g_HdcOut.Txt(800,7,szFoodGoal,RGB(255,0,0));
					continue; // Keep the game going.
				}

				if (!g_GameData.GetPlayerLives(TOMMY))
				{	/* Tommy has no more lives, the game is over, inform the player.
					 Reset level, scores & lives and break out of loop to re-enter menu. */
					MessageBox(g_Hwnd,"Tommy has failed! Better luck next time.",
						"Squibbles",MB_ICONINFORMATION);
					g_GameData.StartOver(false); // Not starting over.
					g_GameData.ResetLevel();; // Reset game level.
					g_GameData.ResetScores(); // Reset all player scores.
					g_GameData.ResetLives(); // Reset player lives.
					break; // Quit the loop.
				}

				/* If the snake has collided, inform the player. Take away 1 life point.
				 break out of loop to start game over. */
				MessageBox(g_Hwnd,"Tommy crashed! Try again.","Squibbles",MB_ICONEXCLAMATION);
				g_GameData.SubtractLives(TOMMY); // Take away 1 life. =/ lol
				g_HdcOut.Txt(245,7,iToStr(g_GameData.GetPlayerLives(TOMMY)), RGB(255,0,0));
				g_GameData.StartOver(true); // Starting over.
				break; // Quit the loop.
			}

			if(g_Sammy.Update())
			{	// If snake returns true, something has found something.
				if (FoodItem.IsFood(g_Sammy.GetCtPixel())) // Is it a food item?
				{	// If so, erase the food item from client area.
					FoodItem.Erase(g_Sammy.GetCtPixel());
					g_LevelData.SubFoodGoal(); // Reduce food count by one
					g_Sammy.ModSnakeStatus("alive"); // Keep the snake alive.
					g_Sammy.Grow(); // Command the snake to grow.
					g_GameData.UpdateScore(SAMMY,100); // Award one-hundred points!
					/* update player score & new food goal. */
					string szSnakeScore(iToStr(g_GameData.GetPlyrScore(SAMMY)));
					g_HdcOut.Txt(320,7,szSnakeScore,RGB(255,0,0));
					string szFoodGoal(iToStr(g_LevelData.GetFoodGoal()));
					szFoodGoal = szFoodGoal + " "; // Add white space at end.
					g_HdcOut.Txt(800,7,szFoodGoal,RGB(255,0,0));
					continue; // Keep the game going.
				}

				if (!g_GameData.GetPlayerLives(SAMMY))
				{	/* Sammy has no more lives, the game is over, inform the player.
					 Reset level, scores & lives and break out of loop to re-enter menu. */
					MessageBox(g_Hwnd,"Sammy has failed! Better luck next time.",
						"Squibbles",MB_ICONINFORMATION);
					g_GameData.StartOver(false); // Not starting over.
					g_GameData.ResetLevel();; // Reset game level.
					g_GameData.ResetScores(); // Reset all player scores.
					g_GameData.ResetLives(); // Reset player lives.
					break; // Quit the loop.
				}

				/* If the snake has collided, inform the player. Take away 1 life point.
				 break out of loop to start game over. */
				MessageBox(g_Hwnd,"Sammy crashed! Try again.","Squibbles",MB_ICONEXCLAMATION);
				g_GameData.SubtractLives(SAMMY); // Take away 1 life. =/ lol
				g_HdcOut.Txt(245,7,iToStr(g_GameData.GetPlayerLives(SAMMY)), RGB(255,0,0));
				g_GameData.StartOver(true); // Starting over.
				break; // Quit the loop.
			}

			/* If there is no food, create it. */
			if(!FoodItem.OnScreen()) FoodItem.Create(15);
			continue; // Keep the game going.

   		} /* End main game loop. */

		if (g_bQuitGame) break; /* Player ended game. */

	} /* End master game loop. */

	// Delete snakes.
	g_Tommy.Delete();
	g_Sammy.Delete();

	/* Delete created brushes. */
	DeleteObject(hbBkColor); // Arena background.
	DeleteObject(hbTopBox); // Box at top.
	DeleteObject(hbBottomBox); // Box at bottom.

    return 0; // End game.
}
closed account (3pj6b7Xj)
oh boy...part 3...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
string GameMenu()
/* This function displays the main menu of the game. The MenuSystem object
 created here returns the selection chosen by the user. */
{
	/* If the player has crashed the snake and is trying again, we do not need to
	 show the menu but just re-enter the master game loop. */
	if (g_GameData.StartOver()) return "_dummy_";

	/* Clean up the screen for drawing the menu. */
	g_HdcOut.WideBox(0,1018,710,CreateSolidBrush(RGB(0,128,192)));

	/* Draw yellow rectangle with shadow for menu. */
	HRGN hrShadowRect = CreateRoundRectRgn(310,170,710,570,15,15);
	FillRgn(g_WinDC,hrShadowRect,CreateSolidBrush(RGB(80,80,80)));
	DeleteObject(hrShadowRect);
	HRGN hrRoundRect = CreateRoundRectRgn(300,160,700,560,15,15);
	FillRgn(g_WinDC,hrRoundRect,CreateSolidBrush(RGB(255,255,0)));
	DeleteObject(hrRoundRect);

	/* Create & define a rectangle structure. */
	RECT rect; SetRect(&rect,10,250,130,700);

	/* Paint the main Squibbles game title! */
	g_HdcOut.Font("Courier New",50);
	g_HdcOut.MakeBold(); // quickly make this font bold
	g_HdcOut.Txt(315,30,"SQUIBBLES",RGB(0,0,0));
	g_HdcOut.MakeTransparent(); // next font will have transparent background
	g_HdcOut.MakeBold(); // quickly make this font bold
	g_HdcOut.Txt(315,32,"SQUIBBLES",RGB(255,255,255));
	g_HdcOut.Font("Courier",30);
	g_HdcOut.Txt(675,65,"®",RGB(255,255,0)); // just for fancyness!

	/* Create the main menu for the Squibbles game. */
	MenuSystem Main;
		// Fill the menu with selections...
		Main.AddItem(312,190,"   Start Game    ","Lucida Console",28);
		Main.AddItem(312,240,"   Player Mode   ","Lucida Console",28);
		Main.AddItem(312,340,"   Skill Level   ","Lucida Console",28);
		Main.AddItem(312,440,"   How To Play   ","Lucida Console",28);
		Main.AddItem(312,490,"   Quit Game     ","Lucida Console",28);
		// Initialize menu display parameters.
		Main.BkColor(RGB(255,255,0)); // Background.
		Main.TxtColor(RGB(0,0,0)); // Text color.
		Main.SltBkColor(RGB(255,0,0)); // Selection text background.
		Main.SltTxtColor(RGB(230,230,230)); // Selection text color.
		Main.InitMenu(); // Initialize menu for operation.
		Main.Present(); // Display menu to screen.

	/* Copyright information & developer. */
	g_HdcOut.Font("Fixedsys",10);
	g_HdcOut.WideBox(710,1018,30,CreateSolidBrush(RGB(192,192,192))); // draws the box
	g_HdcOut.Txt(10,717,":: Copyright (C) 2011 Rafael Perez-Santana",RGB(0,0,0));

	string MenuSlt; // To store returned selection.
	bool blQuit = false; // For quitting the loop.

	while(!blQuit)
	{
		/* Display the play mode and difficulty. */
		g_HdcOut.Font("Verdana",20);
		g_HdcOut.Txt(400,290,g_GameData.GetPlayMode(),RGB(128,128,128)); // play mode
		g_HdcOut.Txt(415,390,g_GameData.GameDiffStr(),RGB(128,128,128)); // skill level

		MenuSlt = Main.Control(); // Give control to the menu.

		// If the window was commanded to close, quit the game. */
		if (MenuSlt == "MenuSys::Close") return "   Quit Game     ";

		if (MenuSlt == "   Player Mode   ")
		{	// Cycle to; two player if single player.
			if (g_GameData.GetPlayMode() == " ONE PLAYER ")
			{ g_GameData.SetPlayMode(" TWO PLAYER "); continue; }
			// Cycle to; single player if two player.
			if (g_GameData.GetPlayMode() == " TWO PLAYER ")
			{ g_GameData.SetPlayMode(" ONE PLAYER "); continue; }
		}

		/* Game difficulty or skill level. */
		if (MenuSlt == "   Skill Level   ")
		{ g_GameData.NextGameDiff(); continue; }

		/* How to play message box. */
		if (MenuSlt == "   How To Play   ")
		{
			MessageBox(g_Hwnd,
				"The squibbles game is very simple. Maneuver your snake with the arrow keys "
				"while eating the colorful squares that appear in random locations on the "
				"screen. The snake will grow the more you eat and it will become faster!\n\n"
				"If your in one player mode, your snake is Tommy and is yellow in color. If "
				"your in two player mode, the second player controls Sammy, the white "
				"snake.\n\n"
				"To control Sammy the white snake, use the WASD keys. W is up, S is down, "
				"A is left and D is right, MAKE SURE CAPSLOCK is off! To control Tommy the yellow "
				"snake, use the arrow keys like I just pointed out in the first paragraph.\n\n"
				"Do not hit walls or edges of the screen or your snake will die! You only get "
				"five lives to attempt to beat the game, there are a total of seven levels and "
				"they become difficult as you progress through the game.\n\n"
				"I hope you like playing squibbles :) I spent four-months making this game, so "
				"if you want to show any kind of support, send me an e-mail, I would love to hear "
				"from you!\n\n"
				"Happy Squibbling!\n\n"
				"mr.rafael.ps@gmail.com",
				"Squibbles --- How to Play",
				MB_OK|MB_ICONINFORMATION);
				continue; // Stay in menu.
		}

		blQuit = true; // Quit the loop.
	}

	return MenuSlt.c_str(); // Return menu selection.
}

////////////////////////////WINDOW PROCEDURE//////////////////////////////////
LRESULT CALLBACK WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
/* This is the window procedure for the Squibbles game. What product can you
 recommend to grow back the hairs I pulled out coding this? It seems that external
 variables were the only solution. */
{
	switch(Message)
	{
		case WM_COMMAND:

			if (LOWORD(wParam) == BUTTON_ABOUT)
				/* Game developer information. */
				MessageBox(g_Hwnd,
				"Squibbles 1.0\n"
				"Copyright (C) 2011 Rafael Perez-Santana\n"
				"All Rights Reserved.\n"
				"Developed with Code::Blocks\n",
				"Squibbles --- About",
				MB_OK|MB_ICONINFORMATION);

			break;

		case WM_CHAR:

			/* Key controls for Sammy. */
			if ((int)wParam == g_GameData.GetKeyLeft(SAMMY)) g_Sammy.MoveLeft();
			if ((int)wParam == g_GameData.GetKeyUp(SAMMY)) g_Sammy.MoveUp();
			if ((int)wParam == g_GameData.GetKeyRight(SAMMY)) g_Sammy.MoveRight();
			if ((int)wParam == g_GameData.GetKeyDown(SAMMY)) g_Sammy.MoveDown();

			break;

		case WM_KEYDOWN:

			/* Key controls for Tommy. */
			if ((int)wParam == g_GameData.GetKeyLeft(TOMMY)) g_Tommy.MoveLeft();
			if ((int)wParam == g_GameData.GetKeyUp(TOMMY)) g_Tommy.MoveUp();
			if ((int)wParam == g_GameData.GetKeyRight(TOMMY)) g_Tommy.MoveRight();
			if ((int)wParam == g_GameData.GetKeyDown(TOMMY)) g_Tommy.MoveDown();

			if (wParam == 27)  // Escape key.
			{	/* Player wants to quit to menu. Alert the player that all game
				 progress will be lost. */
				LPCSTR szConfirm = "Progress will be lost if you return to menu! You sure?";
				int iMB = MessageBox(hWnd,szConfirm,"Squibbles", MB_YESNO | MB_ICONINFORMATION);
				if (iMB == IDNO) return 0; /* Abort and continue. */
				/* Clear all game progress and return to menu. */
				g_GameData.StartOver(false); // Not starting over.
				g_GameData.ResetLevel(); // Reset game level.
				g_GameData.ResetScores(); // Reset all player scores.
				g_GameData.ResetLives(); // Reset player lives.
				g_bQuitToMenu = true; // Stop game and return to menu.
			}

			if (wParam == 13) // Enter key.
			{	// Player wants to pause the game.
				MessageBox(hWnd,"The game is paused! Click OK to continue.",
					"Squibbles",MB_ICONINFORMATION);
				return 0;
			}

			break;

		case WM_SYSCOMMAND:
			/* If window is commanded to close, post a WM_QUIT message and veto
			 the quit! */
			if ((wParam & 0xFFF0) == SC_CLOSE) { PostQuitMessage(0); return 0; }

		default: /* For messages that are not handled. */
			return DefWindowProc(hWnd,Message,wParam,lParam);
	}
	return 0;
}
closed account (3pj6b7Xj)
dont even attempt to compile that mess, you'll need createapp.h, leveldata.h, gamedata.h, texthdc.h, menusystem.h and god only knows what else. This version does not use that LockAfter template and my current game loop is not while (true) i use a bQuitToMenu bool for that but its not implemented in this version.

But clearly, my code is massive and unreliable for such a simple game. I wont degrade myself as the windows api is all new to me and i'm forced to use programming techniques i've never tried before.

Look at the code, EVERYONE look at the code, all your input is very much appreciated, this is the heart of my snake game and once its complete, I will tahnk you all, I should throw that intot he about box...for now read, critic, point out what im doing wrong, were I can improive, etc...I will be busy studying freddie1's code until I can write something of my own that is based on the idea, then...I will return to create Squibbles II I have to finish this mess as it is but i have learned a lot from it...now iits time for some feedback!

Thats it, comparing my style of programming to yours (freddie1), I don't stand a chance, I'm going to have to sit down and learn from it and possibly re-write the entire code in Squibbles.cpp, probably alsoo do away with some programming habits and throw some implementations in the trash, i've got a lot to learn, I will release Squibbles as it is for now, a re-write of all the code will cause a catastrophy right now.

pardon the typos and bad grammer its 3:49 am, I had awaken for a snack and decided to turn the lap top on and see whats new, good night guys, going back to bed here...
Last edited on
Yes, I know you are learning mrfaosfx. Sorry if I sounded too harsh. Globals are one of my 'hot buttons', so I guess that set me off. If it helps, I used globals for I'd guess my 1st five years or so of Windows programming, until I finally 'saw the light'.

I may not have much time this weekend to look at your code, but I'll try next week. Please leave it posted.

I've re-written many programs from scratch myself. The way it works of course is you finally learn all the weak spots and problems of your code while doing it, then eventually start from scratch again after finally painfully finishing one somewhat poor version. Each version gets better though!

The code I post around here is somewhat hard to follow I imagine, because I don't use the standard switch construct to parse Windows messages dispatched to the Window Procedure. I use a function pointer setup and message cracker scheme I learned and modified for my use from Doug Boling who writes Windows CE books for Microsoft. He claims he learned the technique from another famous and advanced programmer named Ray Duncan.

If you are interested on that I have a tutorial on how it works here....

http://www.jose.it-berater.org/smfforum/index.php?topic=3391.0

Perhaps in the future when I post working programs I'll modify the programs to use the standfard switch construct so its easier to follow.

The other thing is, I fear a lot of folks learning C++ don't study function pointers. I feel they are important though, and are the basis for many advanced techniques.
Several weeks ago after I posted some long winded explanations and a fair amount of code ComputerGeek01 mentioned that I might post a tutorial here. I think I'll do that. Something to augument Petzold and the Forger's Win Api tutorial. I believe this program below will be part of it when I get it done.

Anyway, here is an easier program for you to figure out when you finally get your game working as good as you can such as it is. The scroll program I posted above is something I was working 'hot and heavy' on this week, so I posted it but scrolling is inherently complex and not a good place perhaps to attack the global issue. Here, however, is a very short simple Windows program that should succinctly as possible show how globals are eliminated by using some of the functions I previously described. Also, there are lots of comments in the code, and it uses the standard switch Window Procedure construct instead of my tricky message cracker routine. The program just creates a three dimensional box out of a class named CBox with length, width, and height members, and of course there is a CBox::Volume() member function. Can't get much simpler than that. However, the program shows the proper way to persist private data across invovations of the Window Procedure, and the proper way to draw to the Window using BeginPaint() and EndPaint() during a WM_PAINT handler with no improper GetDC() calls involved.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef CBox_h
#define CBox_h

class CBox
{
 public:
 CBox(double,double,double);  //Constructor
 ~CBox();                     //Destructor
 double GetLength();          //m_Length accessor
 double GetWidth();           //m_Width accessor
 double GetHeight();          //m_Height accessor
 double Volume();             //Returns Volume() of Box

 private:
 double m_Length;
 double m_Width;
 double m_Height;
};
#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//CBox.cpp
#include "CBox.h"

CBox::CBox(double dblLength, double dblWidth, double dblHeight)
{
 this->m_Length=dblLength;
 this->m_Width=dblWidth;
 this->m_Height=dblHeight;
}

CBox::~CBox()
{
 //destructor
}

double CBox::GetLength()
{
 return this->m_Length;
}

double CBox::GetWidth()
{
 return this->m_Width;
}

double CBox::GetHeight()
{
 return this->m_Height;
}

double CBox::Volume()
{
 return m_Length*m_Width*m_Height;
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//Main.cpp                                   //C++ Object Oriented Program using native
#include <windows.h>                         //Windows C Api to display the dimensions
#include <stdio.h>                           //and volume of a class CBox object.  Note
#include "CBox.h"                            //that this app contains no global variables.


LRESULT CALLBACK WndProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 switch(msg)
 {
    case WM_CREATE:                            //This message is received one time as a
      {                                        //result of the CreateWindow() call in
         CBox* pBox=NULL;                      //WinMain().  The CreateWindow() call in
         pBox=new CBox(2.0,3.0,4.0);           //WinMain will not return until this case
         if(pBox)                              //handler returns.  A new CBox is created
            SetWindowLong(hWnd,0,(long)pBox);  //here on the heap.  A pointer to it is
         else                                  //stored in the instantiated window's class
            return -1;                         //structure.  This is how the CBox object
         return 0;                             //is persisted across invocations of the
      }                                        //Window Procedure without using globals
    case WM_PAINT:
      {
         CBox* pBox=NULL;             //The Window Procedure is called with the msg parameter
         HFONT hFont,hTmp;            //of WM_PAINT any time any part of the Window's client
         PAINTSTRUCT ps;              //area becomes invalid.  This will be true right after
         HDC hDC;                     //processing of the WM_CREATE handler when the window
         char szBuffer[128];          //is not yet visible.  Since this window's purpose in
         hDC=BeginPaint(hWnd,&ps);    //life is to display the specs on a CBox object, it has
         SetBkMode(hDC,TRANSPARENT);  //a pointer to one of these stored as part of its instan-
         hFont=CreateFont             //tiated Window Structure (i.e., an 'object' with members).
         (
          28,0,0,0,FW_HEAVY,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,        //The GetWindowLong()
          CLIP_DEFAULT_PRECIS,PROOF_QUALITY,DEFAULT_PITCH,"Courier New"   //call just below-left
         );                                                               //retrieves the pointer
         hTmp=(HFONT)SelectObject(hDC,hFont);                             //to the CBox object
         pBox=(CBox*)GetWindowLong(hWnd,0);                               //from the object struct
         sprintf(szBuffer,"Box.GetLength() = %6.2f",pBox->GetLength());   //and does a lot of
         TextOut(hDC,25,30,szBuffer,strlen(szBuffer));                    //fancy GDI (Graphics
         sprintf(szBuffer,"Box.GetWidth()  = %6.2f",pBox->GetWidth());    //Device Interface) stuff
         TextOut(hDC,25,60,szBuffer,strlen(szBuffer));                    //to format the CBox's
         sprintf(szBuffer,"Box.GetHeight() = %6.2f",pBox->GetHeight());   //specifications, i.e.
         TextOut(hDC,25,90,szBuffer,strlen(szBuffer));                    //dimensions and volume
         sprintf(szBuffer,"Box.Volume()    = %6.2f",pBox->Volume());      //to the screen, i.e.
         TextOut(hDC,25,120,szBuffer,strlen(szBuffer));                   //window.  All in all, its
         SelectObject(hDC,hTmp);   //one mean logical machine!  Note down in WinMain() when I filled
         DeleteObject(hFont);      //out the Window Class struct I set the wc.cbWndExtra bytes to 4.
         EndPaint(hWnd,&ps);       //It is in these four exta bytes that I'm storing the pointer to
         return 0;                 //the CBox object, whose 'duration' is the lifetime of the program.
                                   //It remains alive until the  user clicks the [x] button to close
      }                            //the app, at which point delete is called on the CBox object.
    case WM_DESTROY:               //Try to think about this whole application as a C++ object.  It
      {                            //is created an allocates memory; it hides all its internal details
         CBox* pBox=NULL;          //through hidden private members; and it cleans up after itself at
         pBox=(CBox*)GetWindowLong(hWnd,0);   //termination.
         if(pBox)
            delete pBox;
         PostQuitMessage(0);
         return 0;
      }
 }

 return (DefWindowProc(hWnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 char szClassName[]="CBox5";
 MSG messages;
 WNDCLASS wc;
 HWND hWnd;

 wc.lpszClassName=szClassName;                wc.lpfnWndProc=WndProc;
 wc.style=0,                                  wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 wc.hInstance=hIns,                           wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=4;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClass(&wc);
 hWnd=CreateWindow(szClassName,szClassName,WS_OVERLAPPEDWINDOW^WS_MAXIMIZEBOX,100,100,400,300,0,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
       TranslateMessage(&messages);
       DispatchMessage(&messages);
 }

 return messages.wParam;
}


I might mention that sometimes you just have to bear down and study other people's code. This week I got stuck real bad on a scrolling issue involving SB_THUMBTRACK notification messages and the SCROLLINFO::nPage member. I had to turn to Petzold once more and thank my lucky stars he was there! That scroll program shows the proper way to do it. I did this because I knew you would have never asked the question about globals if you had studied and understood my code.
Last edited on
closed account (3pj6b7Xj)
Thanks freddie1, your awesome, I've printed the code out and will study it. Consider me your padawan learner. Now to study these, i've also printed out the first program and compiled...my simplest windows program compiled at 484kb while yours compiled at only 10kb, I was shocked! your program did more than mine and yet the resulting executable was incrediblly small, this was amazing to see...
closed account (3pj6b7Xj)
Ok I sat down and reviewed C++ pointers which I know about but just needed a refresher course, apparently I had some concepts wrong but i've got it all back now. I then studied the SetWindowLong() & GetWindowLong() functions in detail at MSDN and see what they do.

I see that you reserved 4 bytes of memory for cbWndExtra which is were you store the pointer, since the pointer is some type of special integer that holds addresses 4 bytes strikes a hint, anything smaller could probably cause a run time error depending how you use it.

First I notice you create a NULL pointer with the Cbox object class ...... CBox * Pbox=NULL; I also see that you have to always declare the null pointer on every part of the procedure, this made sense as the temporary null pointer is destroyed...where the magic really happened was when you wrote; pBox = new CBox(2.0,3.0,4.0); I know this will allocate memory for an object of type Cbox and store the address of that object in pBox the current temporary pointer which is now no longer NULL and pointing to an object. I do knwo for a fact that if you do not store the pointer address some where, when you return, you'll have a memory leak because you'll no longer be able to access the object since you lost the address! So you store it in the window extra bytes using window long ... SetWindowLong(hwnd,0,(long)pBox); ... I know the (long)pBox casts the value of pBox into a long since that is what SetWindowLong() expects, that effectively stores the address of the object in the window extra bytes...thats is simply genius...I smiled when I figured that part out! *rubbing hands* lol

So then on WM_PAINT, I see you create the null pointer again but now you use GetWindowLong to fetch the address and put it into the null pointer... pBox=(CBox*)GetWindowLong(hWnd,0); GetWindowLong(hWnd,0) gets the contents at the window extra bytes and I see you cast that into (CBox*) syntatic sugar that is some what confusing me but I know what is going on...I should return to pointers again and maybe re-read on casting or maybe some more casting.

It all makes sense now and you did everything through the windows procedure, after all, that is what the window procedure is for anyway. The WinMain was nothing special as it was just a simple winapi skeleton of a window frame and message pump which is quite attractive actually because you can store that code and just paste it in there when ever you want to use it.

pBox = (CBox*) GetWindowLong(hWnd,0); <--- I suppose (CBox*) is telling the compiler that you want the address of a CBox object? it was stored as a long so i'm assuming you are casting it back to type CBox the class object and the * indicates you want the address and not the object itself.

Then you can use pBox->m_dblHeight = what ever .... which looks very nice.

To add, on WM_DESTROY, I see you fetch the pointer again and call Delete to de-allocate the memory that was allocated for the object, good stuff. The way I saw the windows procedure, it looks like a C++ class in a rather strange way in that it has its own constructor WM_CREATE and its destructor WM_DESTROY, I see the hidden object-oriented programming you are talking about now :)

I will continue to re-read the code and see what else I can learn.

The way I see this, it looks like a genius windows exploit lol!
Last edited on
Just wait mrfaosfx! It keeps getting better!

You've got everything exactly right! Makes me proud! Are you beginning to see the full implications of this power?

C and C++ are phobic about ALWAYS knowing exactly what pointers are pointing to. You'll have to become familiar with casting and learn to live with it. When I put the (CBox*) in front of GetWindowLong() like this...

pBox=(CBox*)GetWindowLong(Wea->hWnd,0);

all I'm doing is making it possible for a function typed as returning a long to assign it to a pBox variable which was declared as being a pointer to a CBox. Without the cast the compiler will report that pBox is a CBox pointer - not a long.

When I declare pointers I always set them to NULL. The reason for this is safty. Usually memory allocation functions that fail put a NULL in the variable, but I hate to count on that. Its usually a good idea to test a pointer before you use it; especially if you have it stored somewhere. You'll use something like this...

if(pBox)
{
//whatever you want to do with it

}
else
//code for failure

It saves you from crashes!

Pointers are really, really important, and difficult to learn. One of the things that helped me with them is my full realization that they were conceptually really difficult, and that it would take my greatest effort and fullest concentration to completely master them.
Last edited on
Pages: 12