Why does BINDEVENT not work with StatusBar_Change?

A customer had a question and sent some code:

PUBLIC oHandler

oHandler = NEWOBJECT("StatusBarText")

?BINDEVENT(_VFP,[STATUSBAR],oHandler,[STATUSBAR_CHANGE],1)

_VFP.StatusBar = "AAAA" && triggers handler

SET MESSAGE TO "1111" && does not trigger handler

*navigate menus: does not trigger handler

DEFINE CLASS StatusBarText AS Session

      PROCEDURE StatusBar_Change

            ?PROGRAM(),_vfp.StatusBar

      RETURN

ENDDEFINE

This code demonstrates 3 ways to change the status bar text, but only one fires the hooked event.

The status bar text can change in various ways, which means there are various code paths internal to VFP that are followed. When changing the status bar text directly via direct assignment to _VFP.Statusbar, the normal VFP code (the object manager) that changes an object’s property gets invoked, which knows to check for and execute any BindEvent code.

When the user activates a menu, the WM_ENTERIDLE Notification is sent to VFP’s Window procedure with WPARAM = MSGF_MENU, which tells VFP to draw the menu message on the status bar.

When the user executes SET MESSAGE TO, the internal VFP object manager is completely bypassed to change the status bar text.

The BINDEVENT feature allows the user to bind events to when an object property or method is invoked or changed. When I implemented the feature, I modified the internal VFP object manager to check for user bound events. I paid no attention to the SET MESSAGE TO and the WM_ENTERIDLE code, which had been in existence for several versions.

I suppose one could argue that this is a bug, but the documentation says:

to bind events, properties, or methods from native Visual FoxPro objects to other Visual FoxPro objects.

There is a simple workaround: use Bindevents to bind to the WM_ENTERIDLE message and change SET MESSAGE TO cNewMessage to _VFP.StatusBar=cNewMessage

#define GWL_WNDPROC (-4)

#define WM_ENTERIDLE 0x0121

#define MSGF_DIALOGBOX 0

#define MSGF_MESSAGEBOX 1

#define MSGF_MENU 2

#define MSGF_SCROLLBAR 5

PUBLIC oHandler

oHandler = NEWOBJECT("StatusBarText")

?BINDEVENT(_VFP,[STATUSBAR],oHandler,[STATUSBAR_CHANGE],1)

?BINDEVENT(_vfp.HWnd,WM_ENTERIDLE,oHandler, "HandleMsg",5)

_VFP.StatusBar = "AAAA" && triggers handler

SET MESSAGE TO TRANSFORM(SECONDS()) && does not trigger handler

DEFINE CLASS StatusBarText AS Session

      dwOrigWindProc=0

      PROCEDURE init

            DECLARE integer GetWindowLong IN WIN32API ;

                  integer hWnd, ;

                  integer nIndex

            DECLARE integer CallWindowProc IN WIN32API ;

                  integer lpPrevWndFunc, ;

                  integer hWnd,integer Msg,;

                  integer wParam,;

                  integer lParam

            THIS.dwOrigWindProc =GetWindowLong(_VFP.HWnd,GWL_WNDPROC)

      PROCEDURE HandleMsg(hWnd as Integer, msg as Integer, wParam as Integer, lParam as Integer)

            LOCAL nRetvalue

            nRetvalue=0

* ?PROGRAM(),msg,TRANSFORM(wParam,"@0x"),TRANSFORM(lParam,"@0x")," "

            DO CASE

            CASE msg =WM_ENTERIDLE

                  IF wParam = MSGF_MENU

                        *call default processing to set menu message

                        nRetvalue=CallWindowProc(this.dwOrigWindProc,hWnd,msg,wParam,lParam)

                        ?PROGRAM(),_vfp.StatusBar

                  ENDIF

            ENDCASE

            RETURN nRetvalue

      PROCEDURE StatusBar_Change

            ?PROGRAM(),_vfp.StatusBar

      RETURN

ENDDEFINE

See also How to hook command window keystrokes.

Why doesn't drag/drop work with BindEvents?

Is this a bug in BindEvents?