The MASM Forum

General => The Workshop => Topic started by: jj2007 on October 14, 2013, 08:51:01 PM

Title: Simple template for creating a window
Post by: jj2007 on October 14, 2013, 08:51:01 PM
Many new members are eager to see their first window in action. Here is a Masm32 template containing only the really essential elements. The code creates a window with a menu and an edit control, and is less than 70 lines long.

The attachment contains two more sources for "Hello World" console and MessageBox apps.

; *** minimalistic code to create a window; see also \masm32\examples\exampl01\generic\generic.asm and Iczelion tutorial #3 ***
include \masm32\include\masm32rt.inc        ; set defaults and include frequently used libraries (Kernel32, User32, CRT, ...)

.data        ; initialised data section
txClass        db "MyWinClass", 0                ; class name, will be registered below
wcx        WNDCLASSEX <WNDCLASSEX, CS_HREDRAW or CS_VREDRAW, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, 4>

.data?        ; uninitialised data - use for handles etc
hEdit        dd ?

.code
WinMain proc uses ebx
LOCAL msg:MSG
  mov ebx, offset wcx
  wc equ [ebx.WNDCLASSEX]
  mov wc.hInstance, rv(GetModuleHandle, 0)
  mov wc.hIcon, rv(LoadIcon, NULL, IDI_APPLICATION)
  mov wc.hIconSm, eax
  ; mov wc.hCursor, rv(LoadCursor, NULL, IDC_ARROW)        ; not needed
  invoke RegisterClassEx, addr wc                ; the window class needs to be registered
  invoke CreateWindowEx, NULL, wc.lpszClassName, chr$("Hello World"),        ; window title
    WS_OVERLAPPEDWINDOW or WS_VISIBLE,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,        ; x, y, w, h
    NULL, NULL, wc.hInstance, NULL
  .While 1
        invoke GetMessage, ADDR msg, NULL, 0, 0
        .Break .if !eax
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
  .Endw
  exit msg.wParam
WinMain endp

WndProc proc uses esi edi ebx hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
  SWITCH uMsg
  CASE WM_CREATE
        mov esi, rv(CreateMenu)                ; create the main menu
        mov edi, rv(CreateMenu)                ; create a sub-menu
        invoke AppendMenu, esi, MF_POPUP, edi, chr$("&File")        ; add it to the main menu
        invoke AppendMenu, edi, MF_STRING, 101, chr$("&New")        ; and add
        invoke AppendMenu, edi, MF_STRING, 102, chr$("&Save")        ; two items
        invoke SetMenu, hWnd, esi                ; attach menu to main window
        invoke CreateWindowEx, WS_EX_CLIENTEDGE, chr$("edit"), NULL,
          WS_CHILD or WS_VISIBLE or WS_BORDER or ES_AUTOVSCROLL or ES_MULTILINE,
          9, 9, 300, 200,
          hWnd, 103, wcx.hInstance, NULL        ; we have added an edit control
        mov hEdit, eax        ; you may need this global variable for further processing

  CASE WM_COMMAND
        movzx eax, word ptr wParam        ; the Ids are in the LoWord of wParam
        Switch eax
        case 101
                MsgBox 0, "You clicked New", "Hi", MB_OK
        case 102
                MsgBox 0, "You clicked Save", "Hi", MB_OK
        Endsw
  CASE WM_DESTROY
        invoke PostQuitMessage, NULL                ; quit after WM_CLOSE
  ENDSW
  invoke DefWindowProc, hWnd, uMsg, wParam, lParam        ; default processing
  ret
WndProc endp

end WinMain
Title: Re: Simple template for creating a window
Post by: TWell on October 14, 2013, 09:53:52 PM
WM_CREATE could be after WM_COMMAND, because it comes only once ?
Title: Re: Simple template for creating a window
Post by: dedndave on October 14, 2013, 10:16:53 PM
also, i am a little surprised it works
i don't see how WM_CREATE, WM_COMMAND, WM_DESTROY return EAX=0
Title: Re: Simple template for creating a window
Post by: jj2007 on October 14, 2013, 10:57:55 PM
Quote from: TWell on October 14, 2013, 09:53:52 PM
WM_CREATE could be after WM_COMMAND, because it comes only once ?
Could be, but it doesn't matter where the CASE sits. The create, command, destroy order looks a bit more "natural".

Quote from: dedndave on October 14, 2013, 10:16:53 PM
also, i am a little surprised it works
i don't see how WM_CREATE, WM_COMMAND, WM_DESTROY return EAX=0
Yep, that's right, Dave :t

Corrected above - I took away the DEFAULT case and moved DefWindowProc before the ret (which returns zero for the three messages you mentioned).

P.S.: It worked because eax=hEdit, and the only value that would make WM_CREATE fail is -1.
Title: Re: Simple template for creating a window
Post by: dedndave on October 14, 2013, 11:07:22 PM
that doesn't look right, either   :P
the messages you process shouldn't always go to DefWindowProc

i use .if/elseif/endif - which i know you don't like
but, each message type that is handled is allowed to set its' own return value

the way you had it before wasn't too bad
you just needed to zero EAX at the end of each case (except DEFAULT)
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 12:02:20 AM
Quote from: dedndave on October 14, 2013, 11:07:22 PM
that doesn't look right, either   :P
the messages you process shouldn't always go to DefWindowProc

Windows can process all messages, and will return the right value. The only reason not to use DefWindowProc is when you want to bypass the default processing, which is not necessary here.
Title: Re: Simple template for creating a window
Post by: qWord on October 15, 2013, 12:19:05 AM
DefWindowProc is commonly placed in the default case. The template leads to code that process messages two times, which is either useless or simply wrong.
Quote from: msdn: DefWindowProcCalls the default window procedure to provide default processing for any window messages that an application does not process. This function ensures that every message is processed
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 12:58:06 AM
Quote from: qWord on October 15, 2013, 12:19:05 AM
DefWindowProc is commonly placed in the default case. The template leads to code that process messages two times, which is either useless or simply wrong.

Jein...

  CASE WM_DESTROY
        invoke PostQuitMessage, NULL                ; quit after WM_CLOSE
  CASE WM_NCLBUTTONDOWN
          .if word ptr lParam>200 && word ptr lParam+2<100
                  print "X was above 200", 13, 10
          .endif
  CASE WM_NCLBUTTONUP
          .if word ptr lParam>200 && word ptr lParam+2<100
                  print "X was above 200", 13, 10
          .endif
usedefault=1        ; set to one or to zero and try closing the window by clicking into the "x" in the upper right corner
if usedefault
  DEFAULT
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam        ; default processing
  ENDSW
  ret
else
  ENDSW
  invoke DefWindowProc, hWnd, uMsg, wParam, lParam        ; default processing
  ret
endif
WndProc endp

(console assembly & link)
Title: Re: Simple template for creating a window
Post by: dedndave on October 15, 2013, 01:06:25 AM
    mov     eax,uMsg
    .if eax==WM_COMMAND

        ;WM_COMMAND code

        xor     eax,eax

    .elseif eax==WM_CREATE

        ;WM_CREATE code

        xor     eax,eax

    .elseif eax==WM_DESTROY

        ;WM_DESTROY code

        xor     eax,eax

    .else
        INVOKE  DefWindowProc,hWnd,uMsg,wParam,lParam
    .endif
    ret


    SWITCH uMsg
    CASE WM_COMMAND

        ;WM_COMMAND code

        xor     eax,eax

    CASE WM_CREATE

        ;WM_CREATE code

        xor     eax,eax

    CASE WM_DESTROY

        ;WM_DESTROY code

        xor     eax,eax

    DEFAULT
        INVOKE  DefWindowProc,hWnd,uMsg,wParam,lParam
    ENDSW
    ret


ok - so it takes an additional XOR EAX,EAX for each message handled - so what ?
it allows flexibilty in handling different return values for different message types
for example - WM_CTLCOLORxxx messages, you want to return an HBRUSH
another - WM_CLOSE - if you offer a MessageBox to verify user exit, you can return TRUE to ignore the message

default processing after you have handled a message may work ok for the few you are currently processing
but, that will bite you in the ass, at some point - and it's slow, too
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 01:34:17 AM
Quote from: dedndave on October 15, 2013, 01:06:25 AM
for example - WM_CTLCOLORxxx messages, you want to return an HBRUSH

  invoke CreateBrush, ...
  ret


Quoteanother - WM_CLOSE - if you offer a MessageBox to verify user exit, you can return TRUE to ignore the message

Thanks for the example, Dave:

  CASE WM_CLOSE
     MsgBox 0, "Sure to close?", "Hi", MB_YESNO
     sub eax, IDNO
     .if Zero?
        ret
     .endif
usedefault=0   ; set to one and try closing the window
if usedefault
  DEFAULT
   invoke DefWindowProc, hWnd, uMsg, wParam, lParam   ; default processing
  ENDSW
  ret
else
  ENDSW
  invoke DefWindowProc, hWnd, uMsg, wParam, lParam   ; default processing
  ret
endif
WndProc endp


Quotedefault processing after you have handled a message may work ok for the few you are currently processing
but, that will bite you in the ass, at some point - and it's slow, too

It must work for all messages because that's Windows' default option. And it would be slow only if it happened a thousand times in an innermost loop, which is never the case...
Title: Re: Simple template for creating a window
Post by: qWord on October 15, 2013, 01:49:47 AM
Quote from: jj2007 on October 15, 2013, 12:58:06 AM[...]WM_NC[...]
a typical problem for people who want to see their first window in action.

BTW: I would tend to dave's approach.
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 01:59:34 AM
I'll try to explain it in simple words:
- If you put invoke DefWindowProc, ... into the DEFAULT case, then certain messages such as WM_(NC)LBUTTONDOWN and WM_CLOSE stop working properly. They need the DefWindowProc processing, always. Try the WM_CLOSE example above (use console assembly & link, and remember that Ctrl C closes a console program; otherwise, use Task Manager to kill the process).
- you can prevent specific messages from being def-processed simply by returning (e.g. a brush)
- you can do no harm by using DefWindowProc for messages that don't need it; you will just occasionally lose a few nanoseconds.
Title: Re: Simple template for creating a window
Post by: qWord on October 15, 2013, 02:27:37 AM
when you process WM_CLOSE and want to close the window, you must call DestroyWindow as per documentation. And of course the none client area message requires a special a handling, but how often did you work with such messages? IMO, especially for beginners, a template should follow the typical WndProc layout as proposed by Microsoft:
Code ("msdn: Using Window Procedures") Select
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;
}


BTW: I can't see what is problematic on WM_LBUTTONDOWN not being processed by DefWindowProc
Title: Re: Simple template for creating a window
Post by: dedndave on October 15, 2013, 02:33:02 AM
as for WM_CLOSE....
i have written code for WM_CLOSE, many times, without calling DefWindowProc
DefWindowProc handles it with a call to DestroyWindow

so - if you choose to handle WM_CLOSE, you can call DestroyWindow (or opt not to), then return 0

my previous comment about returning TRUE was incorrect
sorry about that - i was merely pointing out that it's nice to have individual control of return values

i will say this....
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

the idea of a template is to be expandable
i.e., the beginner should be able to add processing of a new message with minimal difficulty
Title: Re: Simple template for creating a window
Post by: hutch-- on October 15, 2013, 03:43:40 AM
Well, after about 20 years of writing windows, I have learnt a few things about WndProc procedures. Even though a normal switch/.if or whatever block processes sequentially, only one message will be processed at a time so it is bad design to preserve registers by default with the USES notation as many messages simply don't need to have extra registers. This allows you to use an instruction pair like XOR EAX, EAX / RET for messages that need to have zero returned by bypassing the DefWindowProc function call.

You properly do your register preservations on a message by message basis as you may for example have complex code that requires extra registers in a WM_COMMAND processing but nothing for many of the other messages.

For WndProc switch or .IF blocks, the most common mistake for people learning this style of coding is messing up where the DefWindowProc needs to be so critical messages get dumped out the other end without the correct default processing. Why doesn't my window show or what can't I move my window are common questions from WndProc errors.

For folks learning this style of coding, it is not always good advice to follow 1996 window templates as they were still in transition from 16 bit versions and had a lot of extra crap in them for 16 bit.

The style I recommend in a normal WndProc is LOCAL variables first like normal, then the .IF or switch block and when it is terminated (.endif or endsw), !!!! THEN !!!!! set you DefWindowProc call followed by a RET.

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
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 04:42:16 AM
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 (http://masm32.com/board/index.php?topic=250.msg1314#msg1314)) where we timed the cost of "uses esi edi ebx".
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 15, 2013, 05:18:02 AM
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.
           
           
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 05:41:00 AM
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 ;)
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 15, 2013, 05:58:09 AM
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 ?
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 15, 2013, 06:40:20 AM
Jochen,
         «Unfortunately, the documentation says otherwise»

          Could you show me that documentation ?
Title: Re: Simple template for creating a window
Post by: Gunther on October 15, 2013, 07:09:48 AM
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. (http://msdn.microsoft.com/en-us/library/k1a8ss06.aspx)

Gunther
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 07:35:08 AM
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.
Title: Re: Simple template for creating a window
Post by: dedndave on October 15, 2013, 07:48:07 AM
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
Title: Re: Simple template for creating a window
Post by: 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. 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
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 08:10:42 AM
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.
Title: Re: Simple template for creating a window
Post by: qWord on October 15, 2013, 08:58:18 AM
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 (http://support.microsoft.com/kb/976038/en-us)
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:
Title: Re: Simple template for creating a window
Post by: MichaelW on October 15, 2013, 05:55:09 PM
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.

Title: Re: Simple template for creating a window
Post by: habran on October 15, 2013, 08:00:39 PM
No kidding with nonvolatile registers :exclaim:
not preserved nonvolatile registers makes a programmer a charlatan :icon13:
Title: Re: Simple template for creating a window
Post by: Vortex on October 15, 2013, 08:03:09 PM
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


Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 10:20:00 PM
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.
Title: Re: Simple template for creating a window
Post by: dedndave on October 15, 2013, 10:47:11 PM
i don't know, Jochen
a lot can be done with EAX, ECX, EDX and INVOKE
i try to be conservative with system resources, when it's practical to do so - push if you need to
true - that one instance of one app doesn't take much
but, i try to think in terms of what else the user may have going on
pretend he has 100 other windows open - lol
if they all use the same paradigm, his system slows to a crawl

Erol - i like the example
i have one, someplace - i use PUSH to put the parms on the stack, then call DialogBoxIndirectParam
it's clumsy to code, at first - but that part only has to be done once   :P
Title: Re: Simple template for creating a window
Post by: jj2007 on October 15, 2013, 11:31:32 PM
Quote from: dedndave on October 15, 2013, 10:47:11 PM
pretend he has 100 other windows open - lol
if they all use the same paradigm, his system slows to a crawl

Dave,

Even if the user has a thousand windows open, only one is active and gets messages. Three cycles for push & pop is around 3/1500000000=2 nanoseconds per message. Not milliseconds, not microseconds, no: nanoseconds.

Our exercises in the lab typically run with a Million loops. Even with the most frequent message, WM_MOUSEMOVE, you cannot produce more than a few dozen per second if you move the mouse frenetically all over the desktop...
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 16, 2013, 02:51:03 AM
Hi MichaelW,
            Good explanation

«So you believe that Windows preserves EBX, ESI, and EDI around calls to callbacks?»

I believe the OS preserves EBX, ESI and EDI (if it needs to use it) when it calls the
WndProc callback and i know that the OS preserves them in any API like i
preserve them in procedures of my library because i need it.

Why ?
We can see the OS as a «server» and a program a «client».
Now, suppose i am the «server» and you are my «client».
In this way, i write myprocedures that call yourprocedures. When
i write myprocedures i need to use ebx, esi and edi before and
after calling yourprocedure. If i know that i need to use those
registers after calling your procedures, it makes no sense
to ask you «make me a favor, please, preserve ebx, esi, and edi».
It is stupid. (The same way it doesnt make sense to give you
a glass of something and to ask you to return the glass cleaned
because i need to use it again).

I never read any document (from Microsoft) or any remark
that says «you must preserve ebx, esi, edi in
any callback function like Window procedure WndProc»
-------------------------------------------------------------------------------------
Now, suppose i write the following procedure (ProcX) for my library.
It uses EBX but it doesnt preserve EBX before calling ProcY.
But i make a remark:

        «Hey people, please, preserves EBX when you define ProcY»

Do you think is this correct ?
For me, it is stupid.

ProcX       proc    pTblX:DWORD, pProcY:DWORD
            push    ebx
            push    esi
           
            mov     esi, pTblX
            mov     ebx, [esi-4]
            jmp     _start
           
    @@:     push    esi
                mov     eax, ebx
                shl     eax, 4
                add     esi, eax
                call    pProcY      ; esi is the table address
            pop     esi
       
    _start: sub     ebx, 1
            jns     short @B

            pop     esi
            pop     ebx
            ret
ProcX       endp
--------------------------------------------------------
It might be something like this and we have no problems

ProcX       proc    pTblX:DWORD, pProcY:DWORD
            push    esi
           
            mov     esi, pTblX
            mov     eax, [esi-4]
            jmp     _start
           
    @@:     push    eax
            push    esi
                shl     eax, 4
                add     esi, eax
                call    pProcY      ; ESI is the table address
            pop     esi
            pop     eax
       
    _start: sub     eax, 1
            jns     short @B

            pop     esi
            ret
ProcX       endp
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 16, 2013, 02:52:52 AM
Hi Jochen,

«All three regs have a different value every time they enter the WndProc,
with ebx always being zero.»

        Does this means the OS use them after, as they are ?
       
        By the way, i never wrote procedures
        for anyone and when i do it you may read it or
        i say what they do (.txt file or ...).
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 16, 2013, 03:07:37 AM
Hi qWord,

«
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 :
»
        Interesting !
        IMO, it is not a good solution,
        but i think that should be good reasons
        for that.
Title: Re: Simple template for creating a window
Post by: MichaelW on October 16, 2013, 07:38:19 AM
Quote from: RuiLoureiro on October 16, 2013, 02:51:03 AM
I never read any document or any remark
that says «you must preserve ebx, esi, edi in
any callback function like Window procedure WndProc»

See Register Usage in Agner Fog's calling_conventions.pdf, available  here (http://www.agner.org/optimize/).

And note that the callee-save registers include EBP, and preserving EBP is essential if the caller uses a normal stack frame.
Title: Re: Simple template for creating a window
Post by: hutch-- on October 16, 2013, 03:48:59 PM
I am still fascinated that after years of mistakes and crashes that people don't want to do it the right way, the Intel ABI !!! IS !!! the right way and Microsoft do it that way as well. How long have we been hearing "Oh but it worked on Win_whatever but now it crashes on Win_something_else".

Now if you want to write CRAP unreliable code that goes BANG in embarrassing places and make a fool of you, ignore the Intel ABI and do it the wrong way.  :P
Title: Re: Simple template for creating a window
Post by: Vortex on October 16, 2013, 07:05:52 PM
Hi Dave,

Thanks. Another nice tool is Hutch's macro based dialog templates. They are more efficient than a sequence of bytes in the DATA section.
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 16, 2013, 09:47:39 PM
Quote from: MichaelW on October 16, 2013, 07:38:19 AM
Quote from: RuiLoureiro on October 16, 2013, 02:51:03 AM
I never read any document or any remark
that says «you must preserve ebx, esi, edi in
any callback function like Window procedure WndProc»

See Register Usage in Agner Fog's calling_conventions.pdf, available  here (http://www.agner.org/optimize/).

And note that the callee-save registers include EBP, and preserving EBP is essential if the caller uses a normal stack frame.

        MichaelW, could you paste the relevant info here ?
        It is only to read it

        i have no problems with ALI and, as far as i know, no problems
        with my window applications. They are running for many years
        without problems. And if it has problems i solve it, dont worry.

        Could you tell me what happen with quickeditor ?
       
        There are a lot of times where i type things in the line
        x, and it is written in line the line y. For instance,
        x=14 and y=927. I use backspace in line x and it
        cleans in line y.
        I never saw this type of problems with word !
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 17, 2013, 12:06:47 AM
Quote from: hutch-- on October 16, 2013, 03:48:59 PM
I am still fascinated that after years of mistakes and crashes that people don't want to do it the right way, the Intel ABI !!! IS !!! the right way and Microsoft do it that way as well. How long have we been hearing "Oh but it worked on Win_whatever but now it crashes on Win_something_else".

Now if you want to write CRAP unreliable code that goes BANG in embarrassing places and make a fool of you, ignore the Intel ABI and do it the wrong way.  :P
:biggrin:
Hutch,
        You are taking too much whiskey
        please, dont do that it is not
        good to your health
Title: Re: Simple template for creating a window
Post by: hutch-- on October 17, 2013, 12:31:32 AM
You never waste good pure malt on excess but none the less, disregard the Intel ABI and your app will go BANG and make a fool of you when you least need it.  :biggrin:
Title: Re: Simple template for creating a window
Post by: qWord on October 17, 2013, 12:34:59 AM
Quote from: RuiLoureiro on October 16, 2013, 09:47:39 PMcould you paste the relevant info here ?
from msdn: x86 Architecture (http://msdn.microsoft.com/en-us/library/windows/hardware/ff561502%28v=vs.85%29.aspx)
Quote from: debug: x86 ArchitectureCalling Conventions

The x86 architecture has several different calling conventions. Fortunately, they all follow the same register preservation and function return rules:

    Functions must preserve all registers, except for eax, ecx, and edx, which can be changed across a function call, and esp, which must be updated according to the calling convention.
Title: Re: Simple template for creating a window
Post by: jj2007 on October 17, 2013, 12:37:38 AM
Quote from: hutch-- on October 16, 2013, 03:48:59 PM
I am still fascinated that after years of mistakes and crashes that people ... write CRAP unreliable code that goes BANG in embarrassing places and make a fool of you

Quote from: RuiLoureiro on October 17, 2013, 12:06:47 AMYou are taking too much whiskey

I am still fascinated that innocent threads can so easily turn into fascinating exchanges of ideas :greensml:

The Windows documentation is not exactly "crystal clear" about it, but it does say that you must preserve the non-volatile regs in callbacks. And technically speaking, WndProc is a callback, so it would indeed be bad practice not to preserve them.

Which has little to do with the observation that at least on Win XP and Win7-32, you can apparently trash esi edi ebx without crashing your app. My best guess is that Microsoft knows too well how many crappy coders and compilers are out there ;-)
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 17, 2013, 02:57:42 AM
Jochen,

Quote
The Windows documentation is not exactly "crystal clear" about it,
AFAIK there are many undocumented things
       
Quote
but it does say that you must preserve the non-volatile regs in callbacks.
And technically speaking, WndProc is a callback, so it would indeed be bad
practice not to preserve them.       

        only this:
        of course i understood it many years ago !

        onother thing:
        Doesnt sting me ! Sting Dave ! (bees are not with me) ;)
Title: Re: Simple template for creating a window
Post by: RuiLoureiro on October 17, 2013, 03:04:56 AM
Hutch,
        You need to change to a strong Dutch beear !
        I have here some bottles that came directly
        from Holland. Do you want one ?
Title: Re: Simple template for creating a window
Post by: Manos on October 17, 2013, 03:15:38 AM
Here is a classic template for MDI application.

Manos.
Title: Re: Simple template for creating a window
Post by: jj2007 on October 17, 2013, 03:31:34 AM
Quote from: Manos on October 17, 2013, 03:15:38 AM
Here is a classic template for MDI application.

EIGHT files for a simple MDI template?  :dazzled: :dazzled:
Title: Re: Simple template for creating a window
Post by: Manos on October 17, 2013, 03:47:55 AM
Quote from: jj2007 on October 17, 2013, 03:31:34 AM
Quote from: Manos on October 17, 2013, 03:15:38 AM
Here is a classic template for MDI application.

EIGHT files for a simple MDI template?  :dazzled: :dazzled:

No eight files. Only two: Project1.inc and Project1.asm and of course the Toolbar bitmap.
You add ALL files of the Project.

Manos.
Title: Re: Simple template for creating a window
Post by: dedndave on October 17, 2013, 04:09:00 AM
nice little project, Manos   :t

i usually put the MDI stuff on the File menu, though   :P

(http://imageshack.us/a/img716/9803/e5f8.png)

as for the number of files...
i normally have ASM, INC, RC, XML, ICO, BAT, as a minimum
Title: Re: Simple template for creating a window
Post by: Vortex on October 17, 2013, 04:33:02 AM
Hi Manos,

Nice work :t
Title: Re: Simple template for creating a window
Post by: hutch-- on October 17, 2013, 10:56:57 AM
Rui,

Sad to say I am long past beer, no longer agrees with me but then its no great loss, all serious assembler programmers are whisky drinkers. The only exceptions are our asm friends from eastern Europe who drink either slivovitz or vodka  C programmers are beer drinkers, Pascal programmers are white wine drinkers, C++ programmers drink 7up and the rest drink lemonade.  :P
Title: Re: Simple template for creating a window
Post by: dedndave on October 17, 2013, 11:05:02 AM
cobool and fortran programmers are probably drinking their prune juice   :(
i wonder what basic programmers are drinking   :redface:
Title: Re: Simple template for creating a window
Post by: habran on October 17, 2013, 01:10:44 PM
what about coffee drinkers? :icon_eek:
are they discarded from  programmers group? :(
If that so, I should give up programming or
start drinking whisky :dazzled:
Title: Re: Simple template for creating a window
Post by: hutch-- on October 17, 2013, 01:17:29 PM
 :biggrin:

No no, coffee is when your code has to be coherent or for breakfast. Most asm whisky drinkers are also coffee drinkers so you are safe here. With the choice of giving up programming or drinking whisky, take up the latter, your code will improve.  :P
Title: Re: Simple template for creating a window
Post by: habran on October 17, 2013, 01:25:50 PM
Quoteyour code will improve.  :P
you are maybe right about it ;)
and what about my liver? :(
Title: Re: Simple template for creating a window
Post by: hutch-- on October 17, 2013, 01:36:40 PM
> and what about my liver?

From the coffee or the whisky ?  :biggrin:
Title: Re: Simple template for creating a window
Post by: habran on October 17, 2013, 01:48:50 PM
hey, the new research is persuading us that coffee is loaded with antioxidants and is beneficial for our health :t
that's why I drink it all day long 8)
I've been working on a 64 bit text editor and I want to live long enough to finis it one day before I dye :bgrin:   
Title: Re: Simple template for creating a window
Post by: jj2007 on October 17, 2013, 03:55:09 PM
Quote from: dedndave on October 17, 2013, 11:05:02 AM
i wonder what basic programmers are drinking   :redface:

Dark red wine, Dave, like artists  ;)
Title: Re: Simple template for creating a window
Post by: Paulo on October 18, 2013, 03:51:10 AM
Quote from: dedndave on October 17, 2013, 11:05:02 AM

i wonder what basic programmers are drinking   :redface:

Probably something like Mountain Dew.  :biggrin:
Title: Re: Simple template for creating a window
Post by: Gunther on October 18, 2013, 04:05:22 AM
Jochen,

Quote from: jj2007 on October 17, 2013, 03:55:09 PM
Quote from: dedndave on October 17, 2013, 11:05:02 AM
i wonder what basic programmers are drinking   :redface:

Dark red wine, Dave, like artists  ;)

No. Brandy and alternatively Scotch for a clear head.  :lol:

Gunther
Title: Re: Simple template for creating a window
Post by: dedndave on October 18, 2013, 04:08:47 AM
i was thinking kool-aid - with artificial sweeteners   :biggrin:
Title: Re: Simple template for creating a window
Post by: Paulo on October 18, 2013, 04:14:35 AM
Quote from: dedndave on October 18, 2013, 04:08:47 AM
i was thinking kool-aid - with artificial sweeteners   :biggrin:
Only if it's visual basic.  :biggrin:
Title: Re: Simple template for creating a window
Post by: Manos on October 18, 2013, 05:33:40 AM
Quote from: RuiLoureiro on October 17, 2013, 03:04:56 AM
Hutch,
        You need to change to a strong Dutch beear !
        I have here some bottles that came directly
        from Holland. Do you want one ?
You can send it to me to drink wassail for you !

Manos.
Title: Re: Simple template for creating a window
Post by: qWord on October 18, 2013, 06:11:28 AM
people,
this is what real programmers drink :icon_exclaim:
(http://quangbasanpham.vn/images/picture/2161-22248-ethanol-absolute-gr-for-analysis-1009831000-merck-big.jpg)
Title: Re: Simple template for creating a window
Post by: Paulo on October 18, 2013, 06:15:17 AM
No, no, no, that is for amateurs.  :biggrin:
Real programmers drink this:

(http://s11.postimg.org/46f41c503/Hydro.jpg)
Title: Re: Simple template for creating a window
Post by: Vortex on October 18, 2013, 06:47:29 AM
Hi Paulo,

I think that amateurs are luckier than real programmers. :biggrin:
Title: Re: Simple template for creating a window
Post by: hutch-- on October 18, 2013, 08:29:01 AM
Hmmmm, I can think of a few "beverages" that come close to the bottle of ethanol.

1. schnapps, some versions left over from the development of the V2.
2. vodka, specialised Russian rocket propellant during the cold war.
3. Polish vodka, the Polish entry into the space race.
4. Serbian/Croatian Slivovitz, Tito's terrorist weapon.
5. White Lightning, Tennessee or Missouri, racing car fuel and early space race propellant.
6. Turkish Raki, strong enough to melt the hair off your chest.

We even have one here in OZ, Inner Circle Rum, 140 proof perfectly clear and definitely not to be drunk in shot glasses.
Title: Re: Simple template for creating a window
Post by: avcaballero on October 18, 2013, 09:48:14 PM
Ethanol, etc etc... nothing to do. This is a real drink (http://www.aclandcellars.com.au/spirits/spirit-liqueur/polish-pure-spirit-rectified-spirit-95) I saw in Poland

:: In the label should go "Only for real men" ::  :t
Title: Re: Simple template for creating a window
Post by: Gunther on October 18, 2013, 11:29:17 PM
Quote from: avcaballero on October 18, 2013, 09:48:14 PM
Ethanol, etc etc... nothing to do. This is a real drink (http://www.aclandcellars.com.au/spirits/spirit-liqueur/polish-pure-spirit-rectified-spirit-95) I saw in Poland

:: In the label should go "Only for real men" ::  :t

that's what I'm drinking every day.  :lol:

Gunther
Title: Re: Simple template for creating a window
Post by: GoneFishing on October 20, 2013, 01:56:50 AM
...
Title: Re: Simple template for creating a window
Post by: Gunther on October 20, 2013, 02:01:59 AM
Quote from: vertograd on October 20, 2013, 01:56:50 AM
No one here didn't mention cognac  ...

Let's say more generally Brandy.

Gunther