Callback Functions

Top  Previous  Next

OCX Event Processor

OCX Property Edit Controller

OCX Property Change

Callback functions are a standard part of Windows programming in most programming languages. A callback function is a PROCEDURE that you (the programmer) write to handle specific situations that the operating system deems the programmer may need to deal with. A callback function is called by the operating system whenever it needs to pass on these situations. Therefore, a callback function does not appear to be part of the logic flow, but instead appears to be separate and "magic" without any logical connection to other procedures in your program.

The Clarion for Windows language does not force you to write your own callback functions for all the common tasks that other programming languages require you to, since the Clarion runtime library and the ACCEPT loop handles most of that for you. However, since .OCX controls are written in other languages that do require callback functions, you will need to write your own to deal with the events and other programming issues for the .OCX controls you use in Clarion programs. Since CLASS methods have an implicit first parameter of the class name, they cannot be used as callbacks.

There are three callback functions you can write for your .OCX controls: an event processor, a property edit controller, and a property change handler. You may name these whatever you want, but they have specific requirements for the parameters that they receive.

OCX Event Processor Callback Function

The prototype for the event processor must be:

 

OcxEventFuncName PROCEDURE(*SHORT,SIGNED,LONG),LONG

 

The parameters it receives from the operating system are:

*SHORT

A Reference parameter to pass onto the following other OCX library procedures: OCXGETPARAM, OCXGETPARAMCOUNT, and OCXSETPARAM as their first parameter.

SIGNED

The field number for the control. This is the same number that is represented by the control's field equate label.

LONG

The number of the .OCX event. Equates for some pre-defined event numbers are contained in the OCXEVENT.CLW file.

The LONG return value indicates to the operating system whether any further processing is necessary. Returning zero (0) indicates some further processing is necessary (like updating a USE variable or unchecking a radio button), while returning any other value indicates processing is complete.

Processing the events generated by an .OCX control must occur quickly, since some events have critical timing. Therefore, there should be no user interaction possible within this procedure (such as WINDOWs, ASK statements, or MESSAGE procedures). The code should process only what it needs to, just as quickly as possible (usually, this means eliminating all mouse events).

OCX Property Edit Controller Callback Function

The prototype for the property edit controller must be:

 

OcxPropEditFuncName PROCEDURE(SIGNED,STRING),LONG

 

The parameters it receives from the operating system are:

SIGNED

The field number for the control. This is the same as the number represented by the control's field equate label.

STRING

The name of the property about to be edited.

The LONG return value indicates to the operating system whether permission to edit the property has been granted by the callback function. If the procedure returns zero (0), then permission is denied and the user is not allowed to edit the property. If the procedure returns any value other than zero (0), then permission is granted and the user is allowed to edit the property.

OCX Property Change Callback Function

The prototype for the property change handler must be:

 

OcxPropChangeProcName PROCEDURE(SIGNED,STRING)

 

The parameters it receives from the operating system are:

SIGNED

The field number for the control. This is the same as the number represented by the control's field equate label.

STRING

The name of the changed property.

This procedure is called when a property has been changed.

Example:

! This program uses the Calendar OCX that Microsoft ships with its Access95

! product (specifically, the one in MS Office Professional for Windows 95).

PROGRAM

MAP

INCLUDE('OCX.CLW')

EventFunc  PROCEDURE(*SHORT Reference,SIGNED OleControl,LONG CurrentEvent),LONG

PropChange PROCEDURE(SIGNED OleControl,STRING CurrentProp)

PropEdit   PROCEDURE(SIGNED OleControl,STRING CurrentProp),LONG

END

INCLUDE('OCXEVENT.CLW')                          !Constants that OCX events use

INCLUDE('ERRORS.CLW')                            !Include errorcode constants

 

GlobalQue QUEUE                                   !Event and change display queue

F1         STRING(255)

         END

 

SaveDate FILE,DRIVER('TopSpeed'),PRE(SAV),CREATE

Record    RECORD

DateField  STRING(10)

         END

        END

 

MainWin WINDOW('OCX Demo'),AT(,,350,200),STATUS(-1,-1),SYSTEM,GRAY,MAX,RESIZE

        MENUBAR

         MENU('&File')

          ITEM('Save Date to File'),USE(?SaveObjectValue)

          ITEM('Retrieve Saved Date'),USE(?GetObject)

          ITEM('E&xit'),USE(?exit)

         END

         MENU('&Object')

          ITEM('About Box'),USE(?AboutObject)

          ITEM('Set Date to TODAY'),USE(?SetObjectValueToday)

          ITEM('Set Date to 1st of Month'),USE(?SetObjectValueFirst)

         END

         ITEM('&Properties!'),USE(?ActiveObj)

        END

        LIST,AT(237,6,100,100),USE(?List1),HVSCROLL,FROM(GlobalQue)

        OLE,AT(5,10,200,150),USE(?OcxObject)

        END

       END

CODE

OPEN(SaveDate)

IF ERRORCODE()                                  !Check for error on Open

 IF ERRORCODE() = NoFileErr                     !if the file doesn't exist

  CREATE(SaveDate)                              !create it

  IF ERRORCODE() THEN HALT(,ERROR()) END

  OPEN(SaveDate)                                !then open it for use

  IF ERRORCODE() THEN HALT(,ERROR()) END

 ELSE

  HALT(,ERROR())

 END

END

OPEN(MainWin)

?OcxObject{PROP:Create} = 'MSACAL.MSACALCtrl.7' !MS Access 95 Calendar OCX control

IF RECORDS(SaveDate)                            !Check for existing saved record

 SET(SaveDate)                                  !and get it

 NEXT(SaveDate)

 IF ERRORCODE() THEN STOP(ERROR()).

 POST(EVENT:Accepted,?GetObject)

ELSE

 ADD(SaveDate)                                  !or add one

 IF ERRORCODE() THEN STOP(ERROR()).

END

IF ?OcxObject{PROP:OLE}                         !Check for an OLE Object

 GlobalQue    = 'An Object is in the OLE control'

 ADD(GlobalQue)

 IF ?OcxObject{PROP:Ctrl}                       !See if Object is an OCX

  GlobalQue    = 'It is an OCX Object'

  ADD(GlobalQue)

 END

END

DISPLAY

OCXREGISTEREVENTPROC(?OcxObject,EventFunc)      !Register Event processing Callback

OCXREGISTERPROPCHANGE(?OcxObject,PropChange)    !Register Property Change Callback

OCXREGISTERPROPEDIT(?OcxObject,PropEdit)        !Register Property Edit Callback

?OcxObject{PROP:ReportException} = 1            !Enable the OCX's error reporting

ACCEPT

 CASE EVENT()

 OF EVENT:Accepted

  CASE FIELD()

  OF ?Exit

   POST(EVENT:CloseWindow)

  OF ?AboutObject

   ?OcxObject{'AboutBox'}                       !Display control's About Box

  OF ?SetObjectValueToday

   ?OcxObject{'Value'} = FORMAT(TODAY(),@D1)    !Set control to TODAY's date

  OF ?SetObjectValueFirst

   ?OcxObject{'Value'} = MONTH(TODAY()) & '/1/' & SUB(YEAR(TODAY()),3,2)

  OF ?SaveObjectValue                           !Save control's value to file

   SAV:DateField = ?OcxObject{'Value'}

   PUT(SaveDate)

   IF ERRORCODE() THEN STOP(ERROR()).

  OF ?GetObject                                 !Set control's value from file

   ?OcxObject{'Value'} = SAV:DateField

  OF ?ActiveObj

   ?OcxObject{PROP:DoVerb} = 0                  !Activate control's property dialog

  END

 END

END

 

!Event processing callback function

EventFunc   PROCEDURE(*SHORT Reference,SIGNED OleControl,LONG CurrentEvent)

Count       LONG

Res         CSTRING(200)

Parm        CSTRING(30)

CODE

IF CurrentEvent <> OCXEVENT:MouseMove           !Eliminate mouse move events

 Res = 'Event: ' & OleControl{PROP:LastEventName}

 LOOP Count = 1 TO OCXGETPARAMCOUNT(Reference)  !Cycle through all parameters

  Parm = OCXGETPARAM(Reference,Count)           !getting each parameter name

  Res = CLIP(Res) & ' - ' & Parm                !and concatenate them together

 END

 GlobalQue = Res                                !Assign to a global QUEUE

 ADD(GlobalQue)                                 !and add the entry

 DISPLAY

END

RETURN(True)

 

!Change property callback

PropChange  PROCEDURE(SIGNED OleControl,STRING CurrentProp)

CODE

GlobalQue = 'PropChange: ' & CurrentProp & ' = ' & OleControl{CurrentProp}

                                                !Assign to a global QUEUE

ADD(GlobalQue)                                  !add the entry for display

IF ERRORCODE() THEN STOP(ERROR()).

 

!Edit property callback

PropEdit    PROCEDURE(SIGNED OleControl,STRING CurrentProp)

CODE

!Ask permission to change value:

IF MESSAGE('Allow?','Change',ICON:Question,BUTTON:Yes+BUTTON:No,BUTTON:Yes,1) = BUTTON:Yes

 RETURN(1)                                      !Allow the change

ELSE

 RETURN(0)                                      !Dis-allow the change

END