The MASM Forum

Projects => ObjAsm => Topic started by: HSE on July 27, 2015, 04:22:38 AM

Title: Subclassing an OOP edit control
Post by: HSE on July 27, 2015, 04:22:38 AM
Hi !

I have a littlte problem in using the classic Hutch's subclassing example:
Code: [Select]
; ===============================================================
; 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
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 27, 2015, 04:44:30 AM
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
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 27, 2015, 05:29:29 AM
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
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 27, 2015, 05:42:49 AM
The invoke is in EdDec.WndProc

Code: [Select]
        .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.
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 27, 2015, 05:49:35 AM
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
Title: Re: Subclassing an OOP edit control
Post by: Biterider on July 27, 2015, 06:29:04 AM
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
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 27, 2015, 07:01:56 AM
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
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 27, 2015, 07:05:57 AM
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)
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 27, 2015, 08:19:09 AM
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.   
Title: Re: Subclassing an OOP edit control
Post by: Biterider on July 28, 2015, 02:38:40 AM
Hi HSE
Have you tried what I wrote before?

Biterider
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 28, 2015, 03:50:27 AM
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.
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 28, 2015, 06:22:56 AM
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
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 28, 2015, 06:45:49 AM
It's posible that WM_TIMER arrive in 5s with the message still in the queue?
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 28, 2015, 07:00:23 AM
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
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 28, 2015, 07:42:00 AM
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."   
Title: Re: Subclassing an OOP edit control
Post by: Biterider on July 28, 2015, 08:20:29 PM
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.

Code: [Select]
; ==================================================================================================
; 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
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 28, 2015, 08:59:55 PM
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:
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 29, 2015, 02:05:50 AM
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)
Title: Re: Subclassing an OOP edit control
Post by: jj2007 on July 29, 2015, 02:50:09 AM
Most of the application is working in some rustic way

Doesn't look rustic at all, Héctor - well done :t
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 29, 2015, 03:26:37 AM
right - it looks great  :biggrin:
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 30, 2015, 02:44:23 AM
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
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 30, 2015, 03:08:56 AM
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
Title: Re: Subclassing an OOP edit control
Post by: Biterider on July 30, 2015, 05:31:54 PM
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
Code: [Select]
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

 
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 31, 2015, 01:51:33 AM
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

   
Title: Re: Subclassing an OOP edit control
Post by: dedndave on July 31, 2015, 04:07:40 AM
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
Title: Re: Subclassing an OOP edit control
Post by: Biterider on July 31, 2015, 05:28:50 AM
Hi HSE
Your analysis is correct. I added these lines to the beginning of the proc
Code: [Select]
  DbgLine
  DbgMessage uMsg
  DbgHex wParam
  DbgHex lParam

The logging of the CallWindowProc code shows:

Code: [Select]
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
Code: [Select]
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
Title: Re: Subclassing an OOP edit control
Post by: HSE on July 31, 2015, 08:03:12 AM
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