Compaq TP Desktop Connector
for ACMS
Client Application Programming Guide


Previous Contents Index

5.4.3 Establishing Session Context

In a nonblocking environment, saving session context globally serves two purposes. First, it saves the local data for reuse. Second, it allows the desktop client program to coordinate with message passing between the desktop system and the ACMS system when there are multiple sign-ins. Also, saving session context globally gives you a convenient place to store session-related user interface data such as form IDs and icon IDs.

In the AVERTZ desktop client program, a user can sign in to a ACMS system in one window and initiate a task from another window. The AVERTZ desktop client program establishes context and maintains the context as session data across service calls and presentation procedures. Example 5-3 shows the definition of data in the session.h file that keeps track of session context.

Example 5-3 AVERTZ Session Context

extern enum request_type { 
              NO_OUTSTANDING_TASK, 
              TASK_IN_PROGRESS, 
              SEND_CTRL_RESV_LIST, 
              SEND_VR_CONTROL_WKSP, 
              TRANS_LIST_3_LIST_2, 
              TRANS_LIST_5_LIST_6, 
              TRANS_LIST_7_LIST_6, 
              TRANS_LIST_8_LIST_9, 
              TRANS_VRH_RCLIST_VRH_RESV_LIST, 
              TRANS_VR_CTRL_WKSP_LIST_D, 
              TRANS_LIST_E_LIST_F, 
              TRANS_LIST_F_LIST_F, 
              TRANS_LIST_G_LIST_H}; 
   .
   .
   .
typedef struct { 
              enum request_type   request_id; 
              char               *receive_control_text; 
              long               *receive_control_text_count; 
              ACMSDI_FORM_RECORD *receive_record; 
          } exchange_request_type; 
 
 
typedef struct { 
              ACMSDI_SUBMITTER_ID     *submitter_id;          (1)
              int                      session_id; 
              ACMSDI_CALL_ID          *call_id;               (2)
              char                     node[MAX_NODE_LENGTH]; 
              char                     username[MAX_USERNAME_LENGTH]; 
              char                     password[MAX_PASSWORD_LENGTH]; 
              char                     print_file[20]; 
              HWND                     session_icon; 
              HWND                     resv_form; 
              HWND                     vehicle_form; 
              HWND                     billing_form; 
              HWND                     message_window; 
              exchange_request_type   *current_exchange_request; (3)
              int                      completion_status;        (4)
              char                     task_status_message[80];  (5)
            } session_type; 
 
extern session_type *init_session_list; 

The following context data is required for a session:
(1) submitter ID Returned from the ACMS system at sign-in time
(2) call ID Returned by the acmsdi_call_task service
(3) current exchange request Needed if the program uses the same form for multiple exchange steps
(4) completion status Updated by the TP Desktop Connector client service when the task completes
(5) task status message Updated by the TP Desktop Connector client service when the task completes

The session_type structure enables the desktop client program to access data related to a sign-in session when a Windows operation occurs. The variables submitter_id, call_id, completion_status, and task_status_message are allocated by the desktop client program and updated by TP Desktop Connector client services. The value of the completion_status is updated just before the TP Desktop Connector client services call the completion routine.

The session context structure is also a useful place to maintain window handles of user interface objects related to a session. In the sample, the session context includes window handles for the session's icon, the session's menu entry in the select menu, and window handles for all the forms related to that session.

The AVERTZ.EXE program passes the session context to the acmsdi_call_task service as the call_context parameter. Whenever the TP Desktop Connector client services call a task completion routine or a presentation procedure on behalf of an active ACMS task, the session context is passed to the desktop client program. For example, you can use a session context to determine which form in the application to update with data from an incoming presentation procedure. Example 5-4 shows an example in the avertz.c code where the session context is passed.

Example 5-4 Context Passed to Desktop Client Program

   .
   .
   .
    status = acmsdi_call_task( 
                current_session_ptr->submitter_id, 
                NULL, 
                "VR_RESERVE_TASK", 
                AVERTZ_APPLICATION_NAME, 
                NULL, 
                current_session_ptr->task_status_message, 
                0, 
                NULL, 
                current_session_ptr->call_id, 
                &(current_session_ptr->completion_status), 
                SessionTask_Complete, 
                (void *) current_session_ptr);  (1)
   .
   .
   .

The parameter at (1) specifies a session context to be passed. The desktop client program can use that context to determine which form to deal with. The session context is useful for determining which presentation procedure is ending and which workspaces are affected (see Section 5.5).

When a presentation procedure later starts as a result of the ACMS task executing, the session context is passed back to the desktop client program as shown in the transw.c code in Example 5-5.

Example 5-5 Call Context Returned with Presentation Procedure

long int acmsdi_transceive(ACMSDI_FORMS_SESSION_ID *session_id, 
                       : 
                       ACMSDI_CALL_ID *call_id, 
                       void *call_context, 
                       ACMSDI_FORM_RECORD *send_records, 
                       ACMSDI_FORM_RECORD *recv_records ) 
{ 
 
session_type *session_ptr = (session_type *) call_context; 
   .
   .
   .
        save_sessions_PP_data_ptrs( 
                session_ptr, 
                recv_ctl_text, 
                recv_ctl_text_count, 
                recv_records); 
 
        sts = Trans_List3_List2 ( 
                session_ptr, 
                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 **/ 
                    ); 
   .
   .
   .

The code in Example 5-6 shows how session context is used to establish context for the user interface when the user selects a session icon. The session_type structure contains the information about the form to display for that submitter.

Example 5-6 Session Context Handling for the User Interface

void select_new_session( 
                session_type *new_session) 
{ 
  session_type *former_current_session_ptr = current_session_ptr; 
 
  current_session_ptr = new_session; 
 
 /* 
 ** Hide any forms that are displayed for the former current session 
 */ 
 
  if (former_current_session_ptr != NULL) 
    close_session(former_current_session_ptr); 
 
 /* 
 **  Redraw the icons of the former and new current session 
 **  (The WM_DRAWITEM case in MainWindowProc will redraw 
 **    the icon buttons when & if their select state changes. 
 **    Therefore, here we turn the select state OFF for the former 
 **    current session, and turn the select state ON for the 
 **    new current session. 
 */ 
 
  if (former_current_session_ptr != NULL) 
     SendMessage(former_current_session_ptr->session_icon, 
                 BM_SETSTATE, FALSE, NULL); 
 
  SendMessage(current_session_ptr->session_icon, 
               BM_SETSTATE, TRUE, NULL); 
 
 
 /* 
 **     Determine which menus, menu items must be enabled and disabled 
 */ 
 
  if (former_current_session_ptr != NULL) 
    UncheckSessionInSelectMenu(former_current_session_ptr->session_id); 
 
  CheckSessionInSelectMenu(current_session_ptr->session_id); 
 
 
  if ((current_session_ptr-> 
         current_exchange_request)-> 
           request_id == NO_OUTSTANDING_TASK) 
  { 
       EnableSessionExit; 
       EnableRentalMenu; 
       DisableSearchMenu; 
  } 
 
  else 
  { 
       DisableSessionExit; 
       DisableRentalMenu; 
       if ((current_session_ptr-> 
         current_exchange_request)-> 
           request_id == TRANS_LIST_3_LIST_2) 
 
            EnableSearchMenu; 
       else 
            DisableSearchMenu; 
   } 
} 

5.4.4 Writing a Call to Other Nonblocking Services

A call to the nonblocking acmsdi_call_task or acmsdi_sign_out service must follow the rules described for other nonblocking services (see Section 5.4.1). The calling routine specifies the submitter identification returned from the acmsdi_sign_in service.

The acmsdi_call_task service returns a call identification and call context that are used in any completion routine (see Compaq TP Desktop Connector for ACMS Client Services Reference Manual), presentation procedure, or acmsdi_complete_pp service call.

5.4.5 Canceling Active Tasks

TP Desktop Connector allows client programs, written with nonblocking services, to cancel active tasks running on the gateway node. Being able to cancel active tasks allows you to create applications that provide a CANCEL function for the user. The main advantage of being able to cancel a task is to permit the user to work on other applications, if the response from the gateway is not immediate. For example, if the user starts a transaction on the database, you can display three buttons in the dialog box:

These features are available with both the portable API client services (acmsdi_cancel) and the Macintosh client services (DBBreak). See Compaq TP Desktop Connector for ACMS Client Services Reference Manual for a description of these client services.

Note

Because of the limitations in MacTCP and NetWare, you cannot use the cancel function with these transports.

You cannot use a cancel service in exchange steps. If you call a cancel during a presentation procedure, TP Desktop Connector returns the message "ACMSDI_EXCHACTV". If you issue a cancel while another cancel is already in progress, TP Desktop Connector returns the message "ACMSDI_CANCELACTV". The cancel completion routine is guaranteed to be called before the task completion routine.

5.5 Writing Nonblocking Presentation Procedures

Writing a presentation procedure in a nonblocking environment differs from writing presentation procedures in a blocking environment. A nonblocking presentation procedure does the following:

In a nonblocking environment, presentation procedures are generally divided as follows:

Typically, the initial and completion routines are separate so that data can be obtained from the user. If user action is not required, such as in a stub routine, the initial routine can call the acmsdi_complete_pp service, and the completion routine is not necessary.

Example 5-7 shows pseudocode from several modules in the AVERTZ sample desktop client program to indicate the flow of processing a presentation procedure.

Example 5-7 Nonblocking Presentation Procedure Pseudocode

long int acmsdi_transceive(ACMSDI_FORMS_SESSION_ID *session_id, (1)
                       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 ) 
{ 
 
 session_type *session_ptr = (session_type *) call_context;  (2)
   .
   .
   .
    if ((0 == strcmp (send_record_id, "LIST_3")) && 
        (0 == strcmp (recv_record_id, "LIST_2"))) 
   .
   .
   .
        /* 
        **  Save Pointers To Exchange Step's Receive Data 
        **  And Call Presentation Procedure 
        */ 
 
        save_sessions_PP_data_ptrs(  (3)
                session_ptr, 
                recv_ctl_text, 
                recv_ctl_text_count, 
                recv_records); 
 
 
        sts = Trans_List3_List2 (  (4)
                session_ptr, 
                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 **/ 
              ); 
 
    } 
int Trans_List3_List2 (  (5)
            session_type            *session_ptr,...) 
{ 
   .
   .
   .
   enable_initial_fields(session_ptr->resv_form); 
 
   enable_resv_push_buttons(session_ptr->resv_form); 
 
   return(FORMS_NORMAL);  (6)
} 
   .
   .
   .
    return (sts);  (7)
} 

The code shown in Example 5-7 does the following:

  1. The desktop client program is called at the acmsdi_transceive interface.
    The reserve task in the AVERTZ sample application triggers an exchange step. Refer to the reserve task code shown in Example 4-4 for places where presentation procedures are called. The acmsdi_transceive generic presentation procedure defined in transw.c is called through the polling mechanism. The workspaces from the ACMS system are passed to the AVERTZ desktop client program.
  2. The procedure establishes the session context by doing a type conversion on the call context value.
  3. The program saves the addresses of the write and modify arguments.
    The acmsdi_transceive function parses the workspaces to determine which application-specific presentation procedure to call.
  4. The generic presentation procedure acmsdi_transceive calls the application-specific presentation procedure Trans_List3_List2.
  5. The Trans_List3_List2 function defined in the avertzpp.c module gets control to solicit data from the user.
    The routine creates the dialog box and displays the data passed in the workspaces. (The function reserve defined in resvform.c, not shown, paints the screen and receives control from the Windows message queue.)
  6. Control passes to the acmsdi_transceive function.
    After the data to display is sent to the dialog box, control is returned to the generic function.
  7. Control passes to Windows.
    The desktop client program allows message processing for other activities to continue.

At this point, the user can enter data in the dialog box and the desktop client program no longer has control.

To signal that data entry is complete and to pass status back to the TP Desktop Connector gateway, the user clicks on the OK button in the dialog box some time after the desktop client program yields control to Windows. Example 5-8 shows the processing in the resvform.c module when the user either signals completion or cancels the operation.

Example 5-8 Presentation Procedure Completion Pseudocode

        case WM_COMMAND:                      /* message: received a command */ 
 
              switch (wParam) { 
                case IDOK     :    (1)
                case IDCANCEL : 
                  { 
                    exchange_request_type *exchange_request = 
                      current_session_ptr->current_exchange_request; 
 
                     switch (exchange_request->request_id) {   (2)
   .
   .
   .
                        case TRANS_LIST_3_LIST_2          :  (3)
                                End_Trans_List3_List2(current_session_ptr, 
                                                        wParam); 
                                return(TRUE); 
   .
   .
   .
void End_Trans_List3_List2(        (4)
            session_type *session_ptr, 
            WORD          button_pressed) 
 
{ 
  receive_record    = (session_ptr->current_exchange_request)->receive_record; 
 
  sites_wksp        = (vr_sites_wksp *) (receive_record[0].data_record); 
  reservations_wksp = (vr_reservations_wksp *) (receive_record[1].data_record); 
  customers_wksp    = (vr_customers_wksp *) (receive_record[2].data_record); 
  control_wksp      = (vr_control_wksp *) (receive_record[3].data_record); 
   .
   .
   .
     acmsdi_complete_pp(session_ptr->call_id, FORMS_NORMAL);  (5)
 
     MessageBox( 
       session_ptr->resv_form, 
       "Reservation Data Has Been Submitted.\nWait For Return Data ...", 
       " ", 
       MB_OK | MB_ICONINFORMATION); 
   .
   .
   .
  return;   (6)
} 

The code in Example 5-8 does the following:

  1. Windows passes a message to the desktop client program when the user clicks on the OK button in the dialog box.
    The reserve function in resvform.c parses the Windows command and determines which presentation procedure completion routine to call based on the current exchange request saved for the current session.
  2. The session context determines which presentation procedure is completing.
  3. The desktop client program calls the second part of the presentation procedure.
    Based on the pending exchange request, the reserve function calls the routine End_Trans_List3_List2 in avertzpp.c.

  1. The End_Trans_List3_List2 routine gains control.
    Using the pointers to workspaces saved when the presentation procedure began, the routine collects new data entered from the dialog box. The session context is passed along to the application-specific presentation procedure completion routine. The completion routine can determine which workspaces to update and which call identification to pass to the acmsdi_complete_pp service.
  2. The End_Trans_List3_List2 routine sends the updated arguments to the TP Desktop Connector gateway.
    To send a reply to the TP Desktop Connector gateway, the routine calls the acmsdi_complete_pp service, specifying an OpenVMS completion status and the call identification that the TP Desktop Connector client services passed into the program.
  3. Control returns to Windows.

5.6 Writing Memory Allocation Routines

The desktop client program allocates and manages memory while coexisting with the TP Desktop Connector client services and other software on the desktop platform.

The TP Desktop Connector client services use the malloc and calloc functions. However, Windows converts these functions to the LocalAlloc function, which is limited to the 64K bytes in the local heap.

Because messages sent to and received from the TP Desktop Connector Gateway for ACMS can be quite large (depending on the size of workspaces), message buffer allocation can fail, if the desktop client program is already using a substantial portion of the local heap. (Remember that the local heap is also used for the stack, static data, and global data items.)

Message buffers associated with presentation procedures persist for the duration of the call, including a period where control returns to Windows while the user enters data. The use of malloc/calloc with large workspaces can severely restrict the amount of memory available to the desktop client program in the local heap.

The TP Desktop Connector client services permit you to specify your own allocation and free routines for message buffers. These are passed in to TP Desktop Connector client services using the options parameter on the acmsdi_sign_in call by specifying the ACMSDI_OPT_MALLOC_ROUTINE and ACMSDI_OPT_FREE_ROUTINE options (see Compaq TP Desktop Connector for ACMS Client Services Reference Manual).

Do not use GlobalAlloc in the TP Desktop Connector client services memory management routines.


Previous Next Contents Index