News:

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

Main Menu

Subclass button control

Started by newAsm, June 04, 2013, 07:36:21 PM

Previous topic - Next topic

newAsm

Hi,

I am learning how to subclass a control. I have read some of the examples and I think I have followed the mechanism to subclass, using SetWindowLong. The subclassed routine is never called, only the button event in the main winProc is called. Attached us my example and do please comment of my mistakes. Thanks ..newAsm

dedndave

i didn't do a thorough read, but i do see some problems with regard to use of registers

http://masm32.com/board/index.php?topic=1989.msg20743#msg20743

as explained in that post, EBX, EBP, ESI, and EDI should be preserved inside WndProc

because you already have the code written, a simple fix might be....
WndProc proc USES EBX ESI hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

this code presents another problem
      Case WM_COMMAND

.if eax == WM_COMMAND
        mov ebx,wParam
        and ebx,0FFFFh
shr ebx,16

cmp bx,BN_CLICKED ; button clicked


first, "Case WM_COMMAND" says "execute this code if uMsg is WM_COMMAND"
the next line, ".if eax.....", will never compare true because EAX does not contain the uMsg value
in any case, the 2 lines are redundant
just remove the ".if eax...." line to fix that one

second, for the WM_COMMAND message, wParam is split into low and high words

http://msdn.microsoft.com/en-us/library/windows/desktop/ms647591%28v=vs.85%29.aspx

when the message is received from a control, the high word has the notification code and the low word has the control ID
your code tests BN_CLICKED, but never checks the control ID
you could do it this way
        movzx   eax,word ptr wParam[2]   ;EAX = wParam high word, zero extended
        movzx   edx,word ptr wParam      ;EDX = wParam low word, zero extended
        .if eax==BN_CLICKED
            .if edx==100      ;button control ID = 100
                ;do button stuff here
            .endif
        .endif


as it happens, the value for BN_CLICKED is 0   :biggrin:
so really, you only need to compare wParam (as a dword) to the button identifier number
if the value of the high word is something other than BN_CLICKED, that test will compare false
        .if wParam==100      ;(button control ID = 100) + (BN_CLICKED * 65536)
            ;do button stuff here
        .endif


the code may have other issues, but that should help you get going

newAsm

Thanks dedndave,

I tried your suggestion and I made some changes to both winProc and procBtnCtrl. The program does not work now. The issue is probably I may not understand properly. I have issue using

movzx  eax,wparam+2

The assembler issued an error. As a result,

I changed it to:

      case WM_COMMAND
      mov      eax,wParam                     ;
      mov      edx,eax
      shr           eax,16
      and           edx,0FFFFh
      
                  ;cmp    bx,BN_CLICKED             ; button clicked
            .if eax == BN_CLICKED
                  cmp    eax,100                ; button clicked?
                  jne    Noclick               ; no, Exit
                     invoke   MessageBox,hWnd,ADDR s_MClicked, ADDR szDisplayName,MB_OKCANCEL
                     ;return 0
            .endif
;=================================================================

for procBtnBtrl
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

procBtnCtrl      proc   hwnd:DWORD, uMsg:DWORD,wParam:DWORD,lParam:DWORD
         mov    eax,uMsg

               .if uMsg == WM_COMMAND

                  mov      eax,wParam                     ;
                  mov      edx,eax
                  shr    eax,16
                  and    edx,0FFFFh

                  cmp    ax,BN_CLICKED             ; button clicked
                  jne    procBtnOut               ; no, Exit
                  
                  cmp    dx,100
                  jne    procBtnOut
                     invoke   MessageBox,hWnd,ADDR s_Clicked, ADDR szDisplayName,MB_OKCANCEL
                     ;return   0
               .endif
      procBtnOut:               
               invoke CallWindowProc,lpBtnProc ,hwnd,uMsg,wParam,lParam
               ret
procBtnCtrl      endp

Did I do something wrong? I intend to subclass the button to have it own event handler. Thanks appreciate your quick response.

newAsm

dedndave

sorry about that
it should be
movzx eax,word ptr wParam+2
or
movzx eax,word ptr wParam[2]
the "word ptr" is required (and on the EDX instruction, also)

C:\Masm32\Examples\exampl01\bmbutton

it's a simple example of a subclassed button control

jj2007

You can also do direct mem to immediate comparisons:
      Case WM_COMMAND
        ; movzx eax, word ptr wParam+2        ; use if you prefer to have high and
        ; movzx edx, word ptr wParam          ; low words in registers
        .if word ptr wParam+2 == BN_CLICKED
                cmp word ptr wParam, 100      ; button clicked?
                jne Noclick                   ; no, Exit
                MsgBox 0, "You clicked the button", "Button main proc:", MB_OK
                ;return 0
        .endif

... or, shorter:
      Case WM_COMMAND
        .if word ptr wParam+2 == BN_CLICKED && word ptr wParam==100
                MsgBox 0, "You clicked the button", "Button main proc:", MB_OK
        .endif


MsgBox is a Masm32 macro aimed at improving the readability of your code.

qWord

Just as side note: if I'm not wrong,  the class style CS_BYTEALIGNWINDOW and CS_BYTEALIGNCLIENT have no meaning on nowadays computers. The flags are only useful when the color depth is 1 or 4 bits. In that case the flags modifies the position, width and height in such way that the corresponding regions lies on a byte boundaries and have width/height of N*8 bits - this allows (for example) byte wise copying without the need of expensive bit manipulation.
MREAL macros - when you need floating point arithmetic while assembling!

newAsm

Thanks dedndave, qWord, & ji2007,

I will look at the example and will update the information you have all kindly so provided.

On last question, what was wrong with my subclass function (procBtnCtrl). In WM_CREATE, even though SetWindowLong was invoked, the sub-classed callback function was not called. Did I do the SetWindowLong correctly?

Thanks you very much.
newAsm

Dubby

As far as I know, the WM_CREATE is called whenever the control is created.. if you want to intercept it, you'll need superclassing..

newAsm

Hi there,

I looked through bmbutton in \masm32\examples\exampl02 directory. Just to check with you on this button clicked:

1.  Which is better to use, BN_CLICKED, or WM_LBUTTONDOWN or WM_LBUTTONUP?
2.  Do they mean the same?
3.  For any button click, as in most button controls, the click tend to be left button. Would WM_LBUTTONDOWN or up be better?

Thanks..newAsm


dedndave

he's got it right
that part looks like it should be ok

qWord....
the byte-align thing is a throw-back to Iczelion days   :P
parts of the code were copied from Hutch's ProStart program code

give me a little time to look over the code more thoroughly.....

dedndave

for a button, you are going to get WM_COMMAND/BN_CLICKED

WM_LBUTTONDOWN and WM_LBUTTONUP are useful if you want to detect the mouse click directly, in the client area
notice that the window client area is handled in WndProc
the button is actually a window on it's own, and has it's own WndProc to detect mouse clicks

if the mouse is over the button, the click messages go to the WndProc for the buttons

qWord

Even not a problem, but the DefWindowProc is misplaced in WndProc: it should be placed in the default case of the switch/case block.
The subclass does work. However, the WM_COMAND in procBtnCtrl is useless, because controls does not send notifications to themselves - they notify their parent instead.
The problem for the WndProc is:
cmp eax,100 ; button clicked?it should be EDX!
MREAL macros - when you need floating point arithmetic while assembling!

dedndave

i see a basic problem - that is that your WndProc must return a result to the OS
the result may be different for different messages, but is most often 0

if you do not return 0 from WM_CREATE, window creation is aborted - and you have no window

newAsm

Hi qWord,

Thanks for your eagle eye - you are right. I corrected it.

After I created the button in WM_CREATE, save the button handle, and then invoke SetWindowLong to subclass the button, the subclassed button callback routine is never called. The main WndProc click is activated. I return a zero but the main WndProc is called. Attached is the updated program.

In bmbutton, WM_LBUTTONUP is used in the button callback routine and not in the main WndProc routine.

Thanks..newAsm



dedndave