Link to home
Start Free TrialLog in
Avatar of TheMadManiac
TheMadManiac

asked on

how to pass argument with CreateWindow

hi,

I,m trying to pass a pointer along with the CreateWindow function, to initialize some data.

this whould be possible, and i thought the lParam was for that... but when i put a pointer in there, the CREATEDATA->lpCreateParams in the LPARAM of the WM_CREATE message point to garble...

well, my app. hangs because of this.

Can someone give me an example how to do this? I read that in NT4 this is done a bit differently?

os: NT4
compiler: VC++ 5.0
weird stuff: no MFC/OWL etc

thankers,

  TMM/Florizzzz
Avatar of nietod
nietod

Pass a pointer to the data in the last parameter.  (Not a pointer to a pointer to the data, however.)

In the WM_CREATE message the long parameter points to a CREATESTRUCT.  The lpCreateParams specifies the pointer (or other value you passed).

Example coming.
struct WndInitialData
{
// stuff.
};

 WndInitialData *IniDat = {//stuff };

CreateWindow(... IniDat);

*  *  *  *  *  *

case WM_CREATE:
{
   CREATESTRUCT *CrtStrPtr = (CREATESTRUCT *) LParam;
   WndInitalData *IniDat = (WndInitalData *) CrtStrPtr->lpCreateParams;

   IniDat->//stuff.
}

Post sample code/questions if more help is needed.
Avatar of TheMadManiac

ASKER

thanks nietod...

so i'm not insane.. phew.

that's almost exactly what i did:

char string[100];

// assign something to string here with strcpy or something

CreateWindow(....,(LPARAM)&str);

WM_CREATE:
{
CREATESTRUCT *cs=(CREATESTRUCT *)lParam;
char *str=(char *)cs->lpCreateParams;

// do something with str...
// uuh, error.. str = not string :(
}

the only thing different is that i don't use a struct, and i tell the compiler to give the address of a char array, instead of a pointer to a char array (which is the same)

i really don't know what i'm doing wrong here... difference between win95 & NT ?
If your char string[100] is declared inside of a function, then you will need to put static in front of it to move it from the stack (which gets cleaned every time this function is entered and exited), to the global data area. So change that statement to

  static char string[100];


Actually, I don't think thui is right.  (although it makes me nervous to dissagree with him.)  The WM_CREATE message is sent (not posted) from within the createwindow() procedure, so it should be processed before the CreateWindow(0 procedure returns, this the string can be stored on the stack.  Furthermore, "static" might not be the best way to fix the problem.  It would not work well if the procedure is called recursively as window creation procedures often are.

Actually, I think the problem might be what I hinted at originilly.  I think you are passing a pointer to a pointer, not just a pointer.  
Your sample code is a little messy, but I'll extract and "fix" the appropriate lines.  (Hopefully, not creating a problem that was not there)  You have:

char string[100];
CreateWindow(....,(LPARAM)&string);

"string " is already a pointer, sort of.  arrays and pointers are pretty much the same (in C).  That is "string" is a pointer to the first element of an array.  So when you take the address with "&" you are passing a pointer to a pointer.  Try

CreateWindow(....,(LPARAM)string);
or
CreateWindow(....,(LPARAM)&string[0]);

nietod's solution is right on this one. The Win32 SDK docs for CreateWindow specifically state:

"Before returning, CreateWindow sends a WM_CREATE message to the window procedure."

SendMessage processes the message before it returns, thus the stack variable string will be valid when the WM_CREATE handler is called.

and if you have
char string[100];
&string returns a char **, not a char *

Thanks, anichini.  
The documentation is wrong. There are at least 2 other messages that occur before WM_CREATE gets sent to the window proc: WM_NCCALCSIZE and WM_NCCREATE. The way to figure this out is to create a WNDPROC and print everything to a file and you'll see there are about 3 messages (I forgot the other one) that occur before WM_CREATE. So the WndProc doesn't get called only once during the creation process.

The sequence that gets sent to a wndproc:

Msg = 0x00000024 (WM_GETMINMAXINFO)
Msg = 0x00000081 (WM_NCCREATE)
Msg = 0x00000083 (WM_NCCALCSIZE)
Msg = 0x00000001 (WM_CREATE)



sorry if the sample code was messy :)

but, i tried everything,

 1 : making static            //nothing
 2 :(LPARAM)&string[0]        //nothing
 3 :(LPARAM)string            //nothing
 4 :(LPARAM)&string           //nothing
 5 : any combination of these // nothing
 6 : all of these under windows '95

these all yield the same result... string does not get passed correctly.

why is it always me with strange results.. it's driving me nutters :$

ASKER CERTIFIED SOLUTION
Avatar of FabMan
FabMan

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
to thui:

Yes, other messages are being sent by Createwindow() (and the exact messages change depending on the window being created.)  However the point I was making is that the WM_CREATE is always called from within the CreateWindow() call, so it can always access the local variables of the procedure that called CreateWindow().  My software works this way without a problem (in that area in any case).

To TheMadManiac (or can I just call you mad):

There is hope, I do this and I suspect that 90% of the windows programs out there do it.  You are just doing something a little wrong.  Try to fix the code the way I suggested and then, if it doesn't work, post it again.  

Another thing you could do is test a simpler case.  Rather than passing a pointer, pass a constant you would recognize and then look at the WM_CREATE message and see if the constant is in the lpCreateParams.

Just a note, since the data passed by the pointer is local data, you cannot "keep" the pointer.  The WM_CREATE handler can look at the data and use it, but not retain it forever.  If it needs to keep the data, it must copy it.

One other point.  Are you sure the problem is in the WM_CREATE message handler.  i.e. you are not getting a crash and assuming the problem is there or something right?  For example, there are messages that occur before the WM_CREATE.  If your code tries to use the data that will set by WM_CREATE, but hasn't been set yet...  I have a good way out of this (I've been there) if this is the problem.
The order is kinda weird here because FabMan and I were commenting/answering at the same time.

FabMan, seems to be right.  For some reason the SDK documentation has two entries for CREATESTRUCT.  They are almost word for word identical, but one mentions this and the other does not.  I think this must be a 16/32 bit issue.  My 16 bit program that uses this works fine on NT and win95 and there is no code to treat the two differently.  My 32 bit program has not been tested on NT--yet.

Does the API provide a way to access the parameter you want?  i.e. is there an API procedure that does the work of determining what operating system and then getting to the data.  That would be a good idea.  If not (To Mr. Maniac) you might want to write one to try to localize the logic.  I intend to.
I should mention.  That the points I made are still correct.  You don't want to pass a pointer to a pointer and the data (if local) cannot be "kept".   Just add to this FabMan's points on how to get the data.
nietod is right on his last comment. When you declare an array in C (such as char string[100]), you are creating an r-value pointer that already points to an area of 100 items. So you don't want to pass a pointer to that pointer (&string).
What a great response to my humble question.

I tried heaps of things, including the NT specific:

typedef struct
  {
  SHORT cbExtra;  // size ??? should be 4 me thinks
  char *str;      // a char * to a name or something
  }name;
etc.

Well, the result was ( i literally copied/pasted the online help, so no typo's or pointer errors etc) that the 'str' contained the NAME of the window.. which was NOT what i passed with the lParam.

Then, i tried to (sneaky me) use the title-part to pass along the string i needed in my creation. Well, when it was NOT static, the result was that lpzsName in the CREATESTRUCT pointed to garbish, while when i made it static it worked ok.. This would mean that the WM_CREATE is executed after the calling function is finished?

So now i use the title to pass along my string, and manually change it in the WM_CREATE message (which would be done anyways).

Not a very beautifull solution, but it works! AND this also should work on win '95, so no strange incompatible stuff when i try it under windows '95

btw, you can just call me Floris (which is my name) or mad (which is shorter :P) or maddy or whatever

And the application did not hang when i just threw away all that argument passing code.

And of course nietod is right about the pointer stuff.. but after doing a lot of things to make something work, you'll try about anything ..hehe

so in the end:

thui was right about the static part
nietod was probably right about everything nietod spoke, but somehow didn't work with me
Fabman was also quite right with his NT explanation, but again, this didn't work with me

well, problem not really solved, but at least i don't have to use a global variable anymore to initialize some data in the WM_CREATE message :) only thing now is i can't really change the title in the CreateWindow function, but i don't really mind.

A whole lot of thanks to all who helped me, and i'll just answer the question orso.. i dunno..
As remarked in the quoted section I previously sent to you, you might be using DWORD alignment for your application. This means that when trying to access a field next to the SHORT size, there might be problems. That is probably the reason why things didn't work for you. You have to make sure your structure is WORD aligned to properly access your data.
And it's your data that'll be next to the SHORT size field. A way to access it could be:

typedef struct
{
SHORT cbExtra;
char c[100]; // the first character of data
}name;

Later on, after retrieving the above structure, you would recover your string somewhat like this:

name UNALIGNED* lpName = (name UNALIGNED *)(((LPCREATESTRUCT) lParam)->lpcreateParams);

char *str = &(lpName->c);

Or something like that...
Oops! In the above comment, change:

char *str = &(lpName->c);

to:

char *str = lpName->c;
That not the good way to use createwindow, because you lose all income messages before wn_create
To get all message and forward them to you currently created class you must use the thread local storage (tls) with C
and with C++ you can declare a static TLS variable as _declspect(thread) MyClass* myCurrentCreatedClass;

With TLS you have a thread storage that ensure you never get conflict if you plan having a multithreaded application, else in
single thread app you can use a normal static without TLS

In your create window proc, instance you have to write :

MyClass::Create() {
       myCurrentCreatedClass = this;
       this->myHWnd = CreateWindow(blablabla);
}

In your static proc (you can have a static class proc also, but here it is global fonction) :

LRESULT myStaticProc(...) {
   MyClass*  myClass;

   if(myCurrentCreatedClass != NULL) { //you test if the current calling thread has set the class instance pointer
        myClass = myCurrentCreatedClass;
        myCurrentCreatedClass = NULL;
        SetWindowLongPtr(hWnd, GWL_WNDEXTRA + offset, (LONG)myClass);
   } else {
        myClass = (MyClass*)GetWindowLongPtr(hWnd, GWL_WNDEXTRA + offset);
   }
}

Where here, "offset" is the offset returned from GetClassInfoEx() and set by you when you call RegisterClassEx and you set
the CLASSINFOEX::cbWndExtra to the needed extra bytes for you window.

Instead of calling Get/SetWindowLongPtr(), you can also store all your window in a std::map in this case but you should implement locking mutex
to synchronize all threaded access to the std::map

That all...

;)