The main idea is to make user defined message procedures without subclassing windows, but using modified message loop. Here is only part of the message loop, ilustrating the idea:



.translate:
invoke TranslateMessage, esi

invoke GetProp, [esi+MSG.hwnd], propUserWinProc
test eax, eax
jz .default ; there is no user procedure...

; call user procedure if any...
push esi
stdcall eax, [esi+MSG.hwnd], [esi+MSG.message], [esi+MSG.wParam], [esi+MSG.lParam]
pop esi
jnc .msg_loop

.default:
invoke DispatchMessage, esi
jmp .msg_loop

propUserWinProc db 'UserWinProc', 0


The user can then attach user message procedure to every window, simply by using:
invoke SetProp, [.hwnd], propUserWinProc, ptrToMyUserProc


...and to stop the processing with:
invoke RemoveProp, [.hwnd], propUserWinProc


User message procedure doesn't have to preserve any register and if it returns CF=1, default message procedure will be invoked by DispatchMessage.

What you think about this approach? Any hidden problems, I missed?

Regards.
Posted on 2004-09-02 12:04:32 by JohnFound
It doesn't work when a message is sent using SendMessage from within the same thread. This causes the appropriate window procedure to be called directly.
Posted on 2004-09-02 12:51:17 by Sephiroth3
It doesn't work when a message is sent using SendMessage from within the same thread. This causes the appropriate window procedure to be called directly.


Yes, but especially for user program, that can be fixed with something like in the message loop:


proc MySendMessage, .hwnd, .wmsg, .wparam, .lparam
begin
invoke GetProp, [esi+MSG.hwnd], propUserWinProc
test eax, eax
jz .direct ; there is no user procedure...

; call user procedure if any...
push esi
stdcall eax, [.hwnd], [.wmsg], [.wparam], [lparam]
pop esi
jnc .finish

.direct:
invoke SendMessage, [.hwnd], [.wmsg], [.wparam], [lparam]
.finish:
return


So, the question is whether the OS sends direct messages with SendMessage, or not?

BTW, even normal subclassing is very simple using SetProp and GetProp functions:


proc SubclassWindow, .hwnd, .ptrUserProc
begin
invoke GetWindowLong, [.hwnd], GWL_WNDPROC
invoke SetProp, [.hwnd], propOldWinProc, eax
invoke SetProp, [.hwnd], propUserWinProc, [.ptrUserProc]
invoke SetWindowLong, [.hwnd], GWL_WNDPROC, UniversalWinProc
return
endp

proc UniversalWinProc, .hwnd, .wmsg, .wparam, .lparam
begin
invoke GetProp, [.hwnd], propUserWinProc
test eax, eax
jz .oldproc

push ebx esi edi
stdcall eax, [.hwnd], [.wmsg], [.wparam], [.lparam]
pop edi esi ebx
jnc .finish

.oldproc:
invoke GetProp, [.hwnd], propOldWinProc
invoke CallWindowProc, eax ,[.hwnd],[.wmsg],[.wparam],[.lparam]

.finish:
return
endp

propOldWinProc db 'OldWinProc', 0
propUserWinProc db 'UserWinProc', 0


Here the user procedure does not heve to call old windows procedure and does not have to preserve registers, it uses the convention from the example in my first post.
Posted on 2004-09-03 02:34:02 by JohnFound
The OS sends quite a few messages with SendMessage. Many APIs cause messages to be sent (for example SetFocus, EnableWindow, SetWindowPos) and controls also send messages.
Posted on 2004-09-03 11:11:18 by Sephiroth3