Event Handler

Tight_Coder_Ex 0 Tallied Votes 222 Views Share

I prefer to use this method as it doesn't clutter a windows procedure with dozens of conditionals and it address my primary objective of coding as tight as possible [28 bytes]. If you think my method can be improved upon in size and/or speed please post your solution.

Note: I have not tested this algorithym yet

; 				      *** MESSAGE HANDLER ***

; This procedure determines if application has a handler for a particular event.  All windows
; procedures are passed to this subroutine except those that have been sub or super classed.

;	ENTRY:	ESI = Pointer to message map
;		ECX = Number of sets in map.

; Each set consists of a 16 bit ID and 32 bit pointer to proceedure.

;	LEAVE:	EAX = 0 if application handled event, or result of DefWindowProc is not.
; ____________________________________________________________________________________________		

  ; 		ESP +  0 = Return address in kernel module
  ;		ESP +  4 = hWnd,	this windows 32 bit handle
  ;		ESP +  8 = Msg,	Message ID passed by OS.
  ;		ESP + 12 = wParam
  ;		ESP + 16 = lParam

  MsgHandler		mov	edx, [esp + 8]	; Get message ID 0 - 1024 or user defined.

	; In the unlikely event a null map is passed to this routine, this method of
	; testing ECX first will prevent a fatal crash.

  .NextMsg		and	ecx, ecx	; Are we at the end yet
			jz	.Default	; ZR = 1, If we are to do default proceesing.

			dec	ecx		; Decrement counter
			lodsw			; Get 16 bit ID from message map
			cmp	 ax, dx		; Is there a match
			lodsd			; Load pointer
			jnz	.NextMsg

  	; At this point we've found a match and as many events need wParam & lParam,
	; I'm going to establish a pointer to those in EBX.  hWnd can be simply addressed
	; by EBX - 8 or lParam by EBX + 4.

			lea	ebx, [esp + 12]	; EBX points to wParam
			call	eax		; Execute event
			jnc	.Default	; CY = 0, if event requires default proc.
                                      xor         eax,  eax             ; Handled must return 0
			ret	16		; Stack needs to be re-aligned.

  	; As hWnd, Msg, wParam & lParam are already on the stack I use this method
	; as a trace into kernel showed OS handles this appropriately and there is no
	; point pushing onto stack what is already there.  I've tested this on 98/ME/XP.

  .Default		pop	ebx
			call	_DefWindowProcA@16
			jmp	ebx

  ; You'll notice at this point we don't need RET 16 as the call to DefWindowProc has already
  ; done that for us.

Dani AI

Generated

Nice, compact dispatcher, — the table-driven approach keeps the WindowProc tidy and is often the right tradeoff for small handler sets. A few practical caveats and low-risk improvements to consider before testing.

The LODSw/LODSd style depends on the CPU direction flag; explicitly clear it before walking the table to avoid surprises if some other code left DF set. Packing entries as a 16-bit ID plus a 32-bit pointer gives 6-byte records; that saves space but forces unaligned dword reads and is CPU/ABI specific. For predictable performance and simpler reasoning, prefer fixed-width records (e.g., two dwords) or ensure pointers are dword-aligned. Also be explicit about the message-size assumption: Win32 APIs use 32-bit UINTs, so compare the full message value rather than only the low 16 bits unless you deliberately restrict the scheme to known 16-bit-range messages.

The handler contract should be explicit and easy for C/C++ callers to satisfy. Relying on processor flags across a CALL boundary is brittle — compiler-generated handlers (or third-party handlers) will not reliably preserve CF. Define and document a simple return convention (for example: handler returns a nonzero LRESULT meaning “handled,” or returns the final LRESULT directly), and implement the dispatcher to test that return value rather than flags.

For larger maps consider different dispatch strategies: keep linear scan for small N (cheap), use a sorted table plus binary search for medium N, or a small hash/jump table for many sparse handlers. Add basic safety checks (null map pointer, sane entry count) and avoid calling pointers without sanity validation if the table can be modified at runtime.

Quick testing checklist: exercise RegisterWindowMessage values, test with subclassed windows, verify behavior on a build that uses compiler-generated handlers (C/C++), run a microbenchmark on hot paths, and test on both 32-bit and modern 64-bit targets (the routine is 32-bit-specific and needs redesign for x64 calling conventions).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.