Hi !
I have a littlte problem in using the classic Hutch's subclassing example:
; ===============================================================
; Title: EdDec.inc
; ================================================================
DEF_EdDec struc
dCtlID DWORD ? ;Contol ID
dStyle DWORD ? ;Style
dExStyle DWORD ? ;Extended style
pTitle PSTRING ? ;-> Window title
sdPosX SDWORD ? ;X position
sdPosY SDWORD ? ;Y position
dWidth DWORD ? ;Width
dHeight DWORD ? ;Height
DEF_EdDec ends
PDEF_EdDec typedef ptr DEF_EdDec
; —————————————————————————————————————————————————
; Object: EdiDec
; Purpose: Implement a thin wrapper around the Edit GDI object.
Object EdDec, , WinControl ;Class "Edit"
RedefineMethod Done
RedefineMethod Init, POINTER, HWND, PDEF_EdDec
RedefineMethod WndProc, DWORD, WPARAM, LPARAM
ObjectEnd
; ===========================================================
if IMPLEMENT
; ————————————————————————————————————————————————
; Method: Edit.Done
; Purpose: Finalize the Edit object.
; Arguments: None.
; Return: Nothing.
Method EdDec.Done, uses esi
SetObject esi
Unsubclass EdDec ;Uses esi
ACall esi.Done
MethodEnd
; ——————————————————————————————————————————————————
; Method: EdiDec.Init
; Purpose: Initialize the Edit object.
; Arguments: Arg1: -> Owner object.
; Arg2: Parent window HANDLE.
; Arg3: -> DEF_Edit initialization structure.
; Return: Nothing.
Method EdDec.Init, uses esi, pOwner:POINTER, hParent:HWND, pDefStruc:PDEF_EdDec
SetObject esi
mov ecx, pDefStruc
assume ecx:PDEF_EdDec
mov eax, [ecx].dStyle
or eax, WS_VISIBLE or WS_CHILD or \
ES_AUTOHSCROLL or ES_NOHIDESEL
invoke CreateWindowEx, [ecx].dExStyle, $OfsCStr("EDIT"), [ecx].pTitle, eax, \
[ecx].sdPosX, [ecx].sdPosY, [ecx].dWidth, [ecx].dHeight, \
hParent, [ecx].dCtlID, hInstance, 0
assume ecx:NOTHING
ACall esi.Init, pOwner, eax
Subclass EdDec ;Uses esi
MethodEnd
; —————————————————————————————————————————————————————
; Method: EdiDec.WndProc
; Purpose: Processing of window messages. Before invoking it, the window must be subclassed.
; Arguments: Arg1: Message identifier.
; Arg2: First message parameter.
; Arg3: Second message parameter.
; Return: eax = This value is the result of the message processing and depends on the message ID.
; Note: Window HANDLE is passed in pSelf (hidden argument).
Method EdDec.WndProc,, uMsg:DWORD, wParam:WPARAM, lParam:LPARAM
LOCAL Buffer[32]:BYTE ; numeros con decimales
SetObject ebx
.if uMsg == WM_CHAR
.if wParam == 8 ; backspace
jmp accept
.endif
.if wParam == "." ; only allow one decimal point
; * * * T R O U B L E H E R E
; Hutch's line
invoke SendMessage, [ebx].hWnd, WM_GETTEXT, sizeof Buffer, ADDR Buffer
mov ecx, sizeof Buffer ; byte count in ecx
lea esi, Buffer ; address in esi
@xxx:
lodsb ; load byte into al
cmp al, "." ; if decimal point already in Buffer
jne @xx1
return 0 ; throw it away
@xx1:
dec ecx
cmp ecx, 0
jne @xxx
jmp accept
.endif
.if wParam < "0"
return 0
.endif
.if wParam > "9"
return 0
.endif
.endif
accept:
GetSubclassingInst EdDec, pSelf
OCall eax::EdDec.Dispatch, pSelf, uMsg, wParam, lParam
MethodEnd
endif
Ok. Not a little problem: the program crash when invoke SendMessage for string retrieving.
Thanks. HSE
if the message is sent from the WndProc to the same window, use PostMesssage
otherwise, i guess you are using pTitle ?
you didn't show us the INVOKE code
in fact, there have been times when even that didn't work for me
a case that i call "frame lock" - although, i imagine it has some other name
the case occured when the code that followed the PostMessage was dependant on the result
my solution was to PostMessage, then start a timer that returned to the WndProc a few mS later
the WM_TIMER code then handled the code that operated on the result
probably not the cleanest solution, but it worked for me
the problem is not time, so much as the code is executed in a seperate call to the WndProc frame
that gave the posted message an opportunity to complete before the result was examined
The invoke is in EdDec.WndProc
.if wParam == "." ; only allow one decimal point
; * * * T R O U B L E H E R E
; Hutch's line
invoke SendMessage, [ebx].hWnd, WM_GETTEXT, sizeof Buffer, ADDR Buffer
mov ecx, sizeof Buffer ; byte count in ecx
lea esi, Buffer ; address in esi
@xxx:
lodsb ; load byte into al
cmp al, "." ; if decimal point already in Buffer
jne @xx1
return 0 ; throw it away
@xx1:
dec ecx
cmp ecx, 0
jne @xxx
jmp accept
.endif
using PostMessage don't crash, but apparently don't retrieve the string. It's the situation you mention, the response is later. But in plain asm (not OOP) SenMessage work very well in \masm32\examples\exampl01\filtinpt\
ObjAsm32 have some sort of dispatching mechanism, but I can't figure out how work in this case.
Thanks.
use PostMessage to get the text
then a call to SetTimer to start a timer
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644906%28v=vs.85%29.aspx (https://msdn.microsoft.com/en-us/library/windows/desktop/ms644906%28v=vs.85%29.aspx)
when you get the WM_TIMER message, you can stop the timer with KillTimer
then process the string in the WM_TIMER handler code
i notice you use EBX and ESI - i hope you preserve them :t :t
Hi HSE
The hint of dedndave is very important and maybe the reason for your crashes. In the EdDec.WndProc you are sending a message to itself and since you didn't preserve esi and ebx it crashes on the second call.
First add a "uses ebx esi" in the method definition and then solve the calling issue invoking the original WndProc, which is stored in EdDec.pPrevWndProc using CallWindowProc.
Biterider
if you use WS_HSCROLL and/or WS_VSCROLL styles when you create the window, you get "system" scroll bars
this is probably the easiest way to get started
if you do that, the client area size is automatically adjusted to account for scroll bar size
i prefer to use CreateWindowEx and the class string "'ScrollBar",0
when you do it that way, you have to measure the scroll bar and store the value
i think there is a GetSystemMetrics value, but i usually measure it with GetWindowRect,hScrollBar,addr rcScrollBar
when you create your own, you also have to size and move the scroll bar when the window is sized
a lot of reading, here - lol
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787529%28v=vs.85%29.aspx (https://msdn.microsoft.com/en-us/library/windows/desktop/bb787529%28v=vs.85%29.aspx)
Thanks Dave!! I have a lot to read in that pages :t.
The timer work perfect, but now I have errors when invoking Send or Post message.
Hi HSE
Have you tried what I wrote before?
Biterider
Hi Biterider!
I'm trying now. Perhaps program was crashing because my very strange setting of objects. Now I'm using a simple demo.
Edited: It's working with SendMessage, obviously was a problem of register preservation because I don't see any other diference. I will try to implement the Dave's sugestion using PostMessage and Timer.
Edited2: I think the PostMessage never return nothing.
PostMessage simply places the message in the queue and returns
WndProc is single-threaded
that means that the message you are currently handling has to exit,
before the posted message will be sent to the WndProc routine
thus, the string will not be retrieved until some time later
if you PostMessage, then try to immediately process the string, it won't work
It's posible that WM_TIMER arrive in 5s with the message still in the queue?
no it will likely be handled very quickly
the message queue pulls messages according to type, and time posted
but, that message is probably handled withing a few milliseconds
Thanks Dave!
Perhaps there is another issue that I don't see. I haven't readed the messages subject yet. Of course I used the MASM32 examples as skeletons for years but never studied the matter enough.
Ten minutes later:
in MSDN "ComCtl32.dll version 6 should not be subclassed (or superclassed) with ANSI window procedures."
Hi HSE
I modified the EdDec file, so it works for you. I intoduced some changes to work with Unicode or ANSI Chars.
Use the DBG macros to inspect the content of your structures and strings.
; ==================================================================================================
; Title: EdDec.inc
; ==================================================================================================
DEF_EdDec struc
dCtlID DWORD ? ;Contol ID
dStyle DWORD ? ;Style
dExStyle DWORD ? ;Extended style
pTitle PSTRING ? ;-> Window title
sdPosX SDWORD ? ;X position
sdPosY SDWORD ? ;Y position
dWidth DWORD ? ;Width
dHeight DWORD ? ;Height
DEF_EdDec ends
PDEF_EdDec typedef ptr DEF_EdDec
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; Object: EdiDec
; Purpose: Implement a thin wrapper around the Edit GDI object.
Object EdDec, EdDecID , WinControl ;Class "EdDec"
RedefineMethod Done
RedefineMethod Init, POINTER, HWND, PDEF_EdDec
RedefineMethod WndProc, DWORD, WPARAM, LPARAM
DefineVariable Buffer1 ,BYTE, 32 dup (0) ; numeros con decimales
ObjectEnd
; ==================================================================================================
if IMPLEMENT
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; Method: Edit.Done
; Purpose: Finalize the Edit object.
; Arguments: None.
; Return: Nothing.
Method EdDec.Done, uses esi
SetObject esi
Unsubclass EdDec ;Uses esi
ACall esi.Done
MethodEnd
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; Method: EdiDec.Init
; Purpose: Initialize the Edit object.
; Arguments: Arg1: -> Owner object.
; Arg2: Parent window HANDLE.
; Arg3: -> DEF_Edit initialization structure.
; Return: Nothing.
Method EdDec.Init, uses esi, pOwner:POINTER, hParent:HWND, pDefStruc:PDEF_EdDec
SetObject esi
mov ecx, pDefStruc
assume ecx:PDEF_EdDec
mov eax, [ecx].dStyle
or eax, WS_VISIBLE or WS_CHILD or \
ES_AUTOHSCROLL or ES_NOHIDESEL
invoke CreateWindowEx, [ecx].dExStyle, $OfsCStr("EDIT"), [ecx].pTitle, eax, \
[ecx].sdPosX, [ecx].sdPosY, [ecx].dWidth, [ecx].dHeight, \
hParent, [ecx].dCtlID, hInstance, 0
assume ecx:NOTHING
ACall esi.Init, pOwner, eax
Subclass EdDec ;Uses esi
MethodEnd
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; Method: EdiDec.WndProc
; Purpose: Processing of window messages. Before invoking it, the window must be subclassed.
; Arguments: Arg1: Message identifier.
; Arg2: First message parameter.
; Arg3: Second message parameter.
; Return: eax = This value is the result of the message processing and depends on the message ID.
; Note: Window HANDLE is passed in pSelf (hidden argument).
Method EdDec.WndProc, uses esi, uMsg:DWORD, wParam:WPARAM, lParam:LPARAM
local cBuffer[32]:CHR ; numeros con decimales
GetSubclassingInst EdDec, pSelf
SetObject esi, EdDec, eax
.if uMsg == WM_CHAR
.if wParam == 8 ; backspace
jmp accept
.endif
.if wParam == "." ; only allow one decimal point
; invoke SendMessage, [esi].hWnd, WM_GETTEXT, lengthof cBuffer, addr cBuffer
invoke CallWindowProc, [esi].pPrevWndProc, [esi].hWnd, WM_GETTEXT, lengthof cBuffer, addr cBuffer
DbgHex eax
DbgStr cBuffer
invoke StrScan, addr cBuffer, 2Eh
.if eax > 0
return 0
.endif
jmp accept
.endif
.if wParam < "0"
return 0
.endif
.if wParam > "9"
return 0
.endif
.endif
accept:
OCall esi::EdDec.Dispatch, pSelf, uMsg, wParam, lParam
MethodEnd
endif
Biterider
under common controls 6 or higher (which includes later versions of windows),
the window hierarchy may be different
they started adding "worker" windows between the control window and the parent
so, when you subclass, you get the proc for the worker, not the control
i played with this a bit, a few years ago
we had a discussion in this thread, in the old forum...
http://www.masmforum.com/board/index.php?topic=16931.0 (http://www.masmforum.com/board/index.php?topic=16931.0)
this post shows the control window hierarchy
http://www.masmforum.com/board/index.php?topic=16931.msg154669#msg154669 (http://www.masmforum.com/board/index.php?topic=16931.msg154669#msg154669)
this post has an attachment to try
as i recall, it attempted to subclass, whether the worker window was present or not
http://www.masmforum.com/board/index.php?topic=16931.msg154917#msg154917 (http://www.masmforum.com/board/index.php?topic=16931.msg154917#msg154917)
you may want to browse through the entire thread to get background info
i have to add.....
anymore, if things get too hairy when subclassing, i have gotten where i will just write my own control from scratch - lol
i am always happier when i have complete control over how something works :biggrin:
Thanks Biterider!
Work perfect :t. I remove the DBG to post the pieces of code, but my sources have so many calls to those macros as working code. Sometimes, somebody could think that Debug Center is the main interface of my program.
Thanks Dave for more material to study. I just begin to figure out how SetWindowLong and related subjects work, when are changing to SetWindowSubclass and other stuff.
Most of the application is working in some rustic way
(http://www.prodanimal.com.ar/imagen/capture-20150728-125518.png)
Quote from: HSE on July 29, 2015, 02:05:50 AMMost of the application is working in some rustic way
Doesn't look rustic at all, Héctor - well done :t
right - it looks great :biggrin:
Thank you for comments! Just to show that posting time is not wasted ... (Ok, not enterely wasted). Some points become programs, some become the necesary background and some... I don't know.
Very interesting Biterider's modifications:
- Using CallWindowProc (Why?) In MSDN: "The CallWindowProc function handles Unicode-to-ANSI conversion. You cannot take advantage of this conversion if you call the window procedure directly". At least one good reason.
- Using CHR instead byte (allowing ANSI or Unicode). Take me a time to find it in OA32_Setup.inc
- Using StrScan function. I discover it's one of the ObjMem functions, and there is a lot of optimized functions there.
Thanks. HSE
you may want to write the app as UNICODE
it's best if you intend to be multi-lingual, later on
much easier to start out that way, than to convert a large app from ANSI to UNICODE
Hi HSE
The OA32 framework is prepared to switch from ANSI to Unicode and back using the "SysSetup" macro. The configuration usually it looks like
SysSetup OOP, WINDOWS, WIDE_STRING, DEBUG(WND)
where WIDE_STRING is used for Unicode and ANSI_STRING for ANSI strings. Internally OA32 works with the masm32 _UNICODE_ symbol that switches "A" and "W" ending APIs like SendMessageA and SendMessageW. In addition, the framework procedures (StrScanA, StrScanW, etc.) and the string handling/generating macros are handled the same way.
Character buffers should be defined as CHR (=character) that can be a word or a byte depending on the SysSetup character setting. Note the use of LengthOf in place of SizeOf operators to get the character count, that is usually passed to the APIs.
Working that way, switching form ANSI to Unicode and back only requires changing the SysSetup setting.
In the EdiDec.WndProc, I used the CallWindowProc ("A" or "W") API to call the previous window message procedure to get the original WM_GETTEXT message behaviour. BTW, WinControl.Dispatch is implemented thas way. If I use SendMessge and press the "." key, I would enter in an infinite loop , since the mesage loop will call the EdiDec.WndProc and this triggers again a message and so on.
Biterider
Hi Biterider!
I discover the switch in SysSetup a time ago when trying to use some Masm32Lib functions and Masm32 examples. Needed is to say that was not very easy to find that. All other characteristics of SysSetup are documented in Help, and because that, I skip SysSetup for a while. Of course at some point I noted the obvious STRING word in some example and read the code of Model.inc.
It's good luck that I have. I you remember my first post, there is examples for a lot of objects, but not LinkedList. I don't remember what demo I was trying to understand, but naturally that one don't have any mention in Help (others, that not interested me so much , are well described).
The code have very usefull comments, perhaps for skilled programmers it's the same as documentation, but I think something is lacking for better initial approach to OA32.
Fantastic the approach of "dual" code. I was thinking that relying on ANSI perhaps become a problem for future code reuse, like Dave remarked.
I don't see any infinite loop (or another problem) using SendMessage, either in plain MASM32 or OA32 (using Dbg macros). But your code is more direct. The "." key trigger 4 loops in my code and only 3 loops in your code.
Thanks. HSE
on a side note....
a tool i use a lot is Spy++ (microsoft)
it normally comes with VS, but may be downloaded seperately
i am using version 8, with no problems
i see they now have versions 10 and 11, as well
http://mdb-blog.blogspot.com/2010/11/microsoft-spy-or-spyxx-for-download.html (http://mdb-blog.blogspot.com/2010/11/microsoft-spy-or-spyxx-for-download.html)
it will show you the window hierarchy, as well as style bits, window sizes, etc
Hi HSE
Your analysis is correct. I added these lines to the beginning of the proc
DbgLine
DbgMessage uMsg
DbgHex wParam
DbgHex lParam
The logging of the CallWindowProc code shows:
WM_KEYDOWN
wParam = 0000006Eh
lParam = 00530001h
──────────────────────────────────────────────────────────────────────
WM_CHAR
wParam = 0000002Eh
lParam = 00530001h
eax = 00000003h
cBuffer = 123
──────────────────────────────────────────────────────────────────────
WM_KEYUP
wParam = 0000006Eh
lParam = C0530001h
while using SendMessage results in
WM_KEYDOWN
wParam = 0000006Eh
lParam = 00530001h
──────────────────────────────────────────────────────────────────────
WM_CHAR
wParam = 0000002Eh
lParam = 00530001h
──────────────────────────────────────────────────────────────────────
WM_GETTEXT
wParam = 00000020h
lParam = 0018FDCCh
eax = 00000003h
cBuffer = 123
──────────────────────────────────────────────────────────────────────
WM_KEYUP
wParam = 0000006Eh
lParam = C0530001h
The difference is evident.
The reason why you don't enter in the infinite loop is the uMsg branch ;)
Biterider
Better analysis than mine, no doubt :t
I just downloaded Spy++ version 8 and it's posible to see that messages. Not so easy to read because some unknown message that don't stop to arrive.
Thanks. HSE