Link to home
Start Free TrialLog in
Avatar of _KM_
_KM_

asked on

MDI Problem

I am trying to create an MDI application, i have managed to get everything working fine by creating the main window with MDI frame, then i created an MDI client - all worked well until i tried creating multiple MDI clients, they both loaded but variables such as socket's where shared between them so they used the same ones (DOH), this was because i had them all set as global variables, to fix this i spent ages re-writing the child window to have all the code inside a class so i could create a new instance of the class for each window and therefore get different variables. This didn't work because the WndProc has to be static, therefore the variables have to be static, and that means that if i create 2 instances of the class then they still share the same damn variables... so quite simply, how the hell am i meant to get 2 MDI children (or more) that use different variables? so i can open 100 windows if i want and they will each have their own variable called "s" or whatever
ASKER CERTIFIED SOLUTION
Avatar of mrwad99
mrwad99
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of _KM_
_KM_

ASKER

I created the windows via the win32 API and not via all that MFC stuff...
In that case then you just need to add the variables to whatever class or code you have representing the window.  You could add a struct that contains all the variables, eg

typedef struct myWindStructTAG
{
     int iVar1;
     int iVar2;
     UINT uSomethingelse;
}myWindStruct;

and then whenever creating a new window simply give it one of these with all the variable values filled in appropriately
Avatar of _KM_

ASKER

how would i assign it to the window? i cant put it in the class because as its a static variable it would share it over all the instances of the class, and i tried using the user data part of the windows properties to store it but it just came up with memory write errors
You must be creating an instance of some window or some object that represents each MDI child.

>> i cant put it in the class because as its a static variable

You don't want it to be static if you want each child to have one of these structs.  Post the code that you are calling to create a new MDI child window and I will have a look, although I am not too good at this since it is API and not MFC.
Avatar of _KM_

ASKER

I have a thread for each window       which creates the class and then sits there waiting for the window to be closed before destroying the class

typedef struct THREADSTRUCT
{
    ChannelWindow*    ThisThing;
        //you can add here other parameters you might be interested on
} THREADSTRUCT;

ChannelWindow wind;
      wind.window(data.channame, data.username);
      while(wind.active) Sleep(100);
      return 0;

and the ChannelWindow class is : (partial code, the entire file is way too big)

int windcount;
class ChannelWindow
{
private:
      HWND wind;
      SOCKET theSock;
      DWORD dwUPKey, dwDWKey;
public:
      void OpenChatClient();
      static LRESULT CALLBACK ChanWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};



LRESULT CALLBACK ChannelWindow::ChanWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
      if (GetWindowLong(hwnd, GWL_USERDATA) == 0)
            return DefMDIChildProc(hwnd,msg,wParam,lParam);
      if (msg == 6666)      // Custom Window creation
      {
            // ... Code for creating all the other stuff such as buttons
      }
      else if (msg == WM_SIZE)      // Window resize      
      {
            //... Resize stuff
      }
      return DefMDIChildProc(hwnd,msg,wParam,lParam);
}



void ChannelWindow::OpenChatClient()
{
      CString Name;
      Name.Format( "CLIENT%d", windcount);
      WNDCLASSEX wc;
      wc.cbSize             = sizeof(WNDCLASSEX);      // size of class object (there are 2 types of class object)
      wc.style             = 0;      // style 0
      wc.lpfnWndProc       = ChanWndProc;      // the event handler function (ChanList in this case, see above)
      wc.cbClsExtra       = 1;      // additional options
      wc.cbWndExtra       = 2;      // additional options
      wc.hInstance       = Instance;      // the program? NULL! hahaha
      wc.hIcon             = LoadIcon(NULL, IDI_APPLICATION);      // icon to use
      wc.hCursor             = LoadCursor(NULL, IDC_ARROW);      // cursor to use
      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);      // background
      wc.lpszMenuName  = NULL;      // menu bar to use (yeh right...)
      wc.lpszClassName = Name;      // name of the class
      wc.hIconSm             = LoadIcon(NULL, IDI_APPLICATION);      // "small" icon to use (top left cornet one)

      if(!RegisterClassEx(&wc))      // register the class
      {
            MessageBox(NULL, "Client Registration Failed!", "Error!",      // error message
                  MB_ICONEXCLAMATION | MB_OK);
      }


      MDICREATESTRUCT mcs;
      HWND hChild;
      
      // set up the window options

      mcs.szTitle = chan;                              // the window title
      mcs.szClass = Name;      // name of the class
      mcs.hOwner  = GetModuleHandle(NULL);      // window owner
      mcs.x = mcs.cx = CW_USEDEFAULT;                  // default positions
      mcs.y = mcs.cy = CW_USEDEFAULT;                  // default positions
      mcs.style = MDIS_ALLCHILDSTYLES;            // style to use (all)
      hChild = (HWND)SendMessage(MDI, WM_MDICREATE, 0, (LONG)&mcs);      // tell MDI frame to make me a window
      if(!hChild)      // Error handler
      {
            MessageBox(MDI, "Error Loading WIndow", "Error!",      // Damn it!
                  MB_ICONEXCLAMATION | MB_OK);
      }
      wind=hChild;

      THREADSTRUCT* thingie;
      SetWindowLong(hChild, GWL_USERDATA, (LPARAM) &thingie);       // set this class in user data
      SendMessage(hChild, 6666,0,0);               // Tell it to create buttons etc
      return;
}



basicly it should register a class and create the window using a different class name each time (CLIENT<number>) that was because of my attempts to get the classes registered to use a different WndProc for each one (didn't work) it then sends a message to set "this" (pointer to this instance of the class) as the user data for the window, then sends a message 6666 to create the window (as the default create is executed before the user data is set i need to create after instead) it creates the window fine and the user data contains a pointer to the THREADSTRUCT that contains the pointer to the class, but the problem is if i try to use:

THREADSTRUCT*    ts = (THREADSTRUCT*)GetWindowLong(GetParent(hwnd), GWL_USERDATA);
then access the socket for example using ts->ThisThing->theSock

in theory i figure that should work, but it comes up with a memory write error - the source and target addresses are similar which would imply its not far off on the address it wants but it wont work
>> THREADSTRUCT*    ts = (THREADSTRUCT*)GetWindowLong(GetParent(hwnd), GWL_USERDATA);

Now I am not sure as to API as I have said, but if you put an ASSERT(ts) after this line you will probably find that ts is NULL, in which case it is no surprise that you get a memory access violation.

Recheck your code and ensure that you have correctly initialised what you are trying to cast here.

I will be honest now (as always) I am not the best person to ask and the Windows Programming area would have been a better place to ask this question methinks.  You may be best advised to post a pointer question in that TA to this question.

HTH
Avatar of _KM_

ASKER

its not NULL, the u checked that with a message box using:
CString temp
temp.Format("%d", ts) and got a number from it, and the address access is not trying to access address 00000000 its accessing somewhere near to the address of the function thats calling it
I am surprised that even compiles looking at it again, since theSock is a private member of the ChannelWindow instance (here 'ThisThing').  Break it down into smaller chunks also:

THREADSTRUCT*    ts = (THREADSTRUCT*)GetWindowLong(GetParent(hwnd), GWL_USERDATA);  // OK
ASSERT();

ChannelWindow* pChannelWindow = ts->ThisThing;
ASSERT(pChannelWindow);

SOCKET* pSocket = PChannelWindow->theSock;
ASSERT(pSocket);

But like I say the last line should not work since theSock is a private member...




Correction:

>>THREADSTRUCT*    ts = (THREADSTRUCT*)GetWindowLong(GetParent(hwnd), GWL_USERDATA);  // OK
>> ASSERT(ts );
Avatar of _KM_

ASKER

It did compile, although as i said that was a reduced version of the code so perhaps i missed something important out with the bits i removed that allows it to work...

I had anouther idea of simply creating an array of 1000 for each variable (ie, SOCKET theSock[1000] etc) and then use

// global defenition
int number;

// in the window create
SetWindowLong(clienthwnd, GWL_USERDATA, number);
number++;

//then to access the socket use
theSock[GetWindowLong(clienthwnd, GWL_USERDATA)]

in theory this should work for the first 1000 child windows opened - and if someone plans on using it more than that then i doubt they would mind restarting every now and then, heh

I am just in the middle of going through the 2000 lines of code for the MDI child and so far i'm down to 91 compile errors :-) in a few hours i'll get down to 0 and i'll be able to see if it works or not... heh
That would be an outrageous hack.  Did you try the ASSERTs that I suggested ?
Avatar of _KM_

ASKER

i got it working, and its not very memory intensive, probibly because the array items aren't initialised until they are used and are they destroyed when they're finished with, its using just over 4MB of RAM, it was at 3.9MB before so... lol

i did try the assert, but oh well...

i got it working now, thanks anyway :-)
Well, I am glad you got it sorted.