I did my first program using subclassing. Works fine. But I have some questions about this technique.
1. The MSDN page on subclassing controls (http://msdn.microsoft.com/en-us/library/bb773183%28VS.85%29.aspx) 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.
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.
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)
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.
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
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
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.
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
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?
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
Or just start at Generating Prologue and Epilogue Code about 70% down the page here (http://webster.cs.ucr.edu/Page_TechDocs/MASMDoc/ProgrammersGuide/Chap_07.htm) and continue through User-Defined Prologue and Epilogue Code.
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]".
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
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
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.
In a window procedure that calls external functions, the prologue and epilogue overhead should be insignificant.
It is said here
http://blogs.msdn.com/b/oldnewthing/archive/2003/11/11/55653.aspx
"you must remove your window subclass before the window being subclassed is destroyed"
Is it true ?
Quote from: ToutEnMasm on October 22, 2011, 02:39:13 PM
"you must remove your window subclass before the window being subclassed is destroyed"
Is it true ?
If it's a thread, one could try to find scenarios where this might cause trouble. If the process is about to end, no I can't imagine that not un-subclassing could have any effect. But there are some people here who have a much deeper understanding of Windows internals - and I also find that question interesting.
Quote
"you must remove your window subclass before the window being subclassed is destroyed"
Is it true ?
No answer ?..
One post say:
Quote
If you subclassed with the SetWindowSubclass function, then you need to unsubclass to clean up the bookkeeping that SetWindowSubclass created for you. If you are running a chk build, you will break into the debugger with an error if SetWindowSubclass detects that a subclass failed to clean itself up. If you subclassed manually via SubclassWindow then you're on your own to to clean up whatever bookkeeping you have attached to the subclass. If you have no bookkeeping that needs to be cleaned up, then you're home free.
If it is only a problem with the check build,no matter,masm don't use it.
If there is a risk to let allocated memory ,it is annoying.But this isn't refered in any windows help files.
Convenrtional subclassing has never had the problem, if you destroy the Window then there is no callback for that window to be subclassed. The code will still be in memory as it is part of the application but it is dead until it is re-used again. Think of what a subclass is, a user defined bypass that gains access into the message handler for a pre-defined window that already has its own class. This is why you need the address of the original message handler to pass control back to it.
I have made some tests with the crt debug functions who detects memory leaks and more.
Results are almost obvious.
There is no need to call the RemoveWindowSubclass until we don't pass allocated memory to the subclass procédure.
In this case (the dwRefData is used) , the close of the subclass is needed before freed of the allocated memory.Those to avoid read write operations in a freed memory.
That all.