PROCEDURE statement

Defines an internal procedure as an ABL procedure or declares an internal procedure prototype for an external routine in a Windows dynamic link library (DLL) or UNIX shared library, or for an internal ABL procedure defined in an external procedure that is itself a super procedure of the declaration procedure. The following syntax boxes describe the syntax for each use of the statement, beginning with an internal procedure definition.

Syntax

PROCEDURE proc-name[ PRIVATE ] :
  [procedure-body]

This is the syntax to declare an internal procedure prototype for a routine in a Windows DLL or UNIX shared library, or for an internal ABL procedure defined in a super procedure:

PROCEDURE proc-name 
  {   EXTERNAL "dllname" [ CDECL | PASCAL | STDCALL ]
         [ ORDINAL n ][ PERSISTENT ][ THREAD-SAFE ]
    | IN SUPER } :
  [ procedure-body ]
proc-name
The name of the internal procedure.

To define the name of an internal ABL procedure that is an event handler for ActiveX controls (OCX event procedure), you must specify proc-name according to the following syntax:

{control-frame-name.control-name.event-name | ANYWHERE.event-name}

For more information on naming event handlers for ActiveX controls using this syntax, see the notes for this reference entry.

EXTERNAL "dllname"
Declares the internal procedure as a Windows DLL or UNIX shared library routine. The dllname argument, specified as a string literal, is the name of the DLL or library containing the routine. The value of dllname can contain Unicode characters. See OpenEdge Development: Internationalizing Applications for more information about Unicode.
CDECL
Tells ABL to use the C calling convention when accessing the routine.
PASCAL
Supported only for backward compatibility. This option is not valid for SpeedScript.
STDCALL
Tells ABL to use the standard Windows calling convention when accessing the routine. This is the default.
Note: The 64-bit Windows GUI and character clients ignore the CDECL, PASCAL, and STDCALL calling conventions if they are specified. The 64-bit Windows GUI and character clients always use the standard 64-bit FASTCALL calling convention.
ORDINAL n
Specifies the number of the DLL entry point (the nth routine) to invoke. If you use the ORDINAL option, then proc-name can specify any name used in the corresponding RUN statement to reference the routine. If you omit the ORDINAL option, proc-name specifies which DLL routine you want to invoke.

For UNIX shared library routines, this option does not apply and is ignored.

PERSISTENT
Specifies that the DLL or shared library routine should remain loaded in memory until the AVM exits or the session executes the RELEASE EXTERNAL statement.
THREAD-SAFE
Specifies that the DLL or shared library routine is thread safe. When a DLL or shared library is marked as THREAD-SAFE, multiple sessions can access it simultaneously. This option is only valid when running an application on an instance of the Pacific Application Server for OpenEdge; otherwise, this option does not apply and is ignored.
PRIVATE
Indicates the following about the internal procedure:
  • It cannot be invoked from an external procedure—that is, from a procedure file external to the current procedure file.
  • The INTERNAL-ENTRIES attribute on the procedure that defines it does not provide its name (unless the procedure that defines it is the current procedure file).
  • The GET-SIGNATURE method on the procedure that defines it does not provide its signature (unless the procedure that defines it is the current procedure file).
IN SUPER
Declares that the definition of the internal procedure resides in a super procedure. For more information on super procedures, see OpenEdge Development: ABL Handbook.
procedure-body
The body of an internal procedure definition. Define procedure-body using the following syntax:
procedure-logic
       .
       .
       .
    [ catch-block [ catch-block...]]
    [ finally-block ]
[ END [ PROCEDURE ] . ]
procedure-logic
Zero or more ABL statements, depending on the internal procedure definition or declaration. Each logic statement must end in with a period (.).

If you declare the internal procedure as an ABL procedure, these statements can include executable statements and non-executable statements including definitions of run-time parameters (using the DEFINE PARAMETER statement), local program variables, frames, widgets, and buffers. Any such objects you define within the internal procedure remain in effect only for the life of the internal procedure.

If you are defining the internal procedure for use as an event procedure to handle asynchronous remote requests, you can specify run-time parameters as INPUT only. (Any other type of parameter generates a run-time error.) Each INPUT parameter must correspond in order and data type with an OUTPUT (or INPUT-OUTPUT) parameter as defined in the remote procedure that executes the request. For more information on working with asynchronous remote requests and event procedures, see OpenEdge Application Server: Developing AppServer Applications.

If you declare the internal procedure as a DLL or UNIX shared library routine (using the EXTERNAL option), these statements can include only DEFINE PARAMETER statements.

For more information on accessing DLL or UNIX shared library routines from ABL, see the chapter on DLLs in OpenEdge Development: Programming Interfaces.

catch-block
Specifies a CATCH statement that defines error handling code for one or more error types. A DO block does not have any default error handling. Therefore, a DO block must have error handling options specified such that it becomes an undoable block. Otherwise, ABL generates a compiler warning. For more information on catch-block, see the CATCH statement reference entry.
finally-block
Specifies a FINALLY statement that defines the processing that must occur after all other processing in the block occurs. For more information on finally-block, see the FINALLY statement reference entry.
END [ PROCEDURE ]
Specifies the end of the internal procedure body. If procedure-logic contains one or more statements, you must end the internal procedure body with the END statement.

Example

The following example declares an ABL internal procedure that computes the factorial of an integer entered as an INPUT parameter. The result is returned as an OUTPUT parameter. Note that the following procedure calls itself recursively to obtain the result:

r-factrl.p

DEFINE VARIABLE FactorialResult AS INTEGER NO-UNDO FORMAT ">>>,>>>,>>9".
DEFINE VARIABLE FactorialInput  AS INTEGER NO-UNDO.

REPEAT:
  SET FactorialInput VALIDATE(FactorialInput <= 12 AND FactorialInput >= 0,
    "Value must be between 0 and 12.").
  RUN Factorial (INPUT FactorialInput, OUTPUT FactorialResult).
  DISPLAY FactorialResult.   
END.

PROCEDURE Factorial:
  DEFINE INPUT PARAMETER  PTerm           AS INTEGER NO-UNDO.
  DEFINE OUTPUT PARAMETER FactorialResult AS INTEGER NO-UNDO.

  DEFINE VARIABLE WorkingResult AS INTEGER NO-UNDO.
   
  IF PTerm <= 1 THEN DO:
    FactorialResult = 1.
    RETURN.
  END.
  ELSE DO:
    RUN Factorial (INPUT PTerm - 1, OUTPUT WorkingResult).
    FactorialResult = PTerm * WorkingResult.
  END.
END PROCEDURE.

The following example declares a DLL routine, MessageBox(), which displays a message:

r-dllex1.p

DEFINE VARIABLE iResult AS INTEGER NO-UNDO.

MESSAGE "  It's a whole new world!"
  VIEW-AS ALERT-BOX MESSAGE BUTTONS OK TITLE "ABL Message".

RUN MessageBoxA (0, "  It's a whole new world, again!!",
  "ABL DLL Access", 0, OUTPUT iResult).

PROCEDURE MessageBoxA EXTERNAL "user32.dll":
  DEFINE INPUT PARAMETER hwnd    AS LONG.
  DEFINE INPUT PARAMETER mbtext  AS CHARACTER.
  DEFINE INPUT PARAMETER mbtitle AS CHARACTER.
  DEFINE INPUT PARAMETER style   AS LONG.
  DEFINE RETURN PARAMETER result AS LONG.
END. 

The following code fragment declares a UNIX shared library routine:

PROCEDURE atoi EXTERNAL "/usr/lib/libc.so.1":
...

Notes

See also

Call object handle, COM-SELF system handle, DEFINE PARAMETER statement, END statement, RUN statement, TRIGGER PROCEDURE statement