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
WM_CREATE could be after WM_COMMAND, because it comes only once ?
also, i am a little surprised it works
i don't see how WM_CREATE, WM_COMMAND, WM_DESTROY return EAX=0
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.
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)
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.
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
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)
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
Quote from: dedndave on October 15, 2013, 01:06:25 AM
for example - WM_CTLCOLORxxx messages, you want to return an HBRUSH
invoke CreateBrush, ...
retQuoteanother - 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 endpQuotedefault 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...
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.
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.
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:
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
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
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
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 endpP.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".
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.
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 uMsgHappy testing ;)
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 ?
Jochen,
«Unfortunately, the documentation says otherwise»
Could you show me that documentation ?
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
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.
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
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_® proc hWnd:HWND,lParam:LPARAM
xor reg,reg ; let's break the WinABI ;-)
mov eax,TRUE
ret
enum_® 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
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.
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:
; 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:
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.
No kidding with nonvolatile registers :exclaim:
not preserved nonvolatile registers makes a programmer a charlatan :icon13:
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
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.
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
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...
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
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 ...).
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.
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.
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
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.
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 !
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
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:
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.
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 ;-)
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) ;)
Hutch,
You need to change to a strong Dutch beear !
I have here some bottles that came directly
from Holland. Do you want one ?
Here is a classic template for MDI application.
Manos.
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:
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.
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
Hi Manos,
Nice work :t
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
cobool and fortran programmers are probably drinking their prune juice :(
i wonder what basic programmers are drinking :redface:
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:
: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
Quoteyour code will improve. :P
you are maybe right about it ;)
and what about my liver? :(
> and what about my liver?
From the coffee or the whisky ? :biggrin:
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:
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 ;)
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:
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
i was thinking kool-aid - with artificial sweeteners :biggrin:
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:
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.
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)
No, no, no, that is for amateurs. :biggrin:
Real programmers drink this:
(http://s11.postimg.org/46f41c503/Hydro.jpg)
Hi Paulo,
I think that amateurs are luckier than real programmers. :biggrin:
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.
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
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
...
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