News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

General Windows question: using WM_CREATE

Started by NoCforMe, October 09, 2011, 08:00:56 PM

Previous topic - Next topic

NoCforMe

Another general, top-level Windows programming question.

Note: I'd like to get a general answer to this, not a lot of implementation details. Mainly interested in learning best programming practice, and why.

I see many code examples that use the WM_CREATE message to do things like create child windows, etc.

However, in my short career (as a n00b Windows programmer), I've found that it works just as well to do these things outside of the WindowProc() altogether:



(WinMain)

- create child window
- do other stuff to initialize window


=== OR ===


(WindowProc)

case WM_CREATE:
- create child window
- do other stuff to initialize window



I'd like to know if there are any real differences between these two methods. Now, before you tell me things I already know, I do know that WM_CREATE should be used when one wants to do initialization within the context of the window message handler (specifically, when one needs access to the values in wParam and lParam, for instance if one is tracking mouse positions, keyboard input, etc.). But it seems to me that many tasks, such as the one I gave in the example, are six of one and half a dozen of the other. In other words, it doesn't seem to matter whether I create a child window inside or outside  of the  window procedure.

One consideration for me is that I would rather not clutter up WindowProc() if something could just as well be done outside of it.

What do y'all have to say about this? Is there a best-recommended practice here? Or are Windows programmers like sheeple everywhere who just follow the examples they've been given?

I don't want to be a sheeple ...

Gunner

Do it whatever way you want to!  But don't come complaining here when your program doesn't work on some version of windows.

WM_CREATE/WM_INITDIALOG is the documented way of creating child controls.  You create your controls here because your window is NOT displayed yet.  Your window gets the messages, creates all controls, then it gets a WM_PAINT which draws all controls and the window.

~Rob (Gunner)
- IE Zone Editor
- Gunners File Type Editor
http://www.gunnerinc.com

NoCforMe

Quote from: Gunner on October 09, 2011, 08:14:29 PMDo it whatever way you want to!  But don't come complaining here when your program doesn't work on some version of windows.

Well, since you raised this potentially dire warning, can you tell me how this behavior varies between different flavors of Windows?

QuoteWM_CREATE/WM_INITDIALOG is the documented way of creating child controls.  You create your controls here because your window is NOT displayed yet.  Your window gets the messages, creates all controls, then it gets a WM_PAINT which draws all controls and the window.

With all due respect, I don't see how this compels one to put such creation code inside the WM_CREATE handler. The way I'm doing it, creating things in my mainline procedure, I'm creating stuff for a window that isn't displayed yet. I don't see a problem with this. If I want to force the window to be displayed (or the control to be updated), all I've got to do is call UpdateWindow() or some other procedure that forces repainting, re-drawing, etc. Of course, the painting code has to be in the WM_PAINT handler; I get that.

By the way, here's what MSDN has to say about this message:

Quote
Sent when an application requests that a window be created by calling the CreateWindowEx or CreateWindow function. (The message is sent before the function returns.) The window procedure of the new window receives this message after the window is created, but before the window becomes visible.

So I suppose one reason for putting stuff in the WM_CREATE handler would be if you want do something before the parent window becomes visible (may reduce flicker, etc.).

Tedd

You can create child windows as a result of WM_CREATE so they appear with your main (parent) window, i.e. all in one go. Other messages are delivered in the cascade of window creation (WM_CREATE is only one) so this is your chance to perform initialisation at a well defined point.
Or you can create them some time after, if you must. But not before, obviously.

If it's purely for neatness, add a call to CreateMyChildWindows in your WM_CREATE handler, and then do the fun stuff in there. I wouldn't really say that stuffing chunks of code into WinMain is making anything tidier, you're just moving the mess elsewhere.
There are reasons many things are done in a particular way; trying to be different for the sake of it will generally lead you into trouble in the future - this was Gunner's point: doing things in a non-standard way, when windows assumes you will, is going to result in your code being broken.
No snowflake in an avalanche feels responsible.

jj2007

Here is a standard example on how to create an edit control in the WM_CREATE handler. Question: Where would you get hWnd from before it was created?

         invoke CreateWindowEx, WS_EX_CLIENTEDGE, chr$("edit"), NULL,
            WS_CHILD or WS_VISIBLE or WS_BORDER\
            or ES_LEFT or ES_AUTOHSCROLL or ES_MULTILINE, 0, 0, 0, 0,
            hWnd, IdEdit, hInstance, NULL
         mov hEdit, eax                     ; we have created an edit window

sinsi

Another reason for doing it in WM_CREATE is if you have more than one main window, this ensures that the code for a particular window is in with that window's other code and not scattered everywhere.
Easier to modify window2.inc rather than go looking through myprog.asm (for example).
Light travels faster than sound, that's why some people seem bright until you hear them.

NoCforMe

@jj: To answer you, here's what I'm doing in one of my programs:



; Create our main window:
INVOKE CreateWindowEx, WS_EX_OVERLAPPEDWINDOW, ADDR MainClassName, ADDR MainDisplayName,
$mainWinAttrs, wX, wY, $mainWinWidth, $mainWinHeight,
NULL, NULL, hInst, NULL
MOV MainWinHandle, EAX
INVOKE ShowWindow, MainWinHandle, SW_SHOWNORMAL

; Create font window:
INVOKE CreateWindowEx, WS_EX_WINDOWEDGE, ADDR FontClassName, NULL,
$fontWinAttrs, $fontWinX, $fontWinY, $fontWinWidth, $fontWinHeight,
MainWinHandle, NULL, hInst, NULL
MOV FontWinHandle, EAX



You asked where I would get the handle to create a child: it's right there (MainWinHandle). If I wanted to, I could go ahead and populate the child with controls, using its handle. Do you see anything wrong with what I'm doing here?

I'm really not trying to go against the grain here, but at the same time, I can't see any compelling reason to put this inside my window procedure. Just because a million code examples out there do it that way doesn't mean that one must do it that way. I can see how some things should definitely go under WM_CREATE, but this doesn't seem to be one of them. (I'm always ready to be convinced otherwise, but not by the argument "that's the way everyone else does it". Sorry, I just don't buy that.)

NoCforMe

Quote from: sinsi on October 09, 2011, 10:32:52 PM
Another reason for doing it in WM_CREATE is if you have more than one main window, this ensures that the code for a particular window is in with that window's other code and not scattered everywhere.
Easier to modify window2.inc rather than go looking through myprog.asm (for example).

Well, sure, but that's more of a source file organization thing, not an underlying reason to do it one way or the other.

jj2007

Quote from: NoCforMe on October 09, 2011, 11:41:51 PM
@jj: To answer you, here's what I'm doing in one of my programs:
...
You asked where I would get the handle to create a child: it's right there (MainWinHandle)
...
Sorry, I just don't buy that.)

No problem. But check the order of events:

   invoke RegisterClassEx, addr wc
   deb 4, "We registered a class", eax
   invoke CreateWindowEx, WS_EX_CLIENTEDGE,
      addr ClassAppWin, addr txApp,
      WS_OVERLAPPEDWINDOW or WS_CLIPCHILDREN or WS_VISIBLE,
      AppX, AppY, AppWidth, AppHeight,
      wmNull, wmNull, wc.hInstance, wmNull
   mov hMainWnd, eax
   deb 4, "We have a handle, hooray", eax

WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   CASE WM_CREATE
   deb 4, "We are in the WM_CREATE handler now"

We registered a class   eax             1360404
We are in the WM_CREATE handler now
We have a handle, hooray        eax             854802

NoCforMe

Right, because WM_CREATE is sent before the call that caused it has returned.

So all is cool as long as we know that, correct? and don't try to use any handles that haven't been created yet, or send messages to things that don't exist yet.

I wanted to add one more comment: Even if I do things "by the book", it's still better to know why I'm doing them that way, rather than just following what I see others doing, don't you agree?

jj2007

Quote from: NoCforMe on October 10, 2011, 12:00:40 AM
Even if I do things "by the book", it's still better to know why I'm doing them that way, rather than just following what I see others doing, don't you agree?

Absolutely :U

sinsi

The point I was trying to make is if you have a main window and then another one that comes and goes the only way you can re-create that window again is when it is created, not in the "called once" code.
In the WM_CREATE code you usually make controls, load bitmaps/cursors etc., create fonts for the window. These things you get rid of in the WM_DESTROY code, to free resources.

Of course use your own method, as long as you realise what is going on behind the scenes. jj loves his masmbasic, I hate the idea of "invoke" much less macros. Each to their own...
Light travels faster than sound, that's why some people seem bright until you hear them.

NoCforMe

Quote from: sinsi on October 10, 2011, 07:57:20 AM
The point I was trying to make is if you have a main window and then another one that comes and goes the only way you can re-create that window again is when it is created, not in the "called once" code.

That makes sense. (Of course, one could just as easily put the creation code into a subroutine, and call that routine again to re-create the window/control/whatever; still no compelling reason to put it under WM_CREATE. Although you could put it there, and send that message to the window through its procedure to re-create them.)

QuoteOf course use your own method, as long as you realise what is going on behind the scenes. jj loves his masmbasic, I hate the idea of "invoke" much less macros. Each to their own...

Well, I look at it this way: I don't really like those things either, as I'm an older fart who prefers "pure assembler". However, having said that, I must say that INVOKE is a much neater way of doing calls, and reduces code clutter (as long as you realize what's going  on behind the scenes: that's why I enable generated code in my listing, so I can see what MASM is actually sticking in there). Macros? I've used them, probably will do so in the future; it's nice to be able to automate certain repetitive tasks, like defining identical data structures, etc. I used to use my @PUSH-@POP macros, which took an identical register list and handled them in the correct order, so I didn't have to draw lines on my source code to see if registers were getting restored correctly. Things like that.

The stuff that lets you create a program that looks like C or Basic? I'll probably never use them. But I'm glad they're available for those folks who prefer those styles of coding; diff'rent stroke for diff'rent folks and all that. I also appreciate it just as a technical achievement of jj's; it's a lot of fun to create tools like this. So it's all good.

dedndave

besides what has already been mentioned, i can think of a couple points about placing code in WM_CREATE

first, it is the earliest time that you can obtain the window handle
of course, the handle is returned by CreateWindowEx, but WM_CREATE knows the value before that   :bg

a major advantage of placing code in WM_CREATE is that you get to do things before the first WM_SIZE code is called
for example, you can a create scroll bar, setting the initial position and size to 0,0,0,0
your WM_SIZE code will then take care of positioning and sizing the scroll bar

in the following list, you will see the messages received by a particular WndProc at startup



this program has 6 children (created in WM_CREATE), 5 of them are controls, the other is a child window (hTxt)
the messages with a handle of 00000000 are not actual messages
they were inserted into the list to mark the beginning and end of WM_CREATE execution
the last message in the list is the first message handled by the message loop

NoCforMe

OK, now we're getting somewhere: Good reasons for doing it a certain way, with supporting evidence. Good going.

How did you capture those messages? Yourself, or did you use some external tool?