Windows in a class

Started by
13 comments, last by mk83 22 years, 5 months ago
Hi Guys, I am trying to write a program, but to make it neater + in order to learn, I am trying to put the windows code into classes, it seemed to be going pretty well, I have managed to create a simple windows class that calls a static message handler, that is able to get a pointer to the class by using the getwindowlong function. All fine and well, when having multiple instances of a window each one responds only to the commands clicked on it! But then I created a second class, I create a window in this class by handing it the handle of the first window I have created and using GetWindowLong(ParentWindow, GWL_HINSTANCE) to get the instance of the program. This new class also has a static Message handling procedure, with the same code as for the previous class only modified to be applicable to the new class. When I create a new window with the class in my main program, it works fine, all the buttons work etc. when I create the new window from within the Message handling procedure of the first class I created, nothing works, the window appears, but the buttons don't work and if I trace through it, the Instance of the the class and the HWND of the window seem to get all jumbled up!!! I have tried many things, but just don't seem to see where I am going wrong, please help!! Source to create the first class
    
wMainWindow::wMainWindow(HINSTANCE hofProgram){
	setInstance(hofProgram);

   // define the window class

   #ifndef Main_Window_Class
   #define Main_Window_Class
   WNDCLASS wc;
   wc.style 		  = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = &wMainWindow::WindowProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = sizeof(wMainWindow*);
   wc.hInstance     = hInstance;
   wc.hIcon		     = NULL;
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH) COLOR_WINDOW;
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = "Main Window Class";
   // Create the Window Class

   RegisterClass(&wc);
   #endif

   // Create an actual instance of the Window

   hMainWindow = CreateWindow(
                "Main Window Class", // Type of window we want to create

   				 "Transport manager Server", // Name of our Window

                WS_VISIBLE, // Style of our Window

                0,0,640,480, // Position and Size

                NULL, //No Parent

                NULL, //No Menu

                hInstance, // Belongs to the calling Program

                NULL);

   // Put a pointer to this instance of the class in the Window Header

   SetWindowLong(hMainWindow,GWL_USERDATA,(long)this);


   // create the exit button

   hbExit	  = CreateWindow(
   				"BUTTON",
      			"Exit",
               WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
               570,410,
               50,30,
               hMainWindow,
               NULL,
               (HINSTANCE) GetWindowLong(hMainWindow, GWL_HINSTANCE),
               NULL);

   //create the Add server button

   hbAddServer= CreateWindow(
   				"BUTTON",
      			"Add Server",
               WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
               470,410,
               90,30,
               hMainWindow,
               NULL,
               (HINSTANCE) GetWindowLong(hMainWindow, GWL_HINSTANCE),
               NULL);
};

LRESULT CALLBACK wMainWindow::WindowProc(HWND hwnd,	// handle of window

                                         UINT uMsg,	// message identifier

                                         WPARAM wParam,	// first message parameter

                                         LPARAM lParam 	// second message parameter

                                         ){
                                         
  // retrieve the pointer from the Window header

  wMainWindow* ThisWindow = (wMainWindow*) GetWindowLong (hwnd,GWL_USERDATA);
  switch (uMsg)
   {
   	case WM_CLOSE :
      	ProgActive = false;
         break;

      case WM_COMMAND :
         switch HIWORD(wParam)
         {
           	case BN_CLICKED :
            	if ((HWND)lParam == ThisWindow->hbExit)
               	ProgActive = false;

            	if ((HWND)lParam == ThisWindow->hbAddServer){
                HWND Temp = ThisWindow->GetHandle();
               	wServerWindow TestWindow(Temp);
               	TestWindow.Show();
               };

  	      		break;
         } // switch HIWORD(wParam)

         break;

   } // switch uMsg

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

for the second class

      
wServerWindow::wServerWindow(HWND ParentWindow){

	setInstance((HINSTANCE) GetWindowLong(ParentWindow, GWL_HINSTANCE)); \\ sets hInstance

   // Create an actual instance of the Window

   hServerWindow = CreateWindow(
                "Server Window Class", // Type of window we want to create

   				 "Servers", // Name of our Window

                WS_OVERLAPPEDWINDOW, // Style of our Window

                0,0,320,240, // Position and Size

                NULL, //Parent Window

                NULL, //No Menu

                hInstance, // Belongs to the calling Program

                NULL);

   if (!(hServerWindow)) {
   	// define the window class

   	WNDCLASS wc;
   	wc.style 		  = CS_HREDRAW | CS_VREDRAW;
   	wc.lpfnWndProc   = &wServerWindow::WindowProc;
   	wc.cbClsExtra    = 0;
   	wc.cbWndExtra    = sizeof(wServerWindow*);
      wc.hInstance     = hInstance;
   	wc.hIcon		     = NULL;
   	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   	wc.hbrBackground = (HBRUSH) COLOR_WINDOW;
   	wc.lpszMenuName  = NULL;
   	wc.lpszClassName = "Server Window Class";
   	// Create the Window Class

   	RegisterClass(&wc);

   	hServerWindow = CreateWindow(
                "Server Window Class", // Type of window we want to create

   				 "Servers", // Name of our Window

                WS_OVERLAPPEDWINDOW, // Style of our Window

                0,0,320,240, // Position and Size

                NULL, //Parent Window

                NULL, //No Menu

                hInstance, // Belongs to the calling Program

                NULL);
   };

   // Put a pointer to this instance of the class in the Window Header

   SetWindowLong(hServerWindow,GWL_USERDATA,(long)this);

   // create the exit button

   hbExit	  = CreateWindow(
   				"BUTTON",
      			"Exit",
               WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
               10,10,
               50,30,
               hServerWindow,
               NULL,
               (HINSTANCE) GetWindowLong(ParentWindow, GWL_HINSTANCE),
               NULL);

};

LRESULT CALLBACK wServerWindow::WindowProc(HWND hwnd,	// handle of window

                                         UINT uMsg,	// message identifier

                                         WPARAM wParam,	// first message parameter

                                         LPARAM lParam 	// second message parameter

                                         ){

  // retrieve the pointer from the Window header

  wServerWindow* ThisWindow = (wServerWindow*) GetWindowLong (hwnd,GWL_USERDATA);
  switch (uMsg)
   {
      case WM_COMMAND :
         switch HIWORD(wParam)
         {
           	case BN_CLICKED :
              	ThisWindow->Close();
  	      		break;
         } // switch HIWORD(wParam)

         break;

   } // switch uMsg

	return(DefWindowProc(hwnd, uMsg, wParam, lParam));
};
    
The message handling procedure of the first class creates a new window of the second class!! but it does not function correctly, If a create a new window of the second class from within the main programm and not from the Message handling procedure the second window behaves correctly. What am I missing? Thanks for the help, MK83 Edited by - mk83 on November 8, 2001 4:29:59 AM
Advertisement
I looked it over and what''s wrong isn''t jumping out at me.
Though that''s not the way I''ve seen most people go about it, I think it should work. The problem code may lie elsewhere.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Hi Magmai,

How would it normally be done? this is the first time I am trying it so any help would be great!!

Thanks,
Robert
From a quick lookover it looks as if in the wServerWindow ctor you are creating a window of class "Server Window Class" before you have actually registered that class.
Often a window class is registered for each window, and the code for registration and creation is pushed into a base class. And they usually use RegisterWindowEx and CreateWindowEx (they were added with Win95 for additional window styles).

quote:
From a quick lookover it looks as if in the wServerWindow ctor you are creating a window of class "Server Window Class" before you have actually registered that class.

He tries to create a window, and if it fails he calls the register and then create again. Thought you had it for a minute...

- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Oops - I missed that.

Anyway (luckily), I''m pretty confident that the problem is this:

When you call CreateWindow() in the wServerWindow ctor you are not passing in the handle of the parent window (which is the handle given to the ctor). This would account for messages failing to reach the window.
Thanks for the help so far, unfortunatly I have still not been able to fix the problem! I''m wondering if it is perhaps a scope problem? I.e. the instance of the class I create only has scope in the function that that creates it? That would explain why calls to the instance of the class don''t work outside of the original creating function?

I''m gonna try using global variables and see if I can get around the problem that way,

Just another question is there a way of declaring something so that it has a wider scope than just the creating structure?

Magmai, should I create a base class and then inherit from those for my new classes? That would make sense because most of the windows will have the same class and create functions, my only problem with that is that the base class would have to have the Message handling function as part of it! but the different versions of windows would have different buttons etc on it so each message handling function would have to be different and that would not be possible with a base class, or am I missing something? I''ll play around a bit and see if I can get it to work!

Thanks,
MK83
This is a very interesting subject, I think, and I would appreciate it if someone could post a link to a tutorial or source code on the subject.

Judging by the past, it seems certain that something we cannot imagine today will dominate the landscape in 2021. That''''s why the future of the PC is even more exciting than the past.
Judging by the past, it seems certain that something we cannot imagine today will dominate the landscape in 2021. That''s why the future of the PC is even more exciting than the past.
Ok, there''s a very simple reason why what your trying to do will not work. The address your providing for the wndProc is the address of a function in a class. When you call the function in a line of code like...
MyWindow.WndProc();
the compiler will secretly pass a parameter to the function telling it which instance of that class type (i.e. the ''this'' pointer value).
When windows calls the WndProc function it does not pass the ''this'' parameter. Therefore the function has no idea which instance it is and act''s unpredictably.

P:\ray\main.cpp(74) : error C2440: ''type cast'' : cannot convert from ''long (__thiscall Wnd::*)(struct HWND__ *,unsigned int,unsigned int,long)'' to ''long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)''

if you look at this error you''ll notice that the WndProc of the class is a function call of type __thiscall, meaning that it passes the ''this'' parameter. and the (WNDPROC) definition is a __stdcall meaning that no extra info is passed to the call.

The only way I''ve found to be able to do something like what you want is to do the following...

after the call to CreateWindow add this...

SetWindowLong(hWnd,GWL_USERDATA,this);

this stoues a reference to the object in the windows own data space.
Then write a standalone WndProc function like this...

LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){

MyWndClass *CurrentWnd = (MyWndClass *)GetWindowLong(hWnd,GWL_USERDATA);

if(!CurrentWnd)
return DefWindowProc(hWnd,uMsg,wParam,lParam);

return CurrentWnd->WndProc(hWnd,uMsg,wParam,lParam);
}

this retrieves the reference to the window class and calls it WndProc function.
And refer to this function in the WNDCLASS struct...

wc.lpfnWndProc = (WNDPROC)WndProc;

Thats what I do.
------------------------------Piggies, I need more piggies![pig][pig][pig][pig][pig][pig]------------------------------Do not invoke the wrath of the Irken elite. [flaming]
In the original post the poster says that the WindowProc() methods for the classes are static, as they have to be. This means they are not passed an invisible ''this'' pointer (since they can be used from outside a member of the class).

The real problem is the failure to give ''wServerWindow'' windows a handle to their parent when CreateWindow is called for them. The difference in behaviour described in the original post between creation from inside a ''wMainWindow'' and directly from the app is that when created directly, the window doesn''t need a parent as it is effectively the main window.

This topic is closed to new replies.

Advertisement