Got a few problems with the code from a tutorial.

This is the winapi 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
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
//Main.cpp                                   //C++ Object Oriented Program using native
#define UNICODE                              //Windows C Api to display the dimensions
#define _UNICODE                             //and volume of a class CBox object.  Note
#include <windows.h>                         //that this app contains no global variables.
#include <tchar.h>
#include <stdio.h>
#include "CBox.h"


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
         TCHAR 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,_T("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
         _stprintf(szBuffer,_T("Box.GetLength() = %6.2f"),pBox->GetLength()); //and does a lot of
         TextOut(hDC,25,30,szBuffer,_tcslen(szBuffer));                       //fancy GDI (Graphics
         _stprintf(szBuffer,_T("Box.GetWidth()  = %6.2f"),pBox->GetWidth());  //Device Interface) stuff
         TextOut(hDC,25,60,szBuffer,_tcslen(szBuffer));                       //to format the CBox's
         _stprintf(szBuffer,_T("Box.GetHeight() = %6.2f"),pBox->GetHeight()); //specifications, i.e.
         TextOut(hDC,25,90,szBuffer,_tcslen(szBuffer));                       //dimensions and volume
         _stprintf(szBuffer,_T("Box.Volume()    = %6.2f"),pBox->Volume());    //to the screen, i.e.
         TextOut(hDC,25,120,szBuffer,_tcslen(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)
{
 TCHAR szClassName[]=_T("CBox3");
 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;
}


And this is the CBox.h header code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef CBox_h
#define CBox_h

class CBox
{
 public:
 CBox(double,double,double);  //Constructor
 ~CBox();                     //Destructor

 double GetLength() const;    //m_Length accessor
 double GetWidth () const;    //m_Width accessor
 double GetHeight() const;    //m_Height accessor
 double Volume   () const;    //Returns Volume() of Box

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


This is the CBox.cpp which contain the function definition of the class:
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() const
{
 return this->m_Length;
}

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

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

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


And this is the code which do the calculation:
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
/*
  CBox1  -- Moving From The Console To GUI -- First Steps

  Now that we have a template of sorts (ProgEx 37 above) for getting a GUI up
  and running in Windows, lets try to see how we might convert our console code
  to GUI.  Lets just start with a simple program to calculate the volume of a
  box, but lets wrap the thing up in a C++ class we'll name CBox.  Here's the
  code...
*/

//CBox1
#include <iostream>
#include "CBox.h"
using namespace std;

int main()
{
 CBox Box(2.0, 3.0, 4.0);

 cout << "Box.GetLength() = " << Box.GetLength() << endl;
 cout << "Box.GetWidth()  = " << Box.GetWidth () << endl;
 cout << "Box.GetHeight() = " << Box.GetHeight() << endl;
 cout << "Box.Volume()    = " << Box.Volume   () << endl;

 return 0;
}


/*
  Compiling: CBox.cpp
  Linking console executable: CBox1.exe
  Output size is 457.50 KB
  Process terminated with status 0 (0 minutes, 0 seconds)
  0 errors, 0 warnings

  Box.GetLength() = 2
  Box.GetWidth()  = 3
  Box.GetHeight() = 4
  Box.Volume()    = 24

  Above is my output and even by telling my Code::Blocks IDE that I wanted to
  'Strip All Symbols From The Executable' to minimize code size, and 'Optimize
  For Size' (Project >> Build Options >> Compiler Settings), I'm still coming
  in with a 457 K executable for a dumb console program that just multiplies
  a few numbers together.  My sophisticated GUI example above, i.e., ProgEx37
  was only 8 K in comparison!  I know hard drive space is cheap, but this is
  ridiculous!  Lets try a CBox2 like this...
*/


All the code above is from a tutorial from a forum. If we compile them under a same project( in WINAPI type project), a window will appear right? But i can't see the window, my compiler didn't show any error messages too. Does this mean i missed a line to make the window hold a bit longer like " SYSTEM("pause") " code?
These are 2 independent program, one is a console program (with main entry one) and one is a GUI program (with WinMain entry point)

You should not mix them.
Oh it work, thanks :D
From the explanation of the code:

[quote]In the case of the Window Class CBox3 which is registered down in WinMain()
below, the .cbWndExtra member of the class can be used to tell Windows to
allocate extra bytes within the class to store user specified data, thus
allowing us to store a CBox object within the Window Class structure, and
thereby and in that manner form our logical association of a GUI window with
what had heretofore been only something showing up in a black console screen![/quote

the .cbWndExtra is set to 4 is to create a 4 bytes space to hold the CBox class or its pointer? I am a bit confused by this because i thought the class is 24 bytes large...
A pointer, of course.

Be aware that your code will not work if you try to build a 64 bit application, it will probably crash.
Last edited on
You should, as modoran hinted you, use NOT 4, but sizeof(void*):
1
2
wc.cbWndExtra=4; // Bad!
wc.cbWndExtra=sizeof(void *); // WAAAY better! 


EDIT:
Also substitute your SetWindowLong with SetWindowLongPtr, same for the Get one:

1
2
3
SetWindowLongPtr(hWnd,GWLP_USERDATA,(LONG_PTR)pBox); // line 19
pBox=(CBox*)GetWindowLong(hWnd,GWLP_USERDATA); // line 39
pBox=(CBox*)GetWindowLong(hWnd,GWLP_USERDATA); // line 57 
Last edited on
I still not very sure what the setwindowlong() do, from MSDN,

Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.


So it will only change the .cbWndExtra ?

SetWindowLong(hWnd,0,(long)pBox);

if the 'int nIndex' parameter(the middle one) is set to 0, it change the message in dialog box? I don't really get this part...

For the:
GetWindowLong(hWnd,0)
does it pass the value of the pointer to message dialogue procedure first then to the window?
If i want to let the user change the box's length, width and height, I have to change the class pointed by the pointer first right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//i add this line before the window procedure
#define PBOX_BUTTON 1

//in WM_CREATE
CreateWindow(TEXT("BUTTON"), TEXT("PBOX"),
               WS_VISIBLE | WS_BORDER | WS_CHILD,
               290,150,80,20,
               hWnd, HMENU(PBOX_BUTTON), NULL ,NULL);


//in the window procedure:
case WM_COMMAND:
         if (LOWORD(wParam) == PBOX_BUTTON){
                             CBox * pBox;
                             pBox->SetHeight(6.6);
                             SetWindowLong(hWnd,0,long(pBox));
                             UpdateWindow(hWnd);
                             };


1
2
3
4
// i add this in the CBox.cpp i also added the prototype in the CBox.h files
void CBox::SetHeight(double H){
       m_Height = H;
       }


When i compile my code, a button with "PBOX'' appear under all the texts, but nothing happen when i click it. I am not sure if my class is changed or not when the button clicked. Or my code fail to tell the window to repaint to change the text.
Does that piece of code "works" ?
1
2
3
4
5
6
7
case WM_COMMAND:
         if (LOWORD(wParam) == PBOX_BUTTON){
                           pBox=(CBox*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
                             pBox->SetHeight(6.6);
                             SetWindowLongPtr(hWnd,0, reinterpret_cast<LONG_PTR>(pBox));
                             UpdateWindow(hWnd);
                             };
i tried with your code by adding pointer declaration " CBox * pBox; " before the 'if' line, and i receive no compilation error, but when i click the button, the program become unresponsive and stop working. Then i Change the line
pBox=(CBox*)GetWindowLongPtr(hWnd,GWLP_USERDATA);

into

pBox=(CBox*)GetWindowLong(hWnd,0);

But there is no change when i clicked the button...

This is the new CBox.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
//CBox.cpp
#include "CBox.h"

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

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

double CBox::GetLength() const
{
 return m_Length;
}

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

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

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

void CBox::SetLength(double L){
       m_Length = L;
       }
       
void CBox::SetWidth(double W){
       m_Width = W;
       }

void CBox::SetHeight(double H){
       m_Height = H;
       }


This is the CBox.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//#ifndef CBox_h
//#define CBox_h

class CBox
{
 public:
 CBox(double,double,double);  //Constructor
 ~CBox();                     //Destructor

 double GetLength() const;    //m_Length accessor
 double GetWidth () const;    //m_Width accessor
 double GetHeight() const;    //m_Height accessor
 double Volume   () const;    //Returns Volume() of Box
 void SetLength(double);
 void SetWidth (double);
 void SetHeight(double);

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

And this is the main.pp, have to divide the post because this code is too long...
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
//Main.cpp                                   //C++ Object Oriented Program using native
#define UNICODE                              //Windows C Api to display the dimensions
#define _UNICODE                             //and volume of a class CBox object.  Note
#include <windows.h>                         //that this app contains no global variables.
#include <tchar.h>
#include <stdio.h>
#include "CBox.h"

#define PBOX_BUTTON 1
HWND hWndlength;
HWND hWndheigth;
HWND hWndwidth;

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
         /*hWndwidth = CreateWindow(TEXT("EDIT"), TEXT("width"),
                         WS_VISIBLE | WS_BORDER | WS_CHILD,
                         25,150,80,20,
                         hWnd, HMENU(NULL), NULL ,NULL);
               
         hWndlength = CreateWindow(TEXT("EDIT"), TEXT("length"),
                        WS_VISIBLE | WS_BORDER | WS_CHILD,
                        110,150,80,20,
                        hWnd, HMENU(NULL), NULL ,NULL);
               
         hWndheigth = CreateWindow(TEXT("EDIT"), TEXT("height"),
                        WS_VISIBLE | WS_BORDER | WS_CHILD,
                        195,150,80,20,
                        hWnd, HMENU(NULL), NULL ,NULL);*/
               
         CreateWindow(TEXT("BUTTON"), TEXT("PBOX"),
               WS_VISIBLE | WS_BORDER | WS_CHILD,
               290,150,80,20,
               hWnd, HMENU(PBOX_BUTTON), NULL ,NULL);
         
         if(pBox)                              //handler returns.  A new CBox is created
            SetWindowLongPtr(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_COMMAND:{
         CBox *pBox; 
         if (LOWORD(wParam) == PBOX_BUTTON){
                             pBox=(CBox*)GetWindowLong(hWnd,0);
                             pBox->SetHeight(6.6);
                             SetWindowLongPtr(hWnd,0, reinterpret_cast<LONG_PTR>(pBox));
                             UpdateWindow(hWnd);
                             };
          
          break;
          }
    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
         TCHAR 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,_T("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
         swprintf(szBuffer,_T("Box.GetLength() = %6.2f"),pBox->GetLength()); //and does a lot of
         TextOut(hDC,25,30,szBuffer,_tcslen(szBuffer));                       //fancy GDI (Graphics
         _stprintf(szBuffer,_T("Box.GetWidth()  = %6.2f"),pBox->GetWidth());  //Device Interface) stuff
         TextOut(hDC,25,60,szBuffer,_tcslen(szBuffer));                       //to format the CBox's
         _stprintf(szBuffer,_T("Box.GetHeight() = %6.2f"),pBox->GetHeight()); //specifications, i.e.
         TextOut(hDC,25,90,szBuffer,_tcslen(szBuffer));                       //dimensions and volume
         _stprintf(szBuffer,_T("Box.Volume()    = %6.2f"),pBox->Volume());    //to the screen, i.e.
         TextOut(hDC,25,120,szBuffer,_tcslen(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)
{
 TCHAR szClassName[]=_T("CBox3");
 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;
}
Erm, i find something interesting!! after compiling the code, the volume is 24, and when i clicked the button, no apparent change occur. But when i resize the window border to make its width smaller until the '24' is out of the client area, then i resize the window again to see the '24', it changed into 39.6 . I guess the UpdateWindow() isn't really working in the code,
just by clicking the button, the class did changed but the window didn't update the screen
That's because UpdateWindow() sends a WM_PAINT message to the window. This causes the window to repaint the invalid region. However, your code has not told Windows that any part of the window is invalid. You need to do that. You do that by either telling Windows the part of the window that is invalid, or by telling Windows that the entire window is invalid and needs to be entirely repainted. To do the latter, replace your UpdateWindow() call with this ...

 
InvalidateRect(hWnd, NULL, TRUE);
The reason why it changed to the new value after you minimized the window or resized it back is that you made part of the window invalid, so the code in the WM_PAINT handler executed. You are making progress. You just learned something by experimentation. Good job!
Thanks guys, problem solved and i learned somethings :D
Topic archived. No new replies allowed.