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

Quote from: Tedd on August 29, 2014, 03:29:50 AM
Quote
...for the great majority [of messages], the safest assumption is that Windows may still have plans with that message, so put the invoke DefWindowProc after the Endsw.
No. This is wrong. This is where the actual argument is. The only purpose of DefWindowProc is handle those messages which you do not fully handle yourself. There is no secret processing that happens afterward. If you have processed it fully, that is it - there is no need to pass it on - it has been fully processed. The end. Fin.

Test it on the example attached above:

  CASE WM_SYSKEYDOWN
    .if wParam==VK_F1
          MsgBox 0, "Click OK, then press Alt F4 to close this app", "Hi", MB_OK
    .endif
    xor eax, eax      ; return FALSE
    inc eax      ; or return TRUE
    ret      ; we don't let DefWindowProc do its job


You may return TRUE or FALSE, it won't change the result: You can't close that window with Alt F4 any more. So there is "secret processing" happening, and DefWindowProc is not just there to "notify" the programmer. Who in this example would have to ret rv(DefWindowProc, ...) to make Alt F4 work again.

Quote from: Tedd on August 29, 2014, 03:29:50 AMWM_CREATE is a 'notification' that the window has been created - to allow you to perform initialisation. That is all. There are no standard tasks required.

That seems correct for WM_CREATE, but there are many other messages - like WM_SYSKEYDOWN - that don't fall into this category; they need their default processing, unless you explicitly want to block that by returning something.

I guess we'll now start arguing about the meaning of "fully" 8)

@Hutch: Thanks for moving this :t

Gunther

Moving was a good decision, Hutch.  :t

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

Tedd

Quote from: jj2007 on August 29, 2014, 04:25:26 AM
...
You may return TRUE or FALSE, it won't change the result: You can't close that window with Alt F4 any more. So there is "secret processing" happening, and DefWindowProc is not just there to "notify" the programmer. Who in this example would have to ret rv(DefWindowProc, ...) to make Alt F4 work again.
There are two function calls in the standard message-loop, the second of which is TranslateMessage - whose job is to convert sequences of WM_KEYDOWN+WM_KEYUP into WM_CHAR messages, and WM_SYSKEYDOWN+WM_SYSKEYUP into WM_SYSCHAR messages. (ALT+?? combinations produce WM_SYSCHAR messages.)
So, if you eat WM_SYSKEYDOWN messages, guess what happens - no WM_SYSCHAR message, and so no ALT+F4 notification. In your inadequate example, you're taking ALT+F1 for yourself, but then discarding all other ALT+?? keys (which includes F4). In other words, you're not fully handling WM_SYSKEYDOWN.
Of course it doesn't work. And this is why you need to pass those unhandled messages to DefWindowProc. You are only handling it for ALT+F1 (which should not be passed to DefWindowProc), while you're not handling any others (which should then be passed to DefWindowProc).
Pass the unhandled messages to DefWindowProc and it will work correctly.

Quote
Quote from: Tedd on August 29, 2014, 03:29:50 AMWM_CREATE is a 'notification' that the window has been created - to allow you to perform initialisation. That is all. There are no standard tasks required.

That seems correct for WM_CREATE, but there are many other messages - like WM_SYSKEYDOWN - that don't fall into this category; they need their default processing, unless you explicitly want to block that by returning something.
It was meant only for WM_CREATE, since you were suggesting there may be secret processing going on that required calling DefWindowProc after you had already handled the message yourself. Some messages are only for notification, while others do indeed require processing (which you either do yourself, or you pass it to DefWindowProc, but still not both.)

Quote
I guess we'll now start arguing about the meaning of "fully" 8)
I would hope the meaning is obvious.
As with the WM_SYSKEYDOWN message, if there are many possible cases (all possible key-presses) and you only handle a single one, then that is clearly not 'fully' - you have not handled 100% of the cases; pass the remaining cases to DefWindowProc.
Potato2

dedndave

WM_SYSKEYDOWN is a special case
the OS processes the keys in a pre-defined way if DefWndProc is called
however, the message is sent so that a window proc may bypass the "standard" (default) behaviour, if desired
otherwise, you would probably handle one of the other key-related messages

hutch--

Is it any secret that key strokes are processed directly in the message loop ALA Petzold 1995 ? You do not see system defined accelerators in the WndProc, you see them directly in the message loop, that is how Win32 was designed in the first place and to keep it backwards compatible, it still works that way. It is done to address a 1 character lag that often occurs if you try and process keystrokes in the WndProc or a control subclass.

RE: WM_CREATE, it is only ever called once, during the creation of the Window ALA CreateWindowEx in Win32 so if you can see some advantage in stacking messages in an order in a switch block, you put it down near the end as it has already been called that once at window creation. Truly some of you guys needed to have written Win16 Windows code in a co-operative multitasking environment where the slightest glitch crashed the OS (the BLACK screen of death really told you that you had messed up).

jj2007

Quote from: Tedd on August 29, 2014, 07:00:02 AMSo, if you eat WM_SYSKEYDOWN messages, guess what happens - no WM_SYSCHAR message, and so no ALT+F4 notification. In your inadequate example, you're taking ALT+F1 for yourself, but then discarding all other ALT+?? keys (which includes F4). In other words, you're not fully handling WM_SYSKEYDOWN.
Of course it doesn't work. And this is why you need to pass those unhandled messages to DefWindowProc. You are only handling it for ALT+F1 (which should not be passed to DefWindowProc), while you're not handling any others (which should then be passed to DefWindowProc).
Pass the unhandled messages to DefWindowProc and it will work correctly.

Tedd, YOU ARE ABSOLUTELY RIGHT!!! 100%!!!

The only disagreement is whether it should be done the ugly C way:

WndProc proc hWnd, uMsg, wParam, lParam
  SWITCH uMsg
  CASE WM_SYSKEYDOWN
    .if wParam==VK_F1
          MsgBox 0, "Click OK", "Hi", MB_OK
          ret      ; we don't pass by DefWindowProc, we RETURN
    .else
          ; because of the DEFAULT below, you will need this shitty else branch for all messages that are only "partly" processed:
          invoke DefWindowProc, hWnd, uMsg, wParam, lParam
          ret
    .endif
  CASE WM_DESTROY
    invoke PostQuitMessage, NULL
    xor eax, eax      ; "if an app processes this message, it should return zero"
    ret
  DEFAULT      ; C style...
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam
  ENDSW
  ret
WndProc endp


... or the elegant ASM way:

WndProc proc hWnd, uMsg, wParam, lParam
  SWITCH uMsg
  CASE WM_SYSKEYDOWN
    .if wParam==VK_F1
          MsgBox 0, "Click OK", "Hi", MB_OK
          ret      ; we don't pass by DefWindowProc, we RETURN
    .endif
  CASE WM_DESTROY
    invoke PostQuitMessage, NULL      ; next step is DefWP, which will set eax to zero
  ENDSW
  ; process all messages that did NOT return above
  invoke DefWindowProc, hWnd, uMsg, wParam, lParam
  ret
WndProc endp


Of course, somebody will now remark that WM_DESTROY was processed, and therefore should not pass DefWindowProc; but Raymond Chen says that Even if you have code to handle WM_DESTROY, you're allowed to call DefWindowProc, because you were doing that anyway after all.

P.S.: If you think this is nitpicking, try this thread among C programmers (especially novacain's comments) ;)

P.P.S.: Below an explanation why Raymond Chen is so adamant about using DefWindowProc.

Tedd

Quote from: jj2007 on August 29, 2014, 12:34:23 PM
Tedd, YOU ARE ABSOLUTELY RIGHT!!! 100%!!!
Yes, I know.

Quote
The only disagreement is whether it should be done the ugly C way:

... or the elegant ASM way:
Or, do it the sensible and correct way.
WndProc proc hWnd, uMsg, wParam, lParam
  SWITCH uMsg
  CASE WM_SYSKEYDOWN
    .if wParam==VK_F1
          MsgBox 0, "Click OK", "Hi", MB_OK
          ;fall through to return zero
    .else
          jmp defwndproc
    .endif
  CASE WM_DESTROY
    invoke PostQuitMessage, NULL
    ;fall through to return zero
  DEFAULT
    defwndproc:
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam
    ret
  ENDSW
  xor eax,eax  ;return zero to indicate message processed
  ret
WndProc endp

(Messages which are handled and require a non-zero return can explicitly return instead of falling through.)

Quote
Of course, somebody will now remark that WM_DESTROY was processed, and therefore should not pass DefWindowProc; but Raymond Chen says that Even if you have code to handle WM_DESTROY, you're allowed to call DefWindowProc, because you were doing that anyway after all.
Yes, I will stay that - because it's correct.
As I have already explained very clearly -- not that you're able to read, apparently -- that is not what Raymond Chen says.
Quote from: Tedd on August 28, 2014, 10:30:21 PM
"you were doing that anyway" means "you were doing that anyway [when you were not handling the message at all]", it does not mean "you were doing that anyway [when you had already fully handled the message and then returned so that windows could process the message yet again]"

Quote
P.P.S.: Below an explanation why Raymond Chen is so adamant about using DefWindowProc.
1. He's not;
2. What that's actually demonstrating is that you can't simply return zero as a shortcut for not handling messages, and should call DefWindowProc for the situations you don't handle.


This has been amusing, but it's quite obvious you're either ignoring the main points of my explanations, or you're just unable to understand. Either way, it's pointless to continue replying.

There is quality software to be written; enjoy your weekend! 8)
Potato2

jj2007

Quote from: Tedd on August 30, 2014, 01:04:58 AM
    .else
          jmp defwndproc   ; There is quality software to be written  ;)
    .endif

  CASE WM_DESTROY
    invoke PostQuitMessage, NULL
    ;fall through to return zero <- there is no fall through in assembler (there was no fall through in any language until K&R messed it up)
  DEFAULT
    defwndproc:
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam
    ret
  ENDSW
  xor eax,eax  ;return zero to indicate message processed
  ret
WndProc endp
...
enjoy your weekend! 8)

Same to you :icon14:

Tedd

Really - this is the best you could come up with?

Quote from: jj2007 on August 30, 2014, 02:35:31 AM
    .else
          jmp defwndproc   ; There is quality software to be written  ;)
    .endif
It's a straightforward modification to fit with the lacking example you posted, not the way I would necessarily implement it.

Quote
  CASE WM_DESTROY
    invoke PostQuitMessage, NULL
    ;fall through to return zero <- there is no fall through in assembler (there was no fall through in any language until K&R messed it up)
The CASE macro is implemented as IF-statements, it's not a real 'case' statement. I was clearly referring 'falling through' to exit the case (actually a jump to the ENDIF) and thus return zero; if it were 'real' fall-through, execution would continue in the next case-block, not exiting to return zero.

:eusa_boohoo:
Potato2

jj2007

Quote from: Tedd on August 30, 2014, 03:50:18 AMI was clearly referring 'falling through' to exit the case

You mean you clearly intended to add the red line below, and then were distracted by something more important?

    .else
          jmp defwndproc   ; in C speak, this elegant solution means "the absence of a break statement" ;-)
    .endif
  CASE WM_DESTROY
    invoke PostQuitMessage, NULL
    jmp defwndproc     ;fall through to return zero
  DEFAULT
    defwndproc:
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam

qWord

Quote from: Tedd on August 30, 2014, 03:50:18 AM
Really - this is the best you could come up with?
he just needs that to whitewash his original calim, which was the reason for the whole discussion.
MREAL macros - when you need floating point arithmetic while assembling!

Tedd

Quote from: Tedd on August 27, 2014, 09:34:12 PM
1. If you do not handle a message at all, pass it to DefWindowProc;
2. If you do handle a message and complete all processing you require, do not pass it to DefWindowProc;
3. If you handle a message, but in some cases it will require further processing, pass it to DefWindowProc in those cases only.

Quote from: jj2007 on August 29, 2014, 12:34:23 PM
Tedd, YOU ARE ABSOLUTELY RIGHT!!! 100%!!!


:eusa_boohoo:
Potato2

Gunther

And what's the essence of the entire elongated discussion?

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

hutch--

Just as an aside to such weighty issues, long ago I remember reading a lament from one of K&R about how the C switch block worked and the need for "break". Its something you get used to but its a pain switching from one language to another, with C I often forget the "break" and have to go back and fix it (lucky I write very little C these days) and with Basic I usually forget to put the "Then" operator after the starting line of an "If" block.

Now given that I don't suffer any bias at all over languages  :biggrin: MASM got it right where the rest stuffed it up with their purple prose, wordy waffle and general imprecision. Real men[tm] code their own in mnemonics.  :P

jj2007

Quote from: hutch-- on August 31, 2014, 11:52:31 AM
Just as an aside to such weighty issues, long ago I remember reading a lament from one of K&R about how the C switch block worked and the need for "break". Its something you get used to but its a pain switching from one language to another, with C I often forget the "break"...

You are in good company there, Hutch - remember the Apple's most famous source code thread?

But indeed, Masm is very flexible, just add an equate like goto equ <jmp>, and you can write beautiful code in 'Apple C style' like

  SWITCH uMsg
  CASE WM_SYSKEYDOWN
    .if wParam==VK_F1
          MsgBox 0, "Click OK", "Hi", MB_OK
          ;fall through to return zero
    .else
          goto defwndproc;
    .endif
  CASE WM_DESTROY
    invoke PostQuitMessage, NULL
  DEFAULT
    defwndproc:
    invoke DefWindowProc, hWnd, uMsg, wParam, lParam
;)