Solved

MDI Problem

Posted on 2004-08-01
14
407 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_
  • 8
  • 6
14 Comments
 
LVL 19

Accepted Solution

by:
mrwad99 earned 60 total points
Comment Utility
>> 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_
Comment Utility
I created the windows via the win32 API and not via all that MFC stuff...
0
 
LVL 19

Expert Comment

by:mrwad99
Comment Utility
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
 
LVL 1

Author Comment

by:_KM_
Comment Utility
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
Comment Utility
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_
Comment Utility
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
Comment Utility
>> 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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 1

Author Comment

by:_KM_
Comment Utility
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
Comment Utility
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
Comment Utility
Correction:

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

Author Comment

by:_KM_
Comment Utility
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
Comment Utility
That would be an outrageous hack.  Did you try the ASSERTs that I suggested ?
0
 
LVL 1

Author Comment

by:_KM_
Comment Utility
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
Comment Utility
Well, I am glad you got it sorted.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…

763 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now