Do I have to use global variables?!?

Pages: 12
closed account (3pj6b7Xj)
yes! By the way, did you notice that SetWindowLong() has been overtaken by SetWindowLongPtr() which, is supposed to support both 32bit and 64bit development?
You are ahead of me on the 64 bit thing mrfaoufx. I work for a large organization and as large organizations go we move kind of slow in adopting the newest and latest things because of backward compatability of mission critical apps so on and so forth. We are still on XP, SP3 32 bit, but will be moving I am told to 32 bit Windows 7 this year.

My best laptop (I have 4) has a 64 bit processor but 32 bit OS. I'm thinking of buying myself a new laptop though. I want to experiment with 64 bit OSs to see how it will affect my code, as I'm sure we'll eventually get to 64 bit OSs. As you are aware, pointers (the subject of our discussion) will be 64 bit numbers, but ints, unsigned ints, and other non pointer types will apparently retain their 32 bit meanings, which is a relief to me.

I didn't know about SetWindowLongPtr(). Thanks for telling me.

By the way, what do you think is a good laptop to buy? Have any thoughts on the matter? Anyone? I always buy desktop replacement type models. Was thinking of a Sony. Never had one of those.
closed account (3pj6b7Xj)
I do not recommend, hp, compaq, dell, acer but highly recommend toshiba laptops...these are just my views and experiences with different products ... compaq was by far the worse, HP laptops have a bad habit of failing screens, i've owned many and got tired of the coincidence and figure, it runs in their technology, dell ... they are alright but all their software is too laptop dependent, including their OSs some drivers wont even operate unless its the OEM version of the OS installed on the laptop .... toshiba I love and have had good results with, they don't bloat your laptop with things you don't need and you can pretty much put any OS on them again, i'm just a little biased here but everyone has their experiences.

Also, I have some code to show you today :) I wrote my first program using your technique, and this time I will be posting all of the implementation in case any one wants to compile it and maybe learn something from it.

here is main.cpp...

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
// Main.cpp ; 01/01/2011

/* My first program using freddie1's technique of placing a pointer in the
 window extra bytes which can be used to access objects with out using global
 variables! This program uses one pointer, my next task is to create one
 that uses a pointer to an array of pointers or an object of pointers! This would
 allow access to multiple objects within a program with out using those global
 variables! */

#include <windows.h>
#include "colorarray.h"
#include "CreateApp.h"

#define BTNEXT      9001                    // Id for Next Button.

/*  Declare Windows procedure  */
LRESULT CALLBACK WinProc(HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,char * pArgs,int iShow)
{
    CreateApp Window(hInstance,WinProc);    // Create window & point to procedure
    Window.Size(300,300);                   // Window dimensions.
    Window.Center();                        // Center window on the desktop.
    Window.ExtraBytes(4);                   // Allocate 4 extra bytes for window.
    HWND hWnd=Window.Present();             // Display the window.
    HWND hBtChange=CreateWindow             /* create button; Next (BTNEXT) */
    ("button","Next",WS_CHILD|BS_PUSHBUTTON,10,30,80,35,hWnd,(HMENU)BTNEXT,hInstance,NULL);
    ShowWindow(hBtChange,SW_SHOW);          // show the button.
    MSG Msg;                                // Messaves saved here.
    bool bQuit=false;                       // Loop quits if true.
    /* start the message pump. */
    while(!bQuit)
    {
        if(PeekMessage(&Msg,NULL,0,0,PM_REMOVE))
        {   /* if WM_QUIT is found, quit the loop. */
            if(Msg.message == WM_QUIT) { bQuit=true; continue; }
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    }

    return 0;                               // Program terminates here.
}

/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WinProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam)
{
    switch (Message) // handle program messages...
    {
        case WM_CREATE:
        {   /* this message is placed right after createwindow is done. */
            ColorArray*pColorArray=NULL; // <-- I create null pointer here
            pColorArray=new ColorArray(); // Now I create the object and store...
            if (pColorArray)SetWindowLong(hWnd,0,(long)pColorArray); // pointer in window extra bytes!
            else PostQuitMessage(0); // in case there is a fault, oh no quit!
            return 0;
        }

        case WM_PAINT:
        {   // here we handle what appears on the client window.
            ColorArray*pColorArray=NULL; // you know what this is already.
            PAINTSTRUCT ps; // structure for painting
            HDC WinDC; // handle to device-context
            WinDC=BeginPaint(hWnd,&ps); // stores dc in WinDC
            pColorArray=(ColorArray*)GetWindowLong(hWnd,0); // my favorite part, fetch the pointer.
            LPCSTR Color=pColorArray->GetColor(); // now we can call one of the member functions!
            TextOut(WinDC,10,10,Color,strlen(Color)); // output the result in the client area.
            EndPaint(hWnd,&ps); // done painting...
            return 0;
        }

        case WM_COMMAND:
        {
            if(LOWORD(wParam)==BTNEXT) // is out our Next button?
            {   // cycle to the next color.
                ColorArray*pColorArray=NULL;
                pColorArray=(ColorArray*)GetWindowLong(hWnd,0);
                pColorArray->NextColor(); // here I call the nextcolor() function.
                InvalidateRect(hWnd,NULL,false); // i needed a way to update client area.
            }

            return 0;
        }

        case WM_SYSCOMMAND:
        {
            if((wParam & 0xFFF0)==SC_CLOSE)
            {   /* If window is commanded to close, get confirmation. */
                int iMB=MessageBox(hWnd,"Are you sure you want to quit?",
                                   "Message",MB_YESNO|MB_ICONQUESTION);
                if(iMB==IDYES)
                {   // Delete object & quit program.
                    ColorArray*pColorArray=NULL;
                    pColorArray=(ColorArray*)GetWindowLong(hWnd,0);
                    if (pColorArray) delete pColorArray;
                    PostQuitMessage(0);
                    return 0;
                }

                break;
            }
        }

        default: /* for messages that we don't deal with */
            return DefWindowProc(hWnd,Message,wParam,lParam);
    }

    return 0;
}
closed account (3pj6b7Xj)
Here is the CreateApp class code...

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
/*	CreateApp.h ; 01/01/2011 4:20 PM
	Copyright (c) 2010 Rafael Perez-Santana

    This header file includes a class to create application windows. You only specify
    the hInstance and the window procedure to use with the application you create.
*/

#ifndef CREATEAPP_H_
#define CREATEAPP_H_

#include <windows.h>

class CreateApp
{
	public:
	/* Class constructor & destructors */
	CreateApp(HINSTANCE, WNDPROC);

	void Center(); // Centers the application window.
    HWND Present(); // Presents the application window.

    /* Window positioning and sizing functions. */
    void xPos(int iWindowX_) { iWindowX = iWindowX_; }
    void yPos(int iWindowY_) { iWindowY = iWindowY_; }
	void Width(int iWidth_) { iWidth = iWidth_; }
	void Height(int iHeight_) { iHeight = iHeight_; }
	void Position(int x, int y) { iWindowX = x; iWindowY = y; }
	void Size(int iWidth_, int iHeight_) { iWidth = iWidth_; iHeight = iHeight_; }

    /* Thesse modify data in the window structure. */
	void ClassName(LPCTSTR ClassName_) { WinClassEx.lpszClassName = ClassName_; }
	void ClassStyle(UINT ClassStyle_) {	WinClassEx.style = ClassStyle_; }
	void Icon(HICON Icon_) { WinClassEx.hIcon = Icon_; }
	void Iconic(HICON Iconic_) { WinClassEx.hIconSm = Iconic_; }
	void Cursor(HCURSOR Cursor_) { WinClassEx.hCursor = Cursor_; }
	void Background(HBRUSH Background_) { WinClassEx.hbrBackground = Background_; }
	void Menu(LPCTSTR WindowMenu_) { WinClassEx.lpszMenuName = WindowMenu_; }
	void ClassExtraBytes(int ClassExtra_) { WinClassEx.cbClsExtra = ClassExtra_; }
	void ExtraBytes(int WndExtra_) { WinClassEx.cbWndExtra = WndExtra_; }

    /* These modify data that is used by CreateWindowEx(). */
	void ExtStyle(DWORD dwExStyle_) { dwExStyle = dwExStyle_; }
	void Title(LPCSTR lpszWinTitle_) { lpszWinTitle = lpszWinTitle_; }
	void Style(DWORD dwWindowStyle_) { dwWindowStyle = dwWindowStyle_; }
	void Parent(HWND hWindowParent_) { hWindowParent = hWindowParent_; }
	void CreationData(LPVOID lpWindowParam_) { lpWindowParam = lpWindowParam_; }
	void State(int iWindowState_) { iWindowState = iWindowState_; }

	protected:
	MSG Msg;                // Application messages are saved here
    HWND hWnd;              // Application instance handle
    HWND hWindowParent;     // Parent of application
    WNDCLASSEX WinClassEx;  // Window class structure
    DWORD dwExStyle;        // Extended posibilities for variations
    DWORD dwWindowStyle;    // Window style
    LPCSTR lpszWinTitle;    // Window title bar caption
    HMENU hWindowMenu;      // Menu handle to use with window
    LPVOID lpWindowParam;   // Pointer to window creation data
    int iWindowState;       // Command to use when displaying window
    int iWindowX;           // Horizontal position of window
    int iWindowY;           // Vertical position of window
    int iWidth;             // Width of the window
    int iHeight;            // Height of the window
};

#endif /* CREATEAPP_H_ */ 


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
//  CreateApp.cpp ; 01/01/2011 ; 4:21 PM
//  Copyright (c) 2011 Rafael Perez-Santana

#include "CreateApp.h"

CreateApp::CreateApp(HINSTANCE hInstance_, WNDPROC WndProc_)
/* The class constructor initializes the window structure and window
 data with default values. The windows procedure must be given! */
{
    /* Initialize defaults for window structure. */
    WinClassEx.hInstance = hInstance_;                                  // application instance
    WinClassEx.lpszClassName = "WinClassEx";                            // class name
	WinClassEx.lpfnWndProc = WndProc_;                                  // window procedure
	WinClassEx.style = CS_HREDRAW | CS_VREDRAW;                         // class style
	WinClassEx.hIcon = LoadIcon (NULL,IDI_APPLICATION);                 // Window icon
	WinClassEx.hIconSm = LoadIcon (NULL,IDI_APPLICATION);               // Iconic state
	WinClassEx.hCursor = LoadCursor (NULL,IDC_ARROW);                   // Window cursor
	WinClassEx.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);    // Window background
	WinClassEx.lpszMenuName = NULL;                                     // Menu to attach
	WinClassEx.cbClsExtra = 0;                                          // class extra bytes
	WinClassEx.cbWndExtra = 0;                                          // window extra bytes
	WinClassEx.cbSize = sizeof(WNDCLASSEX);                             // Size of structure

    /* Initialize defaults for window data. */
    dwExStyle = 0;                          // Extended window style
    lpszWinTitle = "WindowsApp";            // Window title bar caption
    dwWindowStyle = WS_OVERLAPPEDWINDOW;    // Window style
	iWidth = 500;                           // Default window width
    iHeight = 500;                          // Default window height
	iWindowX = 10;                          // Horizontal window position
	iWindowY = 10;                          // Vertical window position
	hWindowParent = HWND_DESKTOP;           // Handle to parent or owner window
    hWindowMenu = NULL;                     // Handle to menu, or child-window identifier
    lpWindowParam = NULL;                   // Pointer to window-creation data
    iWindowState = SW_SHOWNORMAL;           // Window state
}

HWND CreateApp::Present()
/* The Present() function will register the window class, create the window
 and obtain a device-context for the window. */
{
    RegisterClassEx(&WinClassEx);   // Register the window class.

	/* Now we can create the application window. */
    hWnd = CreateWindowEx(
        dwExStyle,                  // Extended window style
		WinClassEx.lpszClassName,   // Pointer to registered class name
		lpszWinTitle,               // Window title bar caption
		dwWindowStyle,              // Window style
		iWindowX,                   // Horizontal position of window
		iWindowY,                   // Vertical position of window
		iWidth,                     // Window width
		iHeight,                    // Window height
		hWindowParent,              // Handle to parent or owner window
		hWindowMenu,                // Handle to menu, or child-window identifier
		WinClassEx.hInstance,       // Handle to application instance
		lpWindowParam);             // Pointer to window-creation data

    ShowWindow(hWnd, iWindowState); // Display the window.

	return hWnd;                    // Return the window handle.
}

void CreateApp::Center()
/* Centers the application window on the desktop. */
{
	RECT DesktopRect;
	/* Get the dimensions of the desktop window. */
	SystemParametersInfo(SPI_GETWORKAREA, 0, &DesktopRect, 0);
	/* Calculate the centered positions for our window. */
	iWindowX = (DesktopRect.right - iWidth) / 2;
	iWindowY = (DesktopRect.bottom - iHeight) / 2;
}
closed account (3pj6b7Xj)
Here is the ColorArray class code...just something simple I created so I can make use of your technique.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//  ColorArray.h
#ifndef COLORARRAY_H_
#define COLORARRAY_H_

#include <windows.h>
#include <vector>
using std::vector;

class ColorArray
{
    public:
    ColorArray();

    void NextColor();
    LPCSTR GetColor();

    protected:
    int MyColor;
    vector<LPCSTR> Colours;
};

#endif // COLORARRAY_H_ 


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
// colorarray.cpp
#include "colorarray.h"

void ColorArray::NextColor()
{
    MyColor++; if (MyColor >= (int)Colours.size()) MyColor = 0;
}

LPCSTR ColorArray::GetColor()
{
    return this->Colours[MyColor];
}
ColorArray::ColorArray()
{
    MyColor = 0; // default starting color
    Colours.push_back("  BLACK   ");
    Colours.push_back("  BLUE    ");
    Colours.push_back("  GREEN   ");
    Colours.push_back("  RED     ");
    Colours.push_back("  VIOLET  ");
    Colours.push_back("  PURPLE  ");
    Colours.push_back("  INDIGO  ");
    Colours.push_back("  YELLOW  ");
    Colours.push_back("  BROWN   ");
    Colours.push_back("  EGGS    "); // Huh?
}
closed account (3pj6b7Xj)
That is everything, it all compiled to 23.50kb (release) debug it was a little higher.

Note InvalidateRect, had to force a redraw to call WM_PAINT again after the button is clicked, but i'm pretty sure there is probably a better way but what do you think?

By the way, some of your coding style got attached to me, lol. My next task is to do it with an array of pointers and then make use of the function pointers like you did on the other app.

I'll return to squibbles when I think I fill confident about all of this.
Last edited on
I'll run it tomorrow, but just a quick note on something I saw....

If something fails in a WM_CREATE handler (memory allocation, whatever) that would compromise the app to the point where there is no point continuing, you can just return -1. Look up WM_CREATE in msdn - they mention it. That will cause the CreateWindow() call in WinMain() to fail and the app will just terminate. I'm just mentioning this cuz I saw you were calling PostQuitMessage() in that case, but -1 is a bit less typing.

I'll look into Toshibas. I had an excellent one issued to me ages ago. It was a Win 95. Awesome machine at the time. I always regretted never purchasing one on my own because it was probably the best laptop I ever had. Right now my best laptop is an HP. It works pretty good except I can't go to You Tube or it tears the whole operating system out. Had to reinstall XP on it twice due to that. Never saw anything like it.
Glad to see you are interested in pointers. One of the things you need to think through with storing arrays in dynamic memory is that you need to allocate enough memory to hold either all the objects, or pointers to all the objects, and there is quite a difference between the two. If you need to store ten 32 bit ints then you can just request 10 * 4 = 40 bytes. However, what if you need to store ten strings? A question comes up here for which I have no better way of describing it other than 'ownership'. If the strings are read from a file or user input, and you can get them from some external source that persists, there may be no need to store them. However, for your object to take ownership of them you need to first allocate a buffer to hold ten character pointers, then allocate additional memory to store each string.

You might want to take a close look at that scrolling example I posted earlier in this thread. In that example I wanted to scroll I believe 50 strings. These strings were created and stored in the WM_CREATE handler. 1st a buffer to hold the 50 string pointers was allocated, then a loop ran to create storage for each string. Of course, when you click on the scroll bars or drag the scroll thumb, these buffers were accessed to get the strings to scroll. It tends to burn your brain out thinking through the two levels of indirection involved. Good training for COM.
I got your code to run fine mrfaosfx!

So far I've only looked at Main.cpp (will get to the rest though!), but thought I'd mention these few things, since you mentioned you would appreciate input.

I don't see the point of the PeekMessage() in your message loop. That is useful for some relatively advanced and unusual usages, but I wouldn't say it is anywhere typical.

Same with your use of WM_SYSCOMMAND. Just off the top of my head I believe I've used that already to trap [ESC] keypresses to close a window, and to trap such things as [CTRL] [DELETE] and other such stuff, but for most apps, a simple WM_CLOSE and/or WM_DESTROY works fine.

Here is your main.cpp with those couple minor changes...

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
// Main.cpp ; 01/01/2011

/* My first program using freddie1's technique of placing a pointer in the
 window extra bytes which can be used to access objects with out using global
 variables! This program uses one pointer, my next task is to create one
 that uses a pointer to an array of pointers or an object of pointers! This would
 allow access to multiple objects within a program with out using those global
 variables! */

#include <windows.h>
#include "colorarray.h"
#include "CreateApp.h"

#define BTNEXT      9001                    // Id for Next Button.

/*  Declare Windows procedure  */
LRESULT CALLBACK WinProc(HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,char * pArgs,int iShow)
{
    CreateApp Window(hInstance,WinProc);    // Create window & point to procedure
    Window.Size(300,300);                   // Window dimensions.
    Window.Center();                        // Center window on the desktop.
    Window.ExtraBytes(4);                   // Allocate 4 extra bytes for window.
    HWND hWnd=Window.Present();             // Display the window.
    HWND hBtChange=CreateWindow             /* create button; Next (BTNEXT) */
    ("button","Next",WS_CHILD|BS_PUSHBUTTON,10,30,80,35,hWnd,(HMENU)BTNEXT,hInstance,NULL);
    ShowWindow(hBtChange,SW_SHOW);          // show the button.
    MSG Msg;                                // Messaves saved here.

    while(GetMessage(&Msg,NULL,0,0))
    {
          TranslateMessage(&Msg);
          DispatchMessage(&Msg);
    }

    /*
    bool bQuit=false;                       // Loop quits if true.
    while(!bQuit)
    {
        if(PeekMessage(&Msg,NULL,0,0,PM_REMOVE))
        {   // if WM_QUIT is found, quit the loop.
            if(Msg.message == WM_QUIT) { bQuit=true; continue; }
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    }
    */

    return 0;                               // Program terminates here.
}

/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WinProc(HWND hWnd,UINT Message,WPARAM wParam,LPARAM lParam)
{
    switch (Message) // handle program messages...
    {
        case WM_CREATE:
        {   /* this message is placed right after createwindow is done. */
            ColorArray*pColorArray=NULL; // <-- I create null pointer here
            pColorArray=new ColorArray(); // Now I create the object and store...
            if (pColorArray)SetWindowLong(hWnd,0,(long)pColorArray); // pointer in window extra bytes!
            else return -1; // in case there is a fault, oh no quit!
            return 0;
        }

        case WM_PAINT:
        {   // here we handle what appears on the client window.
            ColorArray*pColorArray=NULL; // you know what this is already.
            PAINTSTRUCT ps; // structure for painting
            HDC WinDC; // handle to device-context
            WinDC=BeginPaint(hWnd,&ps); // stores dc in WinDC
            pColorArray=(ColorArray*)GetWindowLong(hWnd,0); // my favorite part, fetch the pointer.
            LPCSTR Color=pColorArray->GetColor(); // now we can call one of the member functions!
            TextOut(WinDC,10,10,Color,strlen(Color)); // output the result in the client area.
            EndPaint(hWnd,&ps); // done painting...
            return 0;
        }

        case WM_COMMAND:
        {
            if(LOWORD(wParam)==BTNEXT) // is out our Next button?
            {   // cycle to the next color.
                ColorArray*pColorArray=NULL;
                pColorArray=(ColorArray*)GetWindowLong(hWnd,0);
                pColorArray->NextColor(); // here I call the nextcolor() function.
                InvalidateRect(hWnd,NULL,false); // i needed a way to update client area.
            }

            return 0;
        }

        case WM_CLOSE:
        {
             int iMB=MessageBox(hWnd,"Are you sure you want to quit?","Message",MB_YESNO|MB_ICONQUESTION);
             if(iMB==IDYES)
             {
                ColorArray*pColorArray=NULL;
                pColorArray=(ColorArray*)GetWindowLong(hWnd,0);
                if (pColorArray)
                    delete pColorArray;
                DestroyWindow(hWnd);
                PostQuitMessage(0);
             }
             return 0;
         }
         default:
             break;
    }

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


I think you are doing pretty good. Probably a lot better than I when I first started!

Oh yes! Your use of InvalidateRectangle() is correct. That is the proper way to structure Windows programs. Put everything in WM_PAINT necessary to restore/update a window, and force a repaint when necessary. Petzold mentions that this is many times awkward, and to be sure a seemingly strange behavior, but becauise of the way GDI works its the best way to go.
Last edited on
closed account (3pj6b7Xj)
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
// Main.cpp

#include <stdio.h>
#include <windows.h>

#define BTSWAP  9001    // Id for Swap button.

// create structure that holds a name & age.
struct Person { LPCSTR lpszName; int iAge; };

LRESULT CALLBACK WinProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
    switch (Msg) // handle messages.
    {
        case WM_CREATE:
        {   /* this message is trapped before the window appears. */
            Person*ptrPerson=new Person[2]; // allocate memory for two objects of type person.
            if(ptrPerson) // if the memory was allocated...
            {   // fill in object parameters; name & age.
                 ptrPerson[0].lpszName="Billy"; ptrPerson[0].iAge=26; // billy likes a girl.
                 ptrPerson[1].lpszName="Sarah"; ptrPerson[1].iAge=19; // her name is sarah.
            } else return -1;// if memory allocation fails, abort! sarah hates billy.
            // note that I did not create a null pointer.
            SetWindowLongPtr(hWnd,0,(long)ptrPerson); // store pointer address in wnd extra bytes.
            /* the Swap button is part of the window so, we should create it here. */
            HWND hBtSwap=CreateWindow
                ("button", // pre-defined button class
                 "Swap", // the text in our button
                 WS_CHILD|BS_PUSHBUTTON, // the relationship & button type
                 100,20, // horizontal & vertical position
                 100,80, // width & height of the button
                 hWnd, // parent of the button
                 (HMENU)BTSWAP, // the button Id, how we find it.
                 GetModuleHandle(NULL), // application instance handle.
                 NULL); // no window creation-data.
            ShowWindow(hBtSwap,SW_SHOW); // display Swap button.
            /* I allocated 8 bytes, that means I have two offsets I can access to store information
             with SetWindowLongPtr() the first is at offset 0 and the second is at offset 4, each
             offset is 4 bytes. We will store the address of the button handle at offset 4. */
            SetWindowLongPtr(hWnd,4,(long)hBtSwap); // whoat wait, was hBtSwap a pointer!?
            /* yes, a HWND is actually a pointer! so hBtSwap is a pointer to the button. */
            break;
        }

        case WM_PAINT:
        {
            char chBuffer[128]; // temporary character buffer
            PAINTSTRUCT ps; // paint structure
            HDC WinDC; // device-context handle
            WinDC=BeginPaint(hWnd,&ps); // begin paint scene in device-context
            // create and fetch pointer from wnd extra bytes
            Person*ptrPerson=(Person*)GetWindowLongPtr(hWnd,0);
            /* now we will output data with the pointer. I use sprintf() from <stdio.h> */
            TextOut(WinDC,10,10,ptrPerson[0].lpszName,strlen(ptrPerson[0].lpszName)); // billy
            sprintf(chBuffer,"Age: %i",ptrPerson[0].iAge); // format string with billy's age.
            TextOut(WinDC,10,30,chBuffer,strlen(chBuffer)); // display the formated string.
            TextOut(WinDC,10,70,ptrPerson[1].lpszName,strlen(ptrPerson[1].lpszName)); // sarah
            sprintf(chBuffer,"Age: %i",ptrPerson[1].iAge); // format string with sarah's age.
            TextOut(WinDC,10,90,chBuffer,strlen(chBuffer)); // display the formated string.
            /* tell the user what the Swap button does. */
            sprintf(chBuffer,"Click Swap button to swap Billy & Sarah data.");
            TextOut(WinDC,10,130,chBuffer,strlen(chBuffer));
            /* some more information for the user. */
            sprintf(chBuffer,"Press Delete to hide Swap button, Insert to show.");
            TextOut(WinDC,10,150,chBuffer,strlen(chBuffer));
            EndPaint(hWnd,&ps); // end paint scene, releases device-context.
            break;
        }

        case WM_COMMAND:
        {
            if(LOWORD(wParam)==BTSWAP) // Is it the swap button?
            {   /* swap the contents of the objects. */
                LPCSTR lpszName; // temp; to store name
                int iAge; // temp; to store age
                // create & fetch pointer from wnd extra bytes
                Person*ptrPerson=(Person*)GetWindowLongPtr(hWnd,0);
                // move data from object in position 0 to temporary variables
                lpszName=ptrPerson[0].lpszName;
                iAge=ptrPerson[0].iAge;
                // now move object in position 1 to position 0
                ptrPerson[0].lpszName=ptrPerson[1].lpszName;
                ptrPerson[0].iAge=ptrPerson[1].iAge;
                // now move data in temporary variables to position 1
                ptrPerson[1].lpszName=lpszName;
                ptrPerson[1].iAge=iAge;
                /* the swap is complete. */
                InvalidateRect(hWnd,NULL,true); // update the window.
                SetFocus(hWnd); // give focus back to the window.
                /* I give focus back to the window because when the button is pressed, it
                 stays selected (focused) and the delete & insert keys wont work to hide
                 and show the button, we want those to work for this application. */
            }

            break;
        }

        case WM_KEYDOWN:
        {   /* handle virtual key messages only. */
            if (wParam==VK_DELETE) // delete is pressed, hide the button.
            {   // create handle & fetch pointer from wnd extra bytes.
                HWND hBtSwap=(HWND)GetWindowLongPtr(hWnd,4);
                // I did not use (HWND*) this time because HWND is already a pointer!
                ShowWindow(hBtSwap,SW_HIDE); // hides the button.
            }

            if (wParam==VK_INSERT) // insert is pressed, show the button.
            {   // create handle & fetch pointer from wnd extra bytes.
                HWND hBtSwap=(HWND)GetWindowLongPtr(hWnd,4);
                // I did not use (HWND*) this time because HWND is already a pointer!
                ShowWindow(hBtSwap,SW_SHOW); // shows the button.
            }

            break;
        }

        case WM_DESTROY:
        {   /* terminate the application. */
            // create and fetch pointer from wnd extra bytes.
            Person*ptrPerson=(Person*)GetWindowLongPtr(hWnd,0);
            delete [] ptrPerson; // free up the allocated memory.
            PostQuitMessage(0); // post quit message and get out.
            break;
        }

        default:
        /* pass non-trapped messages to DefWindowProc */
        return DefWindowProc(hWnd,Msg,wParam,lParam);
    }

    return 0;
}

int WINAPI WinMain(HINSTANCE hIns,HINSTANCE hPrev,char*pArgs,int iShow)
{
    MSG Msg;
    HWND hWnd;
    WNDCLASSEX WinClassEx;
    WinClassEx.hInstance=hIns;
    WinClassEx.lpszClassName="WindowClassEx";
    WinClassEx.lpfnWndProc=WinProc;
    WinClassEx.style=CS_HREDRAW|CS_VREDRAW;
    WinClassEx.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    WinClassEx.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
    WinClassEx.hCursor=LoadCursor(NULL,IDC_ARROW);
    WinClassEx.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    WinClassEx.lpszMenuName=NULL;
    WinClassEx.cbClsExtra=0;
    WinClassEx.cbWndExtra=8; // window extra bytes
    WinClassEx.cbSize=sizeof(WNDCLASSEX);
    RegisterClassEx(&WinClassEx);
    int iWidth=500; int iHeight=200;
    RECT DesktopRect; SystemParametersInfo(SPI_GETWORKAREA, 0, &DesktopRect, 0);
    int iWindowX=(DesktopRect.right-iWidth)/2;
    int iWindowY=(DesktopRect.bottom-iHeight)/2;
    hWnd = CreateWindowEx(0,
         WinClassEx.lpszClassName,
         "Billy&Sarah",
         WS_OVERLAPPEDWINDOW &~WS_SIZEBOX&~WS_MINIMIZEBOX&~WS_MAXIMIZEBOX,
         iWindowX, iWindowY,
         iWidth, iHeight,
         HWND_DESKTOP,
         NULL,
         WinClassEx.hInstance,
         NULL);
    ShowWindow(hWnd,SW_SHOWNORMAL);
    while(GetMessage(&Msg,NULL,0,0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}
Last edited on
closed account (3pj6b7Xj)
What do you think? :)

Here is an app picture: http://oi51.tinypic.com/2ewoe11.jpg

I had a lot of fun making this one.

Note that this time I didn't create any NULL pointers, I also used SetWindowLongPtr() & GetWindowLongPtr().. the WinMain was highly commented but it dind't fit because the forum only allows 8,192 characters but the WinProc has all its comments and explains everything in detail.

I think I am ready to head back to the squibbles now, I've learned a lot from you freddie1, thank you so much! Teaching yourself to program is not enough, you need to be exposed to people who are doing the same as you, I would have never figured this out on my own!

I wonder if cbClsExtra works the same way, extra bytes after the class? I'll have to google that bit up and see what the differences are.
Last edited on

I wonder if cbClsExtra works the same way, extra bytes after the class? I'll have to google that bit up and see what the differences are.


I used .cbClsExtra bytes in that post of several weeks ago which created four main windows and displayed mouse coordinates, typed text, etc. I keep referring you to that post because there is a lot to learn from it. Check it out and see how I used the .cbClsExtra bytes.

HINT: think static variables in C++ classes!
closed account (3pj6b7Xj)
I remember which post it was now "My class for constructing application windows".
I think the name of the program was "Typer". If you can't find it holler!
Topic archived. No new replies allowed.
Pages: 12