News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Re: problems with a window

Started by jj2007, August 26, 2014, 09:26:01 AM

Previous topic - Next topic

jj2007

Some people believe Windows should complete its job for the most common messages:

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
 
   .IF uMsg==WM_DESTROY
      invoke PostQuitMessage,NULL
   .ENDIF
   invoke DefWindowProc,hWnd,uMsg,wParam,lParam     
   ret

WndProc endp

Tedd

DefWindowProc does handle WM_DESTROY, as far it is concerned -- it returns zero to indicate the message was processed. However, what it doesn't do (for whatever strange reason) is post WM_QUIT to your message queue.
And so, the result is that your process doesn't exit because it's still running around in the message loop, not ending because GetMessage didn't receive zero (which it does for WM_QUIT.)
Presumably, you could indicate the exit yourself with a 'stillRunning' flag, and have your message loop end in response to that; but the standard way is to post WM_QUIT yourself (that's all PostQuitMessage does -- you could manually PostMessage(hwnd,WM_QUIT,0,0) instead.)
Potato2

dedndave

if the WM_DESTROY message is received in a child window, you may not want to terminate

i think Jochen grabbed WM_DESTROY as an example - perhaps a poor choice
but, he's talking about the way the return value is handled
he and i don't agree on that one - not the first time   :lol:

jj2007

Quote from: dedndave on August 27, 2014, 03:15:36 AM
i think Jochen grabbed WM_DESTROY as an example - perhaps a poor choice

WM_DESTROY is, as Tedd rightly elaborated ("what [DefWindowProc] doesn't do (for whatever strange reason) is post WM_QUIT to your message queue"), the only message that you must handle.

All other messages are normally handled by DefWindowProc, including those that can return something useful, like a brush or a zero flag indicating "don't process me".

Of course, often it works without passing by DefWindowProc. But can you be sure that e.g. WM_PAINT is "done" with your handler? If it works with your current version, does it imply it will work with all future Windows versions?

dedndave

each message is documented
what to return if the message has been "dealt with", and so on (many messages are different)
the documentation for DefWindowProc is not always as complete   :(
but, for many messages, an action is taken prior to exit

WM_PAINT is a special case, as DefWindowProc validates the update region and returns 0
if you have properly handled WM_PAINT, that should have been taken care of

if you have handled a message to your satisfaction, it is redundant to call DefWindowProc
it's similar to bloat-code, in that 1) it's the lazy way out, and 2) unnecessarily uses system resources

this is a little beyond the original poster's question, and we are in the campus
it won't hurt for him to become familiar with message handling
but, we don't need to confuse him with advanced discussion

qWord

[irony]
It is a bit strange that Microsoft spreads obviously wrong examples for decades in the documentation and with SDK examples...
Typical crap they are comming up with:

LRESULT CALLBACK MainWndProc(
    HWND hwnd,        // handle to window
    UINT uMsg,        // message identifier
    WPARAM wParam,    // first message parameter
    LPARAM lParam)    // second message parameter
{

    switch (uMsg)
    {
        case WM_CREATE:
            // Initialize the window.
            return 0;

        case WM_PAINT:
            // Paint the window's client area.
            return 0;

        case WM_SIZE:
            // Set the size and position of the window.
            return 0;

        case WM_DESTROY:
            // Clean up window-specific data objects.
            return 0;

        //
        // Process other messages.
        //

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}



:icon_confused:
[\irony]


MREAL macros - when you need floating point arithmetic while assembling!


qWord

jj,
why did you come up with that discussion? Because you think that
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   
   .IF uMsg==WM_DESTROY
      invoke PostQuitMessage,NULL
      xor eax,eax
   .ELSEIF uMsg==WM_CREATE
      xor eax,eax
   .ELSE
      invoke DefWindowProc,hWnd,uMsg,wParam,lParam     
   .ENDIF
   ret

WndProc endp
is wrong?
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

Quote from: qWord on August 27, 2014, 06:05:15 AM
jj,
why did you come up with that discussion? Because you think that
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   
   .IF uMsg==WM_DESTROY
      invoke PostQuitMessage,NULL
      xor eax,eax
   .ELSEIF uMsg==WM_CREATE
      xor eax,eax
   .ELSE
      invoke DefWindowProc,hWnd,uMsg,wParam,lParam     
   .ENDIF
   ret

WndProc endp
is wrong?

Yes, I think it's wrong, from a theoretical point of view:

All messages, if they don't have a handler, do pass DefWindowProc - Windows needs to do its job.

If you are giving special treatment to a message through e.g. a WM_CREATE handler, and you return a value (xor eax, eax / ret), it implies that you are not allowing Windows to perform the standard tasks required by WM_CREATE.

Of course, it might well be that the Windows developers are aware of the bad habit to use DefWindowProc only in a DEFAULT handler, and therefore do whatever needs to be done after the ret.

Of course, some messages are designed so that, if you choose to process them, a special value (e.g. a brush handle) should be returned (and therefore no invoke DefWindowProc, please).

But for the great majority, the safest assumption is that Windows may still have plans with that message, so put the invoke DefWindowProc after the Endsw. Here is an example that shows what may go wrong if you put it only into the Default handler.

This practice is a few bytes shorter, and a few nanoseconds slower; the latter is completely irrelevant, even for the frequently posted messages like WM_MOUSEMOVE.

BTW Raymond Chen supports this view, too: Even if you have code to handle a message, you're allowed to call DefWindowProc, because you were doing that anyway after all.

Gunther

Hi qWord,

I think we've beaten to death that point in the thread which Jochen mentioned, didn't we?

Gunther
You have to know the facts before you can distort them.

qWord

Quote from: jj2007 on August 27, 2014, 06:48:55 AMIf you are giving special treatment to a message through e.g. a WM_CREATE handler, and you return a value (xor eax, eax / ret), it implies that you are not allowing Windows to perform the standard tasks required by WM_CREATE.
Where are these standard tasks documented? You forget that this message is send by a function that creates the window - you imply that the developers split the code for windows creation in two function: CreateWindowEx and DefWindowProc. (the same as for WM_DESTROY which is send by DestroyWindow)

Quote from: jj2007 on August 27, 2014, 06:48:55 AMOf course, it might well be that the Windows developers are aware of the bad habit to use DefWindowProc only in a DEFAULT handler, and therefore do whatever needs to be done after the ret.
It is that exact these developers spread that "bad habit".

Quote from: jj2007 on August 27, 2014, 06:48:55 AMBut for the great majority, the safest assumption is that Windows may still have plans with that message
Any reference for that?

Quote from: jj2007 on August 27, 2014, 06:48:55 AMBTW Raymond Chen supports this view, too: Even if you have code to handle a message, you're allowed to call DefWindowProc, because you were doing that anyway after all.
I fully agree with that, but he only says that you must call DefWndProc if you do not process a message because of it parameters. A practical example that shows the point:
...
.elseif uMsg == WM_TIMER && wParam == IDT_XYZ ; <-- also test message parameter
...
.else
     invoke DefWindowProc,...
.endif


Quote from: Gunther on August 27, 2014, 07:17:01 AM
I think we've beaten to death that point in the thread which Jochen mentioned, didn't we?
no, as usual the discussion drift off.
MREAL macros - when you need floating point arithmetic while assembling!

qWord

I just reconsider your last point and think that might be more reliable to use the following construct as template or for examples:
   .repeat
      .if uMsg == WM_XYZ
         ...
         ; default behaviour wished
      .elseif uMsg == WM_FOO
         .if wParam == xyz   ; further checks
            ...
            mov eax,result
            .break         ; job done
         .endif
      .elseif uMSg == ...
         ...
         xor eax,eax
         .break
      .endif
      invoke DefWindowProc,...
   .until 1
   ret


Of course one must take care to not forget .break and accidentally call DefWndProc.
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

Quote from: qWord on August 27, 2014, 07:33:51 AMI fully agree with that, but he only says that you must call DefWndProc if you do not process a message because of it parameters

No, he says "Even if you have code to handle a message, you're allowed to call DefWindowProc, because you were doing that anyway after all".

And that is precisely what my example does: It calls DefWindowProc for all messages, because that is what Windows would be doing anyway after all.

BTW, both versions work fine. If you continue processing after the WM_CREATE handler (i.e. you pass by DefWindowProc, returning zero = success), then, after some hops, you end up at exactly the same location as if you had returned zero directly; e.g. for XP: 7E398734 mov ecx, fs:[18]. The only difference is some bytes of code less and some nanoseconds more.

QuoteI thought this was obvious.  I guess I was wrong.
;)

qWord

Quote from: jj2007 on August 27, 2014, 08:45:05 AMAnd that is precisely what my example does: It calls DefWindowProc for all messages, because that is what Windows would be doing anyway after all.
Sorry, but I can't find that point in the blog post. The final code that is shown does call DefWndProc only for cases that are not handled:

switch (uMsg) {
   case WM_CHAR:
      OnChar(...);
      return 0; // <--

   case WM_SOMETHING:
   if (wParam == 4) {
      DoSomething4(...);
    }
    else
       return DefWindowProc(...);
   
    return 0; // <--

default:
   return DefWindowProc(...);
}
MREAL macros - when you need floating point arithmetic while assembling!

qWord

Let me describe my point in other words:
With a window procedure we are describing the behavior of that window. So, when calling DefWindowProc for each message, we are always "overwriting" our own behavior-implementation with default behavior.
MREAL macros - when you need floating point arithmetic while assembling!