Solved

how to pass argument with CreateWindow

Posted on 1997-12-09
19
1,059 Views
Last Modified: 2016-11-10
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
0
Comment
Question by:TheMadManiac
  • 7
  • 4
  • 3
  • +3
19 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 1409233
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.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1409234
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.
0
 
LVL 1

Author Comment

by:TheMadManiac
ID: 1409235
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 ?
0
 
LVL 15

Expert Comment

by:Tommy Hui
ID: 1409236
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];


0
 
LVL 22

Expert Comment

by:nietod
ID: 1409237
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]);

0
 
LVL 2

Expert Comment

by:anichini
ID: 1409238
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 *

0
 
LVL 22

Expert Comment

by:nietod
ID: 1409239
Thanks, anichini.  
0
 
LVL 15

Expert Comment

by:Tommy Hui
ID: 1409240
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.

0
 
LVL 15

Expert Comment

by:Tommy Hui
ID: 1409241
The sequence that gets sent to a wndproc:

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



0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 1

Author Comment

by:TheMadManiac
ID: 1409242
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 :$

0
 

Accepted Solution

by:
FabMan earned 50 total points
ID: 1409243
There is a difference between Windows NT and Windows 95 as the CREATESTRUCT is concerned. In Windows NT, lpCreateParams does not point directly to your data, but to an area to a SHORT (16-bit) value specifying the size in bytes of your data, immediately followed by your data. I'm including below the reference for CREATESTRUCT so that you can see what I'm talking about:

 
The CREATESTRUCT structure defines the initialization parameters passed to the window procedure of an application.
 
typedef struct tagCREATESTRUCT { // cs
    LPVOID    lpCreateParams;
    HINSTANCE hInstance;
    HMENU     hMenu;
    HWND      hwndParent;
    int       cy;
    int       cx;
    int       y;
    int       x;
    LONG      style;
    LPCTSTR   lpszName;
    LPCTSTR   lpszClass;
    DWORD     dwExStyle;
} CREATESTRUCT;
 
 
Members
lpCreateParams
Points to data to be used for creating the window.
Windows NT: This member is the address of a SHORT (16_bit) value that specifies the size, in bytes, of the window creation data. The value is immediately followed by the creation data. For more information, see the following Remarks section.
hInstance
Identifies the module that owns the new window.
hMenu
Identifies the menu to be used by the new window.
hwndParent
Identifies the parent window, if the window is a child window. If the window is owned, this member identifies the owner window. If the window is not a child or owned window, this member is NULL.
cy
Specifies the height of the new window, in pixels.
cx
Specifies the width of the new window, in pixels.
y
Specifies the y-coordinate of the upper left corner of the new window. If the new window is a child window, coordinates are relative to the parent window. Otherwise, the coordinates are relative to the screen origin.
x
Specifies the x-coordinate of the upper left corner of the new window. If the new window is a child window, coordinates are relative to the parent window. Otherwise, the coordinates are relative to the screen origin.
style
Specifies the style for the new window.
lpszName
Points to a null-terminated string that specifies the name of the new window.
lpszClass
Points to a null-terminated string that specifies the class name of the new window.
dwExStyle
Specifies the extended style for the new window.
Remarks
Windows NT: Referring to the lpCreateParams member of the CREATESTRUCT structure, because the pointer may not be DWORD aligned, an application should access the data using a pointer that has been declared using the UNALIGNED type, as shown in the following example:
 
typedef struct tagMyData {
    . . .;      // define creation data here
} MYDATA;
 
typedef struct tagMyDlgData {
    SHORT    cbExtra;
    MYDATA  myData;
} MYDLGDATA, UNALIGNED *PMYDLGDATA;
 
PMYDLGDATA pMyDlgdata =
    (PMYDLGDATA) (((LPCREATESTRUCT) lParam)->lpcreateParams);
 
 
See Also
CreateWindow, CreateWindowEx

 

0
 
LVL 22

Expert Comment

by:nietod
ID: 1409244
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.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1409245
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.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1409246
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.
0
 

Expert Comment

by:FabMan
ID: 1409247
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).
0
 
LVL 1

Author Comment

by:TheMadManiac
ID: 1409248
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..
0
 

Expert Comment

by:FabMan
ID: 1409249
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...
0
 

Expert Comment

by:FabMan
ID: 1409250
Oops! In the above comment, change:

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

to:

char *str = lpName->c;
0
 

Expert Comment

by:fragmaster fragmaster
ID: 41882009
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...

;)
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

In this article, I will show how to use the Ribbon IDs Tool Window to assign the built-in Office icons to a ribbon button.  This tool will help us to find the OfficeImageId that corresponds to our desired built-in Office icon. The tool is part of…
What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

707 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

17 Experts available now in Live!

Get 1:1 Help Now