News:

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

Main Menu

Simple template for creating a window

Started by jj2007, October 14, 2013, 08:51:01 PM

Previous topic - Next topic

jj2007

Quote from: dedndave on October 15, 2013, 02:33:02 AM
if you write the code so that everything goes through DefWindowProc,
it may work with the limited set of messages you are processing
at some point, that's not going to be desirable

Dave,

There is exactly one message that cannot be handled properly by DefWindowProc: WM_DESTROY. All others will, if they do not receive special treatment by your hand-written code, eventually pass DefWindowProc.

If you have messages that must return something different, well, let them return it...

Here is a variant that avoids the EPILOGUE by jumping to a RetZero label:
  SWITCH uMsg
  ...
  CASE WM_CLOSE
     MsgBox 0, "Sure to close?", "Hi", MB_YESNO
     cmp eax, IDNO
     je RetZero
  CASE WM_DESTROY
   invoke PostQuitMessage, NULL      ; quit after WM_CLOSE
  ENDSW
  invoke DefWindowProc, hWnd, uMsg, wParam, lParam   ; default processing
@@:     ret
RetZero:  xor eax, eax
   jmp @B
WndProc endp


P.S.: I remember another thread (which I can't find right now here) where we timed the cost of "uses esi edi ebx".

RuiLoureiro

Quote from: hutch-- on October 15, 2013, 03:43:40 AM

Now just for example, this is register preservation at a message level.


  .elseif uMsg == WM_COMMAND
    .if wParam == 1234
      push ebx
      push esi
      push edi
    ; write the complex code here
      pop edi
      pop esi
      pop ebx
    .endif

  .elseif uMsg == WM_whatever_comes_next


Hutch,
            i would like to understand why we need to preserve
            registers like ebx, esi and edi in this case, just
            before returning to the OS
            (we exit with xor eax, eax and ret
            or
            invoke DefWindowProc,... and ret) ?

            I dont preserve registers and i never saw any problems.
            When the OS calls the WndProc, it should preserve what it needs.
            This is what i think.
           
           

jj2007

Quote from: RuiLoureiro on October 15, 2013, 05:18:02 AM
            I dont preserve registers and i never saw any problems.
            When the OS calls the WndProc, it should preserve what it needs.

I fully agree, the OS should preserve them :t Unfortunately, the documentation says otherwise, but nobody stops you to use, for your private (=non-public) code
WndProc proc hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
  xor esi, esi
  xor edi, edi
  xor ebx, ebx
  SWITCH uMsg

Happy testing ;)

RuiLoureiro

Jochen,
         «Unfortunately, the documentation says otherwise»

I dont believe that the OS do something like this

mov         esi, X
mov         edi, Y
...
invoke      WndProc, hWnd, Msg, wParam, lParam
; and now it uses esi and edi
; just supposing esi=X and edi=Y.

Could you show me an example ?

RuiLoureiro

Jochen,
         «Unfortunately, the documentation says otherwise»

          Could you show me that documentation ?

Gunther

Hi RuiLoureiro,

Quote from: RuiLoureiro on October 15, 2013, 06:40:20 AM
          Could you show me that documentation ?

Here is one example documentation from MS.

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

jj2007

Interesting, Gunther :t

Here is a special WndProc - exe attached:

WndProc proc hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
  inc msgCount        ; OPT_Susy Console        ; override autodetect
  .if debMsg()
        deb 4, "# msg #", chg:msgCount, esi, edi, ebx
  .endif
  mov esi, 111111111        ; Win XP couldn't care less...
  mov edi, 222222222        ; ... what you do with the regs
  mov ebx, 333333333        ; but watch out what you get in the next round...

  SWITCH uMsg
  ...


EDIT: MiniWin.exe requires the two WM_ files in the archive - extract all to a temp folder.

dedndave

i try to avoid LOCAL's in WndProc   :P

    .if uMsg==WM_PAINT
        INVOKE  wmPaint,hWnd
        xor     eax,eax

    .elseif......


wmPaint PROC USES EBX ESI EDI hWnd:HWND

    LOCAL   ps      :PAINTSTRUCT

    INVOKE  BeginPaint,hWnd,addr ps
;
    INVOKE  EndPaint,hWnd,addr ps
    ret

wmPaint ENDP

qWord

#23
some Windows versions seems to check if ESI, EDI and EBX has been violated by the window procedure and correct that. More worse, since windows 7 (or maybe Vista), there is exception handler around the WndProc** that silently catch all exceptions thus you have no chance to realized hard errors (unless you run the corresponding code in a debugger or disable that mechanism).
However this does not apply to all callbacks :include \masm32\include\masm32rt.inc
.code
FOR reg,<esi,edi,ebx>
    enum_&reg proc hWnd:HWND,lParam:LPARAM
        xor reg,reg ; let's break the WinABI ;-)
        mov eax,TRUE
        ret
    enum_&reg endp
ENDM

main proc
    invoke EnumWindows,enum_esi,0
    invoke EnumWindows,enum_edi,0
    invoke EnumWindows,enum_ebx,0
    inkey
    exit
main endp
end main
For Win7 and XP, ESI is used as a pointer thus the app. crashes

EDIT: ** applies to Window's x64 versions
MREAL macros - when you need floating point arithmetic while assembling!

jj2007

Quote from: qWord on October 15, 2013, 07:48:53 AM
some Windows versions seems to check if ESI, EDI and EBX has been violated by the window procedure and correct that.
The checking must cost more cycles than simply preserving these three regs...

QuoteMore worse, since windows 7 (or maybe Vista), there is exception handler around the WndProc that silently catch all exceptions
Wow, that is of course a recipe for disaster :greensml:

P.S.: I added two required files to the MiniWin archive above in the reply to Gunther.

qWord

I forgot to mention that the described exception handler applies to Window's x64 versions: Exceptions that are thrown from an application that runs in a 64-bit version of Windows are ignored
Here a cute macro that can be used to disable the EH:
Code ("UsermodeExceptionPolicy.inc") Select
; see Microsoft knowledge base entry KB 976038
; http://support.microsoft.com/kb/976038/en-us
IFNDEF PSETPROCESSUSERMODEEXCEPTIONPOLICY
    ;PROCESS_CALLBACK_FILTER_ENABLED EQU 1
    SETPROCESSUSERMODEEXCEPTIONPOLICY typedef proto dwFlags:DWORD
    GETPROCESSUSERMODEEXCEPTIONPOLICY typedef proto lpFlags: ptr DWORD
    PSETPROCESSUSERMODEEXCEPTIONPOLICY typedef ptr SETPROCESSUSERMODEEXCEPTIONPOLICY
    PGETPROCESSUSERMODEEXCEPTIONPOLICY typedef ptr GETPROCESSUSERMODEEXCEPTIONPOLICY
    _DATA SEGMENT
        UICB_throws_m db "kernel32.dll",0
        UICB_throws_s db "SetProcessUserModeExceptionPolicy",0
        UICB_throws_g db "GetProcessUserModeExceptionPolicy",0
    _DATA ENDS
ENDIF

UI_callback_throws macro exception_if_fails:=<>
    invoke GetModuleHandleA,OFFSET UICB_throws_m
    .if eax
        push eax
        invoke GetProcAddress,eax,OFFSET UICB_throws_s
        .if eax
            pop edx
            push eax
            invoke GetProcAddress,edx,OFFSET UICB_throws_g
            .if eax
                push 0
                mov edx,esp
                invoke PGETPROCESSUSERMODEEXCEPTIONPOLICY ptr eax,edx
                pop edx
                .if eax
                    and edx,NOT 1
                    pop eax
                    invoke PSETPROCESSUSERMODEEXCEPTIONPOLICY ptr eax,edx
                    IFNB <exception_if_fails>
                        .if !eax
                            int 3
                        .endif
                    ENDIF
                .else
                    IFNB <exception_if_fails>
                        int 3
                    ELSE
                        add esp,4
                    ENDIF
                .endif
            .else
                IFNB <exception_if_fails>
                    int 3
                ELSE
                    add esp,4
                ENDIF
            .endif
        .else
            IFNB <exception_if_fails>
                int 3
            ELSE
                add esp,4
            ENDIF
        .endif
    IFNB <exception_if_fails>
    .else
        int 3
    ENDIF
    .endif
endm


Quote from: jj2007 on October 15, 2013, 08:10:42 AMThe checking must cost more cycles than simply preserving these three regs...
lets hope MS will remove that in future versions...  :badgrin:
MREAL macros - when you need floating point arithmetic while assembling!

MichaelW

Quote from: RuiLoureiro on October 15, 2013, 05:58:09 AM
Jochen,
I dont believe that the OS do something like this

mov         esi, X
mov         edi, Y
...
invoke      WndProc, hWnd, Msg, wParam, lParam
; and now it uses esi and edi
; just supposing esi=X and edi=Y.

So you believe that Windows preserves EBX, ESI, and EDI around calls to callbacks? I can recall seeing problems with callbacks not preserving EBX, ESI, or EDI, but note that this was for callbacks coded in assembly. The vast majority of code, inside Windows and outside, is compiler generated. Since any good compiler automatically preserves these registers in any procedure that uses them, there is no need for the caller to preserve these registers around calls to callbacks. The attachment contains a test, compiled with the MSVC++ Toolkit 2003 compiler (the only Microsoft compiler that I currently have set up). The idea behind the code is to determine if the compiler uses EBX, ESI, or EDI, and if so, or if these registers are used in inline assembly, does the compiler generate code to preserve these registers within the procedure, and around a call to an external procedure. I was able to get the compiler to use ESI, but in the time I had to spend on it I could not get it to use EBX or EDI.

Well Microsoft, here's another nice mess you've gotten us into.

habran

No kidding with nonvolatile registers :exclaim:
not preserved nonvolatile registers makes a programmer a charlatan :icon13:
Cod-Father

Vortex

A simple binary resource based window template :

Compile the resource script with rc.exe

Using Resource Hacker, save the Resource as binary file

Convert the binary file to a sequence of readable values with bin2dbex.exe

include DlgBox.inc

.data

DlgBox  db 1,0,255,255,128,0,26,1,0,0,0,0,68,8,202,16
        db 0,0,20,0,10,0,200,0,120,0,0,0,0,0,68,0
        db 105,0,97,0,108,0,111,0,103,0,32,0,98,0,111,0
        db 120,0,0,0,12,0,188,2,0,1,83,0,121,0,115,0
        db 116,0,101,0,109,0,0,0

.code

start:

    invoke  GetModuleHandle,0
    invoke  DialogBoxIndirectParam,eax,ADDR DlgBox,0,ADDR DlgProc,0
    invoke  ExitProcess,eax

DlgProc PROC hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

    .IF uMsg==WM_CLOSE

        invoke    EndDialog,hWnd,0

    .ELSE

        xor eax,eax
        ret

    .ENDIF

    mov    eax,1
    ret

DlgProc ENDP

END start



jj2007

Quote from: MichaelW on October 15, 2013, 05:55:09 PMI was able to get the compiler to use ESI, but in the time I had to spend on it I could not get it to use EBX or EDI.

Interesting, Michael. However, Windows itself uses esi edi ebx like hell - see attachment to reply #21. All three regs have a different value every time they enter the WndProc, with ebx always being zero. This is XP SP3, Win7 could be different. In any case, preserving the three regs with uses esi edi ebx makes coder's life easier and definitely cannot hurt performance, given that a) apps spend only a negligible amount of time in the WndProc (otherwise you'll see CPU usage go up) and b) even a "short" message costs thousands of cycles. I am a great fan of timings and squeezing the last cycle out of an algo, but being stingy with uses in a WndProc simply makes no sense IMHO.