Solved

MDI Problem

Posted on 2004-08-01
14
437 Views
Last Modified: 2012-05-05
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
0
Comment
Question by:_KM_
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 6
14 Comments
 
LVL 19

Accepted Solution

by:
mrwad99 earned 60 total points
ID: 11687845
>> so quite simply, how the hell am i meant to get 2 MDI children (or more) that use different variables?

I take it you mean MDI child windows that reside within the one CFrameWnd.  In this case, simply add variables to the view or the document class and access them either via public methods or via a pointer to your view/document obtained via GetActiveView() or GetDocument() respectively.

HTH
0
 
LVL 1

Author Comment

by:_KM_
ID: 11688244
I created the windows via the win32 API and not via all that MFC stuff...
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 11688274
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
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Author Comment

by:_KM_
ID: 11692867
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
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 11695060
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.
0
 
LVL 1

Author Comment

by:_KM_
ID: 11695751
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
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 11696779
>> 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
0
 
LVL 1

Author Comment

by:_KM_
ID: 11696833
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
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 11696925
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...




0
 
LVL 19

Expert Comment

by:mrwad99
ID: 11696930
Correction:

>>THREADSTRUCT*    ts = (THREADSTRUCT*)GetWindowLong(GetParent(hwnd), GWL_USERDATA);  // OK
>> ASSERT(ts );
0
 
LVL 1

Author Comment

by:_KM_
ID: 11702940
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
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 11703578
That would be an outrageous hack.  Did you try the ASSERTs that I suggested ?
0
 
LVL 1

Author Comment

by:_KM_
ID: 11705772
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 :-)
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 11707818
Well, I am glad you got it sorted.
0

Featured Post

Get HTML5 Certified

Want to be a web developer? You'll need to know HTML. Prepare for HTML5 certification by enrolling in July's Course of the Month! It's free for Premium Members, Team Accounts, and Qualified Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

617 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question