Navigation: Language Reference > App A - DDE, OLE, and OCX > OLE (.OCX) Custom Controls >====== Callback Functions ====== | |
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