News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Subclassing an OOP edit control

Started by HSE, July 27, 2015, 04:22:38 AM

Previous topic - Next topic

Biterider

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

dedndave

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

this post shows the control window hierarchy

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

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:

HSE

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

Equations in Assembly: SmplMath

jj2007

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

dedndave


HSE

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
Equations in Assembly: SmplMath

dedndave

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

Biterider

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


HSE

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

   
Equations in Assembly: SmplMath

dedndave

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

it will show you the window hierarchy, as well as style bits, window sizes, etc

Biterider

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

HSE

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
Equations in Assembly: SmplMath