If anyone has scratched their head over superclassing a window they will know what I mean by this. It is always preferable, at least to me to store as much information with a window as possible, that way you aren't dealing with alot of memory variables. So generally in a subclassing I will store the oldWndProc in the GWL_USERDATA parameter of the window. This is fine and works well enough. However when you superclass a window you cannot affect class memory until *after* the window has been created. This is because Set/GetClassLong require a window handle of the class and not a class atom. So it would seem that there is no way to store the oldWndProc for a class in it's class memory allocated in WNDCLASSEX.cbClsExtra. Here is a solution to that connundrum if you are not assigning a class menu...
First assign the string representation of the old WndProc to the lpszMenuName parameter:
Then in your newWndProc you must process the WM_NCCREATE message to set the class DWORD:
Once that is done there is no longer a need to track the oldWndProc pointer:
First assign the string representation of the old WndProc to the lpszMenuName parameter:
; In the superclassing routine
mov D[wcx.cbClsExtra],4
mov eax,[wcx.lpfnWndProc]
invoke wsprintf,offset buffer,"%u",eax
add esp,12
lea eax,buffer
mov [wcx.lpszMenuName],eax
mov D[wcx.lpfnWndProc],OFFSET newWndProc
invoke RegisterClassEx,ADDR wcx
Then in your newWndProc you must process the WM_NCCREATE message to set the class DWORD:
cmp D[uMsg],WM_NCCREATE
jne >.NXTMSG
; we stored the old proc in the menu name
; that way there is no need to create a variable for it
; This must be processed as the first message
invoke GetClassLong,[hwnd],GCL_MENUNAME
invoke ascii2dw,eax
invoke SetClassLong,[hwnd],0,eax
Once that is done there is no longer a need to track the oldWndProc pointer:
invoke GetClassLong,[hwnd],0
invoke CallWindowProc,eax,[hwnd],[uMsg],[wParam],[lParam]
There's a related trick you might be interested in: using the window properties APIs (SetProp, EnumProps, EnumPropsEx, GetProp, GlobalAddAtom, RemoveProp) you can store data in any window, associateing it with an atom.
That way conflicts nearly impossible when super or subclassing, you can safely store your data in the window without worrying for an ill-behaved WndProc messing with it.
For a speed increase we could store all our data in the heap and keep only the pointer with SetProp. Also the atom can be precalculated (instead of passing it's string as a parameter).
That way conflicts nearly impossible when super or subclassing, you can safely store your data in the window without worrying for an ill-behaved WndProc messing with it.
For a speed increase we could store all our data in the heap and keep only the pointer with SetProp. Also the atom can be precalculated (instead of passing it's string as a parameter).
(Offtopic)
If GoAsm needs 'add esp' after 'invoke wsprintf' -
why it uses name "invoke" like MASM ...
If GoAsm needs 'add esp' after 'invoke wsprintf' -
why it uses name "invoke" like MASM ...
(Offtopic)
If GoAsm needs 'add esp' after 'invoke wsprintf' -
why it uses name "invoke" like MASM ...
My guess is, that this function is declared as "stdcall" (the callee cleans the stack), but as we know (do we?) the wsprintf is a "cdecl" function (the caller is responsible for cleaning the stack)
2 M.O.R.R.I.S.:
My offtopic wasn't about calling conventions.
It was note about name "invoke" for macros in Masm and GoAsm.
In case of GoAsm we must wright
1) invoke wsprintf,offset buffer,"%u",eax
2) add esp,12
In case of Masm we must write only
1) invoke wsprintf,offset buffer,"%u",eax
- do you guess what will be after 2) in Masm ?
So again:
if macros "invoke" in Masm and "invoke" in GoAsm are such different -
why GoAsm didn't use another name instead of Masm's "invoke"...
My offtopic wasn't about calling conventions.
It was note about name "invoke" for macros in Masm and GoAsm.
In case of GoAsm we must wright
1) invoke wsprintf,offset buffer,"%u",eax
2) add esp,12
In case of Masm we must write only
1) invoke wsprintf,offset buffer,"%u",eax
- do you guess what will be after 2) in Masm ?
So again:
if macros "invoke" in Masm and "invoke" in GoAsm are such different -
why GoAsm didn't use another name instead of Masm's "invoke"...
Kero:
if you declare in MASM wsprintf as a stdcall function with, say, 3 arguments, you could still call it by using "invoke" and the function will work, but you'd have to clear the stack yourself after the function returns.
I do not know GoAsm, so I cannot say if this is general rule for calling cdecl functions or is it just wsprintf being declared as stdcall. Notice, that Donkey did not have to "add esp,anything" after invoke GetClassLong
if you declare in MASM wsprintf as a stdcall function with, say, 3 arguments, you could still call it by using "invoke" and the function will work, but you'd have to clear the stack yourself after the function returns.
I do not know GoAsm, so I cannot say if this is general rule for calling cdecl functions or is it just wsprintf being declared as stdcall. Notice, that Donkey did not have to "add esp,anything" after invoke GetClassLong
2 Morris:
i'm afraid we created flame in Donkey's thread...
Let's go to my thread "Need test code on XP/2k3 ..."
(http://www.asmcommunity.net/board/showthread.php?threadid=18853)
- i use there 'wsprintfA proto C' and 33 arguments in my utility without add esp etc
But at first just play with code bellow:
.386
.model flat,stdcall
option casemap:none
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.lib
wsprintfA proto C :DWORD,:DWORD,:VARARG
wsprintf equ <wsprintfA>
.data
tt db "OK",0
ft db "%d",0
.data?
br db 16 dup (?)
.code
start:
push offset tt
invoke wsprintf,addr br,addr ft,0
; add esp,4*3 ; TRY SWITCH ON/OFF THIS LINE
pop eax
cmp eax,offset tt
jz @f
xor eax,eax
@@:
invoke MessageBox,0,addr br,eax,0
invoke ExitProcess,0
end start
i'm afraid we created flame in Donkey's thread...
Let's go to my thread "Need test code on XP/2k3 ..."
(http://www.asmcommunity.net/board/showthread.php?threadid=18853)
- i use there 'wsprintfA proto C' and 33 arguments in my utility without add esp etc
But at first just play with code bellow:
.386
.model flat,stdcall
option casemap:none
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.lib
wsprintfA proto C :DWORD,:DWORD,:VARARG
wsprintf equ <wsprintfA>
.data
tt db "OK",0
ft db "%d",0
.data?
br db 16 dup (?)
.code
start:
push offset tt
invoke wsprintf,addr br,addr ft,0
; add esp,4*3 ; TRY SWITCH ON/OFF THIS LINE
pop eax
cmp eax,offset tt
jz @f
xor eax,eax
@@:
invoke MessageBox,0,addr br,eax,0
invoke ExitProcess,0
end start
Hi,
GoAsm does not adjust the stack after C calling convention calls so you have to do it yourself.If you check the output of MASM it inserts the code for you but GoAsm is designed to insert code as little as possible. For example it does not have .IF or .WHILE or any of the other high level constructs. The reason that it's this way is that GoAsm does not do type or parameter checking so it does not calculate the number of bytes pushed, it simply pushes them.
But even with that the INVOKE from GoAsm is much better than MASM's which frankly is the worst of them all. For example in GoAsm I can do all of these thngs that are impossible in MASM without macros...
So in reality I don't really miss the fact that MASM adds the ADD ESP,cbParams without asking me.
GoAsm does not adjust the stack after C calling convention calls so you have to do it yourself.If you check the output of MASM it inserts the code for you but GoAsm is designed to insert code as little as possible. For example it does not have .IF or .WHILE or any of the other high level constructs. The reason that it's this way is that GoAsm does not do type or parameter checking so it does not calculate the number of bytes pushed, it simply pushes them.
But even with that the INVOKE from GoAsm is much better than MASM's which frankly is the worst of them all. For example in GoAsm I can do all of these thngs that are impossible in MASM without macros...
mov EAX,offset function
invoke EAX, [Param1], [Param2]
mov D[pFunction], offset FunctionArray
mov eax,offset pFunction
mov edx, [Index]
invoke [EAX+EDX*4], [Param1], [Param2]
invoke MyDll.DLL:DllFunction, [Param1], [Param2] ; No need for LoadLibrary/GetProcAddress
invoke MyLib.LIB:LibFunction, [Param1], [Param2]
invoke Real4Func, 1.4567, 0.098
invoke GetProcAddress, [hLib], "SomeFunction"
mov [pFunction], eax
invoke [pFunction], [Param1], [Param2]
push MB_OK
invoke MessageBoxA, NULL, "AnsiString", "Caption"
invoke MessageBoxA, NULL, "AnsiString", "Caption", 0
invoke MessageBoxW, NULL, L"UnicodeString", L"Caption", 0
mov eax,addr LocalString
invoke MessageBoxA, 0, EAX, Addr Local2, 0
mov ??, offset ??
invoke ??, ??1, ??2
So in reality I don't really miss the fact that MASM adds the ADD ESP,cbParams without asking me.