News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Questions about subcalssing

Started by NoCforMe, October 21, 2011, 07:11:45 PM

Previous topic - Next topic

NoCforMe

I did my first program using subclassing. Works fine. But I have some questions about this technique.

1. The MSDN page on subclassing controls describes "old" and "new" ways of subclassing. Since I'm using Windows 2000, I assume I can't use the new method, which requires ComCtl32.dll version 6. OK, fine.

But they list several disadvantages to the "old school" method:

Quote
Disadvantages of the Old Subclassing Approach

  • The window procedure can only be replaced once.
  • It is difficult to remove a subclass after it is created.
  • Associating private data with a window is inefficient.
  • To call the next procedure in a subclass chain, you cannot cast the old window procedure and call it, you must call it by using the CallWindowProc function.

I understand the first limitation, but not the second one. First of all, I'm not sure what exactly they mean by "remove a subclass". Couldn't the application simply revert to sending all messages to the original control? seems like that would effectively remove the effects of the subclassing.

Also, I'm assuming that the control class reverts to normal (i.e., no subclassing) once the application exits back to Windows. I don't have to do anything like release handles, memory, nothing like that, correct?

2. What other methods of subclassing are there that are not described in that MSDN page?

3. When I subclass a class, it affects only my application, correct? So what level does this operate on, technically speaking: thread? process? something else?

In any case, the method I chose was really simple:



EditHandle HWND ?

OldEditProcPtr DD ?

; Create edit control:
INVOKE CreateWindowEx, WS_EX_LEFT OR WS_EX_CLIENTEDGE, ADDR EditClassName, NULL,
$editStyles, wX, wY,
$editWidth, $editHeight, MainWinHandle, $editID, InstanceHandle, NULL
MOV EditHandle, EAX

; Subclass the edit control:
INVOKE SetWindowLong, EditHandle, GWL_WNDPROC, ADDR EditProc
MOV OldEditProcPtr, EAX

;====================================================================
; Edit window Proc
;
; (Subclassed from edit control class)
;====================================================================

EditProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD

MOV EAX, uMsg
CMP EAX, WM_CHAR
JE dochar

; use old window proc for all other messages:
INVOKE CallWindowProc, OldEditProcPtr, hWin, uMsg, wParam, lParam
RET

dochar:
; Do whatever ...

EditProc ENDP



So subclassing basically consists of 2 things:

  • Setting the new window procedure with SetWindowLong() and saving the pointer to the old window procedure
  • Handling certain messages in the new window procedure, passing everything else to the old procedure using CallWindowProc().
All in all, not rocket science.

hutch--

Its easy to remove a subclass using the earlier technique, destroy the control and re-create it. The earlier techynique works fine and is backward compatible to Win95OEM, the later wrapper is for dummies that don't understand the messaging system in Windows.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

jj2007

Quote from: hutch-- on October 21, 2011, 10:13:18 PM
Its easy to remove a subclass using the earlier technique, destroy the control and re-create it

That is overkill...

  invoke SetWindowLong, hEdit, ; we subclass the edit control
  GWL_WNDPROC, SubEdit
  mov opSubClass, eax
  ...
  invoke SetWindowLong, hEdit, ; we redirect the edit control to its original class
  GWL_WNDPROC, opSubClass


Works fine :8)

NoCforMe

So why all the negativity in that MSDN page? Are they just trying to get people to use their new shiny code?

Another question: it seems kind of silly to CALL the previous routine (window proc): why not just JMP to it? Of course, you'd have to set up the stack frame properly to do that, but really, why do we want to CALL something, then return from our code to the original caller? (Of course, if what you get from SetWindowLong() isn't a bona fide address, this of course won't work.)

Curious minds want to know.

dedndave

it doesn't take much to set up the stack frame - lol
the return address and 4 parameters are already in place
i use JMP DefWindowProc in a similar manner
the idea isn't very popular on the forum, but i like it   :bg

dedndave

QuoteAn application must pass any messages not processed by the new window procedure to the previous window
procedure by calling CallWindowProc. This allows the application to create a chain of window procedures.

kind of like a hook

hutch--

Jumping to a proc address looked profound in the DOS era, in the modern era, it messes up the CALL / RET pairing which is really lousy code.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dedndave

i don't see where the CALL/RET pair is messed up at all
well - not in this case, at least

        OPTION  PROLOGUE:None
        OPTION  EPILOGUE:None

        ALIGN   16

WndProc PROC    hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

;------------------------------

        mov     ecx,[esp+8]                             ;uMsg
        cmp     ecx,WM_CLOSE
        jz      WndPrY

        cmp     ecx,WM_DESTROY
        jz      WndPrZ

;------------------------------

        JMP     DefWindowProc

;------------------------------

WndPr0: xor     eax,eax
        ret     16

;------------------------------

;WM_CLOSE

WndPrY: INVOKE  DestroyWindow,[esp+4]                   ;hWnd
        jmp     WndPr0

;------------------------------

;WM_DESTROY

WndPrZ: INVOKE  PostQuitMessage,NULL
        jmp     WndPr0

;------------------------------

WndProc ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

NoCforMe

One small criticism, if you don't mind, Dave: why code something  like


        mov     ecx,[esp+8]                             ;uMsg


instead of just calling the variable "uMsg", which, after all, is already defined in your parameter list, makes it clear what you're referencing, doesn't cost you anything in terms of code size, and would make that comment completely unnecessary?

A matter of style, I know, but why create code that's hard to understand (well, to anybody but you) intentionally?

jj2007

Quote from: NoCforMe on October 22, 2011, 06:07:43 AM
One small criticism, if you don't mind, Dave: why code something  like


        mov     ecx,[esp+8]                             ;uMsg


instead of just calling the variable "uMsg"

Get a good book and find out what OPTION  PROLOGUE:None means :wink

MichaelW

Or just start at Generating Prologue and Epilogue Code about 70% down the page here and continue through User-Defined Prologue and Epilogue Code.
eschew obfuscation

NoCforMe

Sorry, shoulda checked before posting that.

As I said, a matter of style. In my younger days, I would have done things the way you do, not letting the assembler do everything for me, writing my own macros, etc. As an older fart, I'm quite content to let the assembler generate prologues and such; it's a small price to pay. My code still has so much less overhead than any C program. And I don't like chasing down disembodied variables like "[esp+8]".

MichaelW

#12
Actually, with C you can code naked functions that have no more overhead than a OPTION PROLOGUE:None + OPTION EPILOGUE:None MASM procedure would have:

__declspec( naked ) int sum2( int arg1, int arg2 )
{
    __asm
    {
        mov eax, [esp+4]
        add eax, [esp+8]
        ret
    }
}


_sum2 PROC NEAR
; Line 6
  mov eax, DWORD PTR [esp+4]
; Line 7
  add eax, DWORD PTR [esp+8]
; Line 8
  ret 0
_sum2 ENDP


http://msdn.microsoft.com/en-us/library/21d5kd3a(VS.80).aspx
eschew obfuscation

dedndave

i know that using ESP to grab parms is not as efficient as using EBP
but, for certain parts of a WndProc procedure, using EBP is more overhead
for an intensive part of the procedure, like WM_PAINT or something that uses a lot of wParam/lParam accesses, i can push EBP   :P
if the standard prologue is used, then the default exit would be
        pop     ebp
        JMP     DefWindowProc

hutch--

The criterion of efficiency used here would have to be suspect.


mov eax, [esp+4]
; versus
mov eax, [ebp+8]


The difference is that with the second you have extra overhead setting up the stack frame and cannot routinely use EBP.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php