# Compaq TP Desktop Connector
for ACMS
Client Application Programming Guide

Previous | Contents | Index

## 4.5.3 Establishing an Exit Handler

If the desktop client program uses the standard C routines atexit or onexit to establish an exit handler, and the exit handler calls acmsdi_sign_out, make the call to atexit or onexit after the first call to acmsdi_sign_in. If this precaution is not taken, the exit handling built into TP Desktop Connector occurs before the desktop client program exit handler call to acmsdi_sign_out, the submitter or submitters are already signed out, and the desktop client program gets error statuses that can be ignored.

## 4.5.4 Calling Tasks and Signing Out

For examples that handle menu traversal, task selection, and sign-out, see the MENU.CBL and LOGOFF.CBL programs from the AVERTZ sample desktop client program.

## 4.5.5 Passing Multiple Workspaces on acmsdi_call_task

Some TP Desktop Connector client applications may need to pass workspaces on the call to the acmsdi_call_task service. Applications that use NO I/O tasks, for example, must pass workspace data as a parameter of the acmsdi_call_task service.

To do this, the application must create an array of workspace descriptors, which is then passed as the workspaces parameter on acmsdi_call_task.

The workspace descriptor type, ACMSDI_WORKSPACE, is used to describe the size and address of the workspace. The client application can use the ACMSDI_INIT_WORKSPACE macro to create each ACMSDI_WORKSPACE descriptor.

The workspace array should contain a workspace descriptor for each workspace that is to be passed on acmsdi_call_task. Example 4-7 illustrates how an application passes three workspaces on the acmsdi_call_task() service.

Example 4-7 Passing Three Workspaces

    ACMSDI_WORKSPACE wksp_array[3];   /* Declare array of wksp descriptors */

    struct {
      char ctrl_key[5];
      char message[80];
    } control_wksp;

    struct {
      int  id_number;
      char first_name[15];
      char last_name[25];
    } employee_record;

    struct {
      int  dept_id;
      char dept_name[15];
    } department_record;
    int status;

    /*
    **  Create the workspace descriptors
    */

    ACMSDI_INIT_WORKSPACE(wksp_array[0], control_wksp);
    ACMSDI_INIT_WORKSPACE(wksp_array[1], employee_record);
    ACMSDI_INIT_WORKSPACE(wksp_array[2], department_record);

    /*
    **  Pass the number of workspaces passed (3) along with
    **  the array of workspace descriptors on the call to
    **  acmsdi_call_task()
    */

    status = acmsdi_call_task(submitter_id,
                              NULL,
                              "MY_TASK",
         "MY_APPL",
         NULL,
         call_status,
         3,
         wksp_array,
         NULL, NULL, NULL, NULL);

## 4.5.6 Using Unidirectional Workspaces on acmsdi_call_task

When an application passes workspaces on a call to the acmsdi_call_task service, the unidirectional-workspaces option allows the application to specify which of those workspaces are read-only workspaces, which are write-only workspaces, and which are modify workspaces. See Section 2.4.5.1 to determine what is meant by read-only and write-only for the purposes of the unidirectional workspaces feature.

To use the unidirectional-workspaces option:

1. Enable the OPTIMIZE_WORKSPACES option in the client program's call to acmsdi_call_task.
2. Create an array of unidirectional-workspace descriptors to be passed on the call to acmsdi_call_task.

For unidirectional workspaces, the application must use the ACMSDI_WORKSPACE_OPT type in order to specify an access type for each workspace. The workspace descriptor type, ACMSDI_WORKSPACE_OPT, is used to describe the size, address, and access type of the workspace. The access type of each workspace must be specified as either ACMSDI_ACCESS_READ, ACMSDI_ACCESS_WRITE, or ACMSDI_ACCESS_MODIFY.

The client application can use the ACMSDI_INIT_WORKSPACE_OPT macro to create each ACMSDI_WORKSPACE_OPT descriptor. The workspace array should contain a workspace descriptor for each workspace that is to be passed on acmsdi_call_task. Example 4-8 illustrates how an application passes three workspaces on the acmsdi_call_task() service.

Example 4-8 Passing Unidirectional Workspaces

    ACMSDI_CALL_OPTION   call_options[2]; /* Declare call options array */

    ACMSDI_WORKSPACE_OPT wksp_array[3];   /* Declare unidirectional wksp
                                          ** descriptors array
                                          */

    struct {
      char ctrl_key[5];
      char message[80];
    } control_wksp;

    struct {
      int  id_number;
      char first_name[15];
      char last_name[25];
    } employee_record;

    struct {
      int  dept_id;
      char dept_name[15];
    } department_record;

    int status;

    /*
    ** Create the unidirectional workspace descriptors
    */

    ACMSDI_INIT_WORKSPACE_OPT(wksp_array[0], control_wksp, ACMSDI_ACCESS_WRITE);
    ACMSDI_INIT_WORKSPACE_OPT(wksp_array[1], employee_record, ACMSDI_ACCESS_READ);
    ACMSDI_INIT_WORKSPACE_OPT(wksp_array[2], department_record, ACMSDI_ACCESS_MODIFY);

    /*
    **  Turn on the unidirectional workspace call option
    */

    call_options[0].option = ACMSDI_CALL_OPT_OPTIMIZE_WKSPS;
    call_options[1].option = ACMSDI_CALL_OPT_END_LIST:

    /*
    ** Pass the call options array, along with the number
    ** of workspaces passed and the array of unidirectional
    ** workspace descriptors, on the call to acmsdi_call_task()
    */

    status = acmsdi_call_task(submitter_id,
                              call_options,
                              "MY_TASK",
           "MY_APPL",
         NULL,
         call_status,
         3,
         wksp_array,
         NULL, NULL, NULL, NULL);

Note The ACMSDI_CALL_OPT_OPTIMIZE_WKSPS option tells the TP Desktop Connector client services to interpret the array of workspace descriptors as data type ACMSDI_WORKSPACE_OPT. The workspace optimization option and the ACMSDI_WORKSPACE_OPT must be used together, or not at all. Using one without the other produces unpredictable results.

## 4.5.7 Providing Stub Routines

ACMS task definitions do not have to include exchange steps, as is the case with tasks that include only processing steps and specify NO TERMINAL USER I/O as the I/O method. For these tasks, you do not write presentation procedures. However, in your desktop client program, you must supply stub routines for all possible entry points, that is, presentation procedures and action routines (for example, acmsdi_check_version).

The TP Desktop Connector client services refer to all the presentation procedures and the acmsdi_check_version routine. If you do not have code for one or more of these routines in your desktop client program, provide a stub for the linker to use to resolve these references.

Stubs for all presentation procedures and the acmsdi_check_version routine are provided in the file PPSTUBS.C. PPSTUBS.C provides stubs for the following routines:

- acmsdi_disable
- acmsdi_enable
- acmsdi_receive
- acmsdi_request
- acmsdi_send
- acmsdi_transceive
- acmsdi_check_version

Note The file PPSTUBS.C is provided in the ACMSDI$COMMON directory on the OpenVMS system. Copy this file to the desktop system when the TP Desktop Connector client services software is installed.

To use this source code, follow these steps:

1. Edit or comment out any of the presentation procedures that you implement as part of your desktop client program.
2. Edit or comment out the acmsdi_check_version routine if you provide your own version-checking routine.
3. Compile the module in the appropriate memory model.

Include the resulting object module in your LINK command or in your application object library (see Example 4-11).

# 4.6 Writing Presentation Procedures in a Blocking Environment

If your ACMS tasks include exchange steps, write presentation procedures in your desktop client program to handle the interaction with the user required by these exchange steps. TP Desktop Connector software invokes a given presentation procedure when it receives from the desktop gateway an exchange step message corresponding to that procedure.

Your desktop client program must include presentation procedures that correspond to any of the exchange step types actually used in your task definitions:

- SEND exchange steps invoke acmsdi_send.
- RECEIVE exchange steps invoke acmsdi_receive.
- TDMS READ exchange steps invoke acmsdi_read_msg.
- TDMS WRITE exchange steps invoke acmsdi_write_msg.
- TRANSCEIVE exchange steps invoke acmsdi_transceive.
- REQUEST exchange steps invoke acmsdi_request.

TP Desktop Connector client services call acmsdi_read_msg when a TDMS Read exchange is received from the TP Desktop Connector Gateway for ACMS on the host OpenVMS system. The acmsdi_read_msg presentation procedure displays the prompt, if any, sent from the ACMS task, then acquires the text from the form's Message Field to be returned to ACMS.

TP Desktop Connector client services call acmsdi_write_msg when a TDMS Write exchange is received from the TP Desktop Connector Gateway for ACMS on the host OpenVMS system. The acmsdi_write_msg presentation procedure displays the message text sent from the ACMS task in the form's Message Field. See Compaq TP Desktop Connector for ACMS Client Services Reference Manual for the syntax of these procedures.

For a FORM I/O task, include the following presentation procedures as well:

- The first exchange step to specify a DECforms form invokes the acmsdi_enable presentation procedure.
- Closing a TP Desktop Connector session invokes the acmsdi_disable presentation procedure, if any of the tasks used FORM I/O.

See Section 4.6.1 for more information on acmsdi_enable and acmsdi_disable.

In the AVERTZ sample application, these presentation procedures in turn call routines that correspond to the specific exchange step in which the SEND, RECEIVE, TRANSCEIVE, or REQUEST keyword is used.

Example 4-9 shows the TRANSCEIVE presentation procedure, in file trans.c from the AVERTZ sample application.

Example 4-9 TRANSCEIVE Presentation Procedure

    long acmsdi_transceive(ACMSDI_FORMS_SESSION_ID *session_id,
                           char *send_record_id,
                           long send_record_count,
                           char *recv_record_id,
                           long recv_record_count,
                           char *recv_ctl_text,
                           long *recv_ctl_text_count,
                           char *send_ctl_text,
                           long send_ctl_text_count,
                           short timeout,
                           ACMSDI_CALL_ID *call_id,
                           void *call_context,
                           ACMSDI_FORM_RECORD *send_records,
                           ACMSDI_FORM_RECORD *recv_records )  (1)

    {
    long int sts;
    short int i;

        if (send_record_id == NULL)
            return (FORMS_BADARG);

        if (recv_record_id == NULL)
            return (FORMS_BADARG);

        for (i = 0; i < send_record_count; i++)
        {
            if (recv_records[i].data_record == NULL)
                return (FORMS_INVRECDES);
        }

        sts = FORMS_NORECORD;

        if ((0 == strcmp (send_record_id, "LIST_1")) &&   (2)
            (0 == strcmp (recv_record_id, "LIST_2")))
        {
            /*
            **  Validate arguments
            */
            if ((send_record_count != 2) ||
                (recv_record_count != 4))
                return (FORMS_BADRECCNT);

            if (send_records[0].data_length != 123)
                return (FORMS_BADRECLEN);

            if (send_records[1].data_length != 1730)
                return (FORMS_BADRECLEN);

            if (recv_records[0].data_length != 138)
                return (FORMS_BADRECLEN);

            if (recv_records[1].data_length != 144)
                return (FORMS_BADRECLEN);

            if (recv_records[2].data_length != 255)
                return (FORMS_BADRECLEN);

            if (recv_records[3].data_length != 123)
                return (FORMS_BADRECLEN);

       .
       .
       .
            /*
            **  Call Presentation Procedure
            */

            GETSITE (                                        (3)
                 session_id,
                 form_data,
                 send_ctl_text, /** VR_SENDCTRL_WKSP **/
                 send_records[0].data_record, /** VR_CONTROL_WKSP **/
                 send_records[1].data_record, /** VR_SITES_WKSP **/
                 recv_records[0].data_record, /** VR_SITES_WKSP **/
                 recv_records[1].data_record, /** VR_RESERVATIONS_WKSP **/
                 recv_records[2].data_record, /** VR_CUSTOMERS_WKSP **/
                 recv_records[3].data_record  /** VR_CONTROL_WKSP **/
                        );

            sts = FORMS_NORMAL;

        } /** end if strcmp **/
                      .
                      .
                      .

        return (sts);
    }

The following key points are called out in the example:

1. The acmsdi_transceive presentation procedure is called when a TRANSCEIVE exchange step executes in the ACMS task.
2. The procedure looks at the contents of the send and receive record identifier fields to determine what second-level, application-specific routine to call. This is termed the application-specific presentation procedure.
3. The desktop client program runs the presentation procedure.

In Example 4-10, the GETSITE.CBL program receives data from the acmsdi_transceive presentation procedure.

Note PPGEN.COM is a sample tool that generates these generic presentation procedures based on task definitions in the TDB. PPGEN.COM determines the appropriate record counts, data record lengths, and so on, for each application-specific presentation procedure. PPGEN.COM is located in ACMSDI$EXAMPLES.

Example 4-10 GETSITE Application-Specific Presentation Procedure

           PROCEDURE DIVISION USING
                              SESSION-ID,
                              FORM-DATA,
                              VR-SENDCTRL-WKSP,
                              CONTROL-IN,
                              SITES-IN,
                              VR-SITES-WKSP,
                              VR-RESERVATIONS-WKSP,
                              VR-CUSTOMERS-WKSP,
                              VR-CONTROL-WKSP.
       .
       .
       .
          *
          *    Distribute data
          *
               PERFORM 010-INITIALIZE-RECS
                   THRU 010-INITIALIZE-RECS-EXIT.
               MOVE SITES-IN TO VR-SITES-WKSP.  (1)
               MOVE CORRESPONDING VR-SITES-WKSP TO FORM-DATA.
               MOVE CONTROL-IN TO VR-CONTROL-WKSP.
               ACCEPT CURRENT-DATE FROM DATE.
               MOVE CORRESPONDING CURRENT-DATE
                    TO CHECKOUT-DATE-2, RETURN-DATE-2.
               MOVE THIS-CENTURY
                    TO CC OF CHECKOUT-DATE-2,
                       CC OF RETURN-DATE-2.
       .
       .
       .
          *
          *    Collect data from screen                      (2)
          *
               MOVE CORRESPONDING FORM-DATA TO VR-SITES-WKSP.
               MOVE CORRESPONDING FORM-DATA TO VR-CUSTOMERS-WKSP.
               MOVE CORRESPONDING FORM-DATA TO VR-RESERVATIONS-WKSP.
               MOVE CORRESPONDING RETURN-DATE
                   TO VEHICLE-EXPECTED-RETURN-DATE.
               MOVE CORRESPONDING CHECKOUT-DATE
                   TO VEHICLE-CHECKOUT-DATE.
       .
       .
       .
          *
          *    Validate site and customer
          *
               IF ((CUSTOMER-ID OF FORM-DATA EQUAL ZERO) AND   (3)
                   (CU-LAST-NAME OF FORM-DATA = SPACES AND
                    CU-FIRST-NAME  OF FORM-DATA = SPACES))
               THEN
                   MOVE 'N' TO VALID-DATA-FLAG
                   MOVE "Either CUSTOMER ID or NAME is required"
                       TO MESSAGEPANEL OF VR-CONTROL-WKSP
                   MOVE 5 TO CURSOR-LINE
                   MOVE 15 TO CURSOR-COLUMN
               ELSE
               IF ((SITE-ID OF FORM-DATA EQUAL ZERO) AND
                   (CITY OF FORM-DATA EQUAL SPACES))
               THEN
                   MOVE 'N' TO VALID-DATA-FLAG
                   MOVE "Either SITE ID or CITY is required"
                       TO MESSAGEPANEL OF VR-CONTROL-WKSP
                   MOVE 11 TO CURSOR-LINE
                   MOVE 11 TO CURSOR-COLUMN
               END-IF.

               IF (NOT VALID-DATA) THEN GO TO 100-GET-DATA-EXIT.
          *
          *    Validate dates and car type
          *
       .
       .
       .
           100-GET-DATA-EXIT.
               EXIT.             (4)
       .
       .
       .
           END PROGRAM "_GETSITE".

The GETSITE program does the following:

1. Displays to the user the data received from the generic presentation procedure.
2. Accepts new data.
3. Verifies the data.
4. Returns the new data to the acmsdi_transceive procedure.

## 4.6.1 Coding for acmsdi_enable and acmsdi_disable

If your tasks use any of the three DECforms exchange step types (SEND, RECEIVE, TRANSCEIVE), supply these presentation procedures:

- acmsdi_enable
  Write an acmsdi_enable routine to initialize structures that are used by a number of presentation procedures. The acmsdi_enable routine is invoked when a user calls a task that includes a DECforms form name that has not previously been referenced in a task. The acmsdi_enable service is called once for every user and form combination in a task, not for every iteration of the same task.
- acmsdi_disable
  Write the acmsdi_disable routine to clear structures used by the presentation procedures. The acmsdi_disable presentation procedure is invoked when the desktop code issues the acmsdi_sign_out call.

## 4.6.2 Coding Return Status Values

ACMS software expects specific return values from the presentation procedures. These values correspond to the DECforms forms or the TDMS request return values that the customer-written procedure server receives as if the exchange is actually handled by these forms products on the OpenVMS system. In writing presentation procedures, ensure that the values the desktop client program returns to the ACMS system are valid OpenVMS status values.

See Appendix B for more information on determining these status values.

# 4.7 Building and Debugging the Desktop Client Program

Compile and link the desktop client program as you would any program.

## 4.7.1 Linking the Desktop Client Program

Link the executable desktop client program with the debug qualifier initially to look for these problems:

- Errors in argument order, format, or content in your procedures or in the invocations of TP Desktop Connector client services
- Invalid return values in presentation procedures

Link the desktop client program with the following types of libraries:

- Application-specific library containing:
  - Object files of the presentation procedures and menu procedures in your desktop client program
  - Stub routines for presentation procedures and action routines that your desktop client program does not use (see Section 4.5.7)
  Use the library utility on your desktop system to create a runtime library from the .OBJ format files.

- Library that contains the TP Desktop Connector client services
  On DOS, select the large-memory model, acmsdil.lib, library.

  Note By default, all of the TP Desktop Connector client libraries are configured for DECnet. The automated postinstallation procedures (available for DOS and Microsoft Windows) configure these libraries for other transports for you. To use transports other than DECnet on other platforms, you need to configure the TP Desktop Connector library yourself. See Compaq TP Desktop Connector for ACMS Gateway Management Guide for more details.

- Network library that matches the memory-model library you compiled your program modules against
  For more information on network transports, see Compaq TP Desktop Connector for ACMS Gateway Management Guide.

- Language library or libraries
  Use either the C runtime library, the COBOL runtime library, or both, depending on which languages you use. These libraries are supplied with the compilers.

Example 4-11 shows the desktop client program client.obj being linked with the appropriate libraries on DOS:

- Application-specific library client.lib
- TP Desktop Connector large-memory model library acmsdil.lib
- DECnet-DOS Version 4 large-memory model library lnet4.lib
- C runtime large-memory model library llibce.lib

Example 4-11 Linking to the DOS TP Desktop Connector Client Services

    C:\>  link /STACK=n client,,,client.lib+acmsdil.lib+lnetv4.lib+llibce.lib
    C:\>

If the libraries do not reside in the current directory, set the LIB environment variable to include the directory, for example:

    C:\>  set lib=c:\msvc\include;c:\acmsdi

The /STACK qualifier defines the desired stack size (1000 bytes plus whatever is required by your application).

After generating the executable file, run it as you would run any DOS program.

After you test your presentation code, relink it without the debug qualifier. See Compaq TP Desktop Connector for ACMS Gateway Management Guide for a description of how to manage the application after testing it.

Previous | Next | Contents | Index