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:
; 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:

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]
Posted on 2004-07-26 11:48:08 by donkey
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).
Posted on 2004-07-27 09:43:57 by QvasiModo

If GoAsm needs 'add esp' after 'invoke wsprintf' -
why it uses name "invoke" like MASM ...
Posted on 2004-07-29 05:42:20 by kero


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)
Posted on 2004-08-13 02:26:15 by Morris
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"...
Posted on 2004-08-13 05:01:45 by 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
Posted on 2004-08-13 05:08:22 by Morris
2 Morris:

i'm afraid we created flame in Donkey's thread...

Let's go to my thread "Need test code on XP/2k3 ..."
- i use there 'wsprintfA proto C' and 33 arguments in my utility without add esp etc

But at first just play with code bellow:

.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>

tt db "OK",0
ft db "%d",0
br db 16 dup (?)

push offset tt
invoke wsprintf,addr br,addr ft,0


pop eax
cmp eax,offset tt
jz @f
xor eax,eax
invoke MessageBox,0,addr br,eax,0
invoke ExitProcess,0
end start
Posted on 2004-08-13 07:15:03 by kero

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.
Posted on 2004-08-13 09:21:36 by donkey