A handle to a call object allows you to do the following dynamically:
The following fragment dynamically invokes an external procedure non-persistently:
DEFINE VARIABLE hCall AS HANDLE NO-UNDO. CREATE CALL hCall. /* Invoke hello.p non-persistently */ hCall:CALL-NAME = "hello.p". /* Sets CALL-TYPE to the default */ hCall:CALL-TYPE = PROCEDURE-CALL-TYPE hCall:NUM-PARAMETERS = 1. hCall:SET-PARAMETER(1, "CHARACTER", "INPUT", "HELLO WORLD"). hCall:INVOKE. /* Clean up */ DELETE OBJECT hCall. |
The following fragment dynamically invokes an external procedure persistently, then dynamically invokes one of its internal procedures:
DEFINE VARIABLE hCall AS HANDLE NO-UNDO. CREATE CALL hCall. /* Invoke persis.p persistently */ hCall:CALL-NAME = "persis.p". hCall:CALL-TYPE = PROCEDURE-CALL-TYPE hCall:PERSISTENT = TRUE. /* IN-HANDLE automatically set to the handle of the persistent procedure */ hCall:INVOKE. /* Invoke internal-persis-proc in persis.p */ hCall:CALL-NAME = "internal-persis-proc". hCall:NUM-PARAMETERS = 1. hCall:SET-PARAMETER(1, "INTEGER", "INPUT", 333). hCall:INVOKE. /* Clean up */ DELETE PROCEDURE hCall:IN-HANDLE. DELETE OBJECT hCall. |
As an alternative to setting the PERSISTENT attribute, a procedure can be run persistently by setting the PROCEDURE-TYPE to "PERSISTENT". Regardless of which attribute is used, the other will automatically be set.
The following fragment dynamically invokes an external procedure as single-run, then dynamically invokes one of its internal procedures:
DEFINE VARIABLE hCall AS HANDLE NO-UNDO. CREATE CALL hCall. /* Invoke single.p as a single-run procedure*/ hCall:CALL-NAME = "single.p". hCall:CALL-TYPE = PROCEDURE-CALL-TYPE hCall:PROCEDURE-TYPE = SINGLE-RUN. /* IN-HANDLE automatically set to the handle of the single-run procedure */ hCall:INVOKE. /* Invoke internal-single-proc in single.p */ hCall:CALL-NAME = "internal-single-proc". hCall:NUM-PARAMETERS = 1. hCall:SET-PARAMETER(1, "INTEGER", "INPUT", 333). hCall:INVOKE. /* Clean up */ DELETE PROCEDURE hCall:IN-HANDLE. DELETE OBJECT hCall. |
The previous fragment could also be used to dynamically invoke an external procedure as singleton by changing PROCEDURE-TYPE to "SINGLETON".
The following fragment uses a single call object handle multiple times:
DEFINE VARIABLE hCall AS HANDLE NO-UNDO. CREATE CALL hCall. /* Invoke hello.p non-persistently */ ASSIGN hCall:CALL-NAME = "hello.p" hCall:CALL-TYPE = PROCEDURE-CALL-TYPE hCall:NUM-PARAMETERS = 1. hCall:SET-PARAMETER(1, "CHARACTER", "INPUT", "HELLO WORLD"). hCall:INVOKE. /* Reset the call object handle */ hCall:CLEAR. /* Invoke persis.p persistently */ ASSIGN hCall:CALL-NAME = "persis.p" hCall:CALL-TYPE = PROCEDURE-CALL-TYPE hCall:PERSISTENT = TRUE. /* IN-HANDLE automatically set to the handle of the persistent procedure */ hCall:INVOKE. /* Invoke internal-persis-proc in persis.p */ ASSIGN hCall:CALL-NAME = "internal-persis-proc" hCall:CALL-TYPE = PROCEDURE-CALL-TYPE hCall:NUM-PARAMETERS = 1. hCall:SET-PARAMETER( 1, "INTEGER", "INPUT", 333). hCall:INVOKE. /* Clean up */ DELETE PROCEDURE hCall:IN-HANDLE. DELETE OBJECT hCall. |
The following fragment gets an attribute:
/* Get title of frame */ ASSIGN hCall:IN-HANDLE = myframe_handle hCall:CALL-TYPE = GET-ATTR-CALL-TYPE hCall:CALL-NAME = "TITLE". hCall:INVOKE. Mytitle = hCall:RETURN-VALUE. |
The following fragment sets an attribute:
/* Set SESSION:NUMERIC-FORMAT to "european" */ ASSIGN hCall:IN-HANDLE = "session" hCall:CALL-TYPE = SET-ATTR-CALL-TYPE hCall:CALL-NAME = "numeric-format" hCall:NUM-PARAMETERS = 1. hCall:SET-PARAMETER( 1, "CHAR", "INPUT", "european"). hCall:INVOKE. |
The following fragment drives the call object's INVOKE( ) method from a TEMP-TABLE:
/* Suppose hRuntt is a temp-table that has one record with the following fields: parm_1 parm_2 ... parm_n run-name nparms datatypes, extent nparms iomodes, extent nparms */ DEFINE INPUT PARAMETER TABLE-HANDLE hRuntt. DEFINE VARIABLE hDtypes AS HANDLE NO-UNDO. DEFINE VARIABLE hIOmodes AS HANDLE NO-UNDO. DEFINE VARIABLE hCall AS HANDLE NO-UNDO. DEFINE VARIABLE ix AS INTEGER NO-UNDO. ASSIGN hDtypes = hRuntt:BUFFER-FIELD("datatypes") hIOmodes = hRuntt:BUFFER-FIELD("iOmodes"). hRuntt:FIND-FIRST. CREATE CALL hCall. ASSIGN hCall:CALL-NAME = hRuntt:BUFFER-FIELD("run-name"):BUFFER-VALUE hCall:NUM-PARAMETERS = hRuntt:BUFFER-FIELD("nparms"):BUFFER-VALUE. DO ix = 1 to hCall:NUM-PARAMETERS: hCall:SET-PARAMETER(ix, hDtypes:BUFFER-VALUE(ix), hIOmodes:BUFFER-VALUE(ix), hRuntt:BUFFER-FIELD(ix):BUFFER-VALUE). END. hCall:INVOKE. DELETE OBJECT hCall. /* If there are output parms, get values from hRuntt:BUFFER-FIELD(ix) */ |
The following fragment implements an ABL function, sleep, which causes the AVM to sleep for a specified number of milliseconds:
FUNCTION sleep RETURNS INTEGER (INPUT msecs AS INTEGER): DEFINE VARIABLE cFunction AS CHARACTER NO-UNDO INITIAL "sleep". DEFINE VARIABLE cLibrary AS CHARACTER NO-UNDO INITIAL "libc.so.1". DEFINE VARIABLE hCall AS HANDLE NO-UNDO. CREATE CALL hCall. ASSIGN cLibrary = "kernel32.dll" WHEN OPSYS = "WIN32" cFunction = "Sleep" WHEN OPSYS = "WIN32" hCall:CALL-NAME = cFunction hCall:LIBRARY = cLibrary hCall:CALL-TYPE = DLL-CALL-TYPE hCall:NUM-PARAMETERS = 1. hCall:SET-PARAMETER(1, "LONG", "INPUT", msecs). hCall:INVOKE( ). DELETE OBJECT hCall. RETURN msecs. END FUNCTION. |
Note that the code checks to determine on which OS it is running, and invokes the appropriate Windows DLL or UNIX shared library.
If you already know the name of the attribute or procedure, you know its syntax, since the name implies certain syntax. And if you know the syntax, you know the calling sequence, since the syntax defines the calling sequence. And if you know the calling sequence, you can use widget:attribute or widget:method syntax—and avoid the call object altogether.
For example:
For example:
Since the call object, by default, goes into the SESSION widget pool, not into the closest unnamed widget pool, to delete a call object created when the IN widget-pool option is not used, use the DELETE OBJECT handle syntax explicitly.