News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

GetWindowLong, hEdit, GWL_WNDPROC

Started by jj2007, April 06, 2013, 06:02:51 AM

Previous topic - Next topic

jj2007

Pretty standard code for subclassing an edit control:
.elseif uMsg == WM_CREATE
      invoke CreateWindowEx, WS_EX_CLIENTEDGE, chr$("edit"), NULL,
         WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT\
         or ES_AUTOHSCROLL or ES_AUTOVSCROLL or ES_MULTILINE,
         0, 0, 280, 300,
         hWin, 123, hInstance, NULL
      mov hEdit, eax                     ; we have created an Ansi edit window
      xchg eax, ebx                     ; move the handle to a safe reg
      invoke SetWindowLong, ebx,            ; we subclass the edit control
      GWL_WNDPROC, SubEdit
      mov opSubClass, eax                  ; the old pointer
      invoke GetWindowLong, ebx,            ; we subclass the edit control
      GWL_WNDPROC
      mov xSubClass, eax
   print "WM_CREATE:", 9      ; OPT_Susy console
   print hex$(opSubClass), 9
   print hex$(xSubClass), 13, 10
    .elseif uMsg == WM_LBUTTONDOWN
   print "WM_LBDOWN:", 9
   print hex$(opSubClass), 9
   print hex$(xSubClass), 13, 10


Results:
; WM_CREATE:      7E3BB3EC        0040137D
; WM_LBDOWN:      7E3BB3EC        0040137D

Observations:
1. opSubClass is not the original pointer, it's a handle
2. xSubClass is the new pointer

But on Win7-32 xSubClass changes its value when it leaves WM_CREATE, on XP it stays the same...

Windows mysteries :badgrin:

qWord

Quote from: msdnGWL_WNDPROC: Retrieves the address of the window procedure, or a handle representing the address of the window procedure.
MREAL macros - when you need floating point arithmetic while assembling!


jj2007

lpPrevWndFunc ... either the address of a window or dialog box procedure, or a special internal value meaningful only to CallWindowProc

The "lp" is in any case misleading. On Win7, it is the address of the sub-windowproc inside the WM_CREATE handler, then it changes to a new "internal" value.

Afterwards, it remains constant - which could be a way of testing whether a new subclass proc has been added or not. But it's not documented that the value remains constant...

qWord

[speculation]in context to OOP it might makes more sense that they use a handle rather than pointer, because the original window procedure is maybe a non-static method, which requires a this-pointer.[/speculation]
MREAL macros - when you need floating point arithmetic while assembling!

dedndave

i think it has to do with "chained" sub-classes
if you remove a "link" from the chain, it is still connected   :P

(my highly technical description - lol)

qWord

MREAL macros - when you need floating point arithmetic while assembling!

jj2007

Nice link, qWord :t

The comments are worth reading:
Benjamin: "Will Avalon really remove the message pump and do everything new? No more passing of COM-stuff through windows-messages?"

Raymond Chen: "Our research shows that many large companies are STILL running 16-bit Windows programs"

That was December 2003...

Antariy

There is one thing you may find interesting: if you're getting that "internal value" (i.e. handle) instead of a pointer to a sub-classing proc, but wanted to know the precise address, then just use another-encoding-version of a Get/SetWindowLong - i.e. if you've used GetWindowLongA and have got a handle, then use GetWindowLongW to get an address; and vice versa.

The reason is: different "default" encoding for a window (control) - if the windows was created with a Unicode version of CreateWindowExW, then if you're using ANSI version of GetWindowLongA - it will return the handle, not the address. And vice versa. This is required for a message translations between Unicode and ANSI or/and vice versa; that's what I suppose. I.e. when CallWindowProc gets an address - it knows that it just needs to pass  the params it gets without touch to the specified address, when it gets the handle (its value is knowlengly not-a-valid-address for user mode, and still the handle is checked so CallWindowProc will not eat some random garbage as a "handle") it knows it needs to translate some messages from one version of encoding to the contrary version - for example WM_CHAR or WM_GET/SETTEXT message etc - for the second it needs convert the text passed in an original buffer and pass the different, converted, buffer to a calling window proc; - for example.

I.e. to get the proper WndProc for any window (inside your own process, at least), you should use such an algorithm:


EXTERN _imp__GetWindowLongA@8:DWORD
EXTERN _imp__GetWindowLongW@8:DWORD

...

invoke IsWindowUnicode,hwndWindowOfInterest
mov ecx,dword ptr _imp__GetWindowLongA@8
test eax,eax
jz @F
mov ecx,_imp__GetWindowLongW@8
@@:
push GWL_WNDPROC
push hwndWindowOfInterest
call ecx


(or version specially for a gourmands :biggrin:):

push GWL_WNDPROC
push hwndWindowOfInterest
invoke IsWindowUnicode,hwndWindowOfInterest
mov ecx,_imp__GetWindowLongA@8
xchg eax,ecx
jecxz @F
mov eax,_imp__GetWindowLongW@8
@@:
call eax



This method will work for DWL_DLGPROC, too.

At least this is how it seems to work up to XP :biggrin:

BTW, one additional note: if an app uses manifest, so its controls look neatly and in modern style, then standard windows controls you're creating even with CreateWindowExA are default Unicode (at least on XP) - that's because other common controls DLL is used which uses other approach than the older one (common controls version below 6).


This info is probably a bit off-topic but may be useful.

jj2007

Quote from: Antariy on April 06, 2013, 10:27:11 AM
This info is probably a bit off-topic but may be useful.

Alex, that info is "spot on", thanks a lot :t

I've modified the code a little bit, results look like this on XP:

Where           opSub           addrSub         gwlA            gwlW
WM_CREATE:      7E3BB3EC        00401424        00401424        FFFF03BB
WM_LBDOWN:      7E3BB3EC        00401424        00401424        FFFF03BB


So the ANSI version of GetWindowLong yields the true address. Can anybody test it on Win7 please?

fearless

Win7 64bit - ran it 4 times, gwlW changed on each run.

Where           opSub           addrSub         gwlA            gwlW
WM_CREATE:      77A80BDE        00401424        00401424        FFFF08CF
WM_LBDOWN:      77A80BDE        00401424        00401424        FFFF08CF

Where           opSub           addrSub         gwlA            gwlW
WM_CREATE:      77A80BDE        00401424        00401424        FFFF086F
WM_LBDOWN:      77A80BDE        00401424        00401424        FFFF086F

Where           opSub           addrSub         gwlA            gwlW
WM_CREATE:      77A80BDE        00401424        00401424        FFFF0BC9
WM_LBDOWN:      77A80BDE        00401424        00401424        FFFF0BC9

Where           opSub           addrSub         gwlA            gwlW
WM_CREATE:      77A80BDE        00401424        00401424        FFFF0697
WM_LBDOWN:      77A80BDE        00401424        00401424        FFFF0697

jj2007

Quote from: fearless on April 06, 2013, 06:28:46 PM
Win7 64bit - ran it 4 times, gwlW changed on each run.

Yes, because it's not an address but rather a handle, as the FFFF shows.

Antariy

Quote from: jj2007 on April 06, 2013, 04:42:54 PM
Alex, that info is "spot on", thanks a lot :t

Thank you, Jochen :biggrin:

Here is what XP SP2 gives:

Where           opSub           addrSub         gwlA            gwlW
WM_CREATE:      77D6063A        00401424        00401424        FFFF0A8D
WM_LBDOWN:      77D6063A        00401424        00401424        FFFF0A8D
WM_LBDOWN:      77D6063A        00401424        00401424        FFFF0A8D
WM_LBDOWN:      77D6063A        00401424        00401424        FFFF0A8D


jj2007

Great. So it seems that one could use the gwlA value to determine if the subclass is still the right one (or gwlW for a Unicode control, of course).

Background: There is a thread in PellesC forum where the OP claims that "Minimum operating system: Windows XP (SetWindowSubclass api), but could be lowered after replacing SetWindowSubclass with unsafe SetWindowLong(GWL_WNDPROC)", which is in line with Raymond Chen's "what would happen if somebody else had subclassed the window"? (Safer subclassing). See inter alia reply #6 ;-)

Antariy

Probably it's easy to make "partially eliminating" subclasses. When we don't need to subclass some window, we check - if our subclass is on the top of the chain. If it is - we just restore an old subclass (old WndProc), if it is not, then we put some flag in the data of the program or in the data associated with that window (it's easy to use GCL_CBWNDEXTRA with SetClassLong to add 4 bytes to every window that will be created which will hold a pointer to any sized structure with any set of values/flags/etc) - the flag which means "do not process any things for this window". I.e.


subclass proc ...
... some way to get the flag "dontProcess" into eax ...
cmp eax,1
jnz @F
pop edx
push oldWndProc
push edx
jmp CallWindowProc
@@:

... some other things which will become unwanted when "dontProcess" flag is set ...

subclass endp


... somewhere in the code we can do ...

mov dontProcess,1

...