| **Navigation:**  [[introduction.htm|Language Reference]] > App A - DDE, OLE, and OCX > OLE (.OCX) Custom Controls >====== Callback Functions ====== | [[ ocx control properties.htm|{{btn_prev_n.gif|Previous page}}]][[introduction.htm|{{btn_home_n.gif|Return to chapter overview}}]][[calling ole object methods.htm|{{btn_next_n.gif|Next page}}]] | | || [[callback functions.htm#ocxeventprocessor|OCX Event Processor]] [[callback functions.htm#ocx property edit controller|OCX Property Edit Controller]] [[callback functions.htm#ocx property change|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**