Previous | Contents | Index |
The RISC architecture for Alpha systems requires that data references be naturally aligned. That is, short words (2 bytes) must be on an even byte boundary. Long words (4 bytes) must be accessed on a boundary evenly divisible by 4.
When an Alpha client defines a C structure, it creates padding in the structure, if necessary, to ensure that each field complies with these requirements. (The padding is not visible to you.)
An ACMS task running on either an OpenVMS VAX or an OpenVMS Alpha system, however, does not impose such restrictions on its data objects and does not pad its structures. The problem arises when data, defined on one of these machines, is transmitted across the network to the other machine, and interpreted using the same C structure definitions. In a TP Desktop Connector application, this is a concern only when ACMS workspaces are being sent (in either direction) between the client program on a RISC machine and the ACMS application (OpenVMS).
Example 6-9 shows how a sample application client program deals with incoming workspaces (or send records). Before calling the application-specific presentation procedure (Trans_List3_List2), the generic presentation procedure (acmsdi_transceive) dynamically allocates structures for all the incoming workspaces (the send_records array).
The data in the send_records array is byte copied field by field into the newly allocated workspaces (load_control_wksp()). These workspaces are then passed to Trans_List3_List2, where their contents are used to update the display. Once Trans_List3_List2() returns, acmsdi_transceive() assumes that the workspaces it dynamically allocated are no longer needed and it frees them.
Example 6-9 OpenVMS to RISC Structure Byte Copy |
---|
void load_control_wksp( vr_control_wksp *control_wksp, char *data_ptr) { memcpy(&control_wksp->ctrl_key, data_ptr, sizeof(control_wksp->ctrl_key)); data_ptr = data_ptr + sizeof(control_wksp->ctrl_key); memcpy(&control_wksp->current_entry, data_ptr, sizeof(control_wksp->current_entry)); data_ptr = data_ptr + sizeof(control_wksp->current_entry); . . . } long int 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 ) { . . . if ((0 == strcmp (send_record_id, "LIST_3")) && (0 == strcmp (recv_record_id, "LIST_2"))) { . . . /* ** Create the send workspace structures and load their ** fields with the data in the send_records array. */ vr_control_wksp *send_control_wksp = (vr_control_wksp *) malloc(sizeof(vr_control_wksp)); vr_sites_wksp *send_sites_wksp = (vr_sites_wksp *) malloc(sizeof(vr_sites_wksp)); load_control_wksp(send_control_wksp, (char *) send_records[0].data_record); load_sites_wksp(send_sites_wksp, (char *) send_records[1].data_record); sts = Trans_List3_List2 ( session_ptr, send_ctl_text, /** VR_SENDCTRL_WKSP **/ send_control_wksp, /** VR_CONTROL_WKSP **/ send_sites_wksp, /** 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 **/ free(send_control_wksp); free(send_sites_wksp); } . . . } |
Example 6-10 shows how a client deals with outgoing workspaces (or receive records). In the sample application, the workspaces are not allocated until it is time to pull the data off the form. In the presentation procedure's completion routine, End_Trans_List3_List2(), the structures for all the presentation procedure's outgoing workspaces (receive records) are allocated. They are then initialized to guarantee that string fields are padded with blanks.
These structures are then loaded with data retrieved from the form. When the data retrieval is complete, the contents of the structure are byte copied field by field to the receive_records array (unload_control_wksp()). (This receive_records array is the same receive_records array that was originally passed into the acmsdi_transceive() routine.) Finally, the application calls acmsdi_complete_pp to send the contents of the receive_records array back to the ACMS application.
Example 6-10 RISC to OpenVMS Structure Byte Copy |
---|
void unload_control_wksp( vr_control_wksp *control_wksp, char *data_ptr) { memcpy(data_ptr, control_wksp->ctrl_key, sizeof(control_wksp->ctrl_key)); data_ptr = data_ptr + sizeof(control_wksp->ctrl_key); memcpy(data_ptr, &(control_wksp->current_entry), sizeof(control_wksp->current_entry)); data_ptr = data_ptr + sizeof(control_wksp->current_entry); . . . } void End_Trans_List3_List2( session_type *session_ptr, int button_pressed) { receive_record = (session_ptr->current_exchange_request)->receive_record; sites_wksp = (vr_sites_wksp *) malloc(sizeof(vr_sites_wksp)); reservations_wksp = (vr_reservations_wksp *) malloc(sizeof(vr_reservations_wksp)); customers_wksp = (vr_customers_wksp *) malloc(sizeof(vr_customers_wksp)); control_wksp = (vr_control_wksp *) malloc(sizeof(vr_customers_wksp)); if( ((int) sites_wksp == NULL) || ((int) reservations_wksp == NULL) || ((int) customers_wksp == NULL) || ((int) control_wksp == NULL)) { DisplayWarningBox( AvertzMainWindow, "Application Has Run Out Of Memory. \n\nUnable To Continue.", "WARNING!!"); return; } /* ** Initialize all the workspace fields (set all the ** characters in the character arrays to blanks, etc.). */ init_sites_wksp(sites_wksp); init_reservations_wksp(reservations_wksp); init_customers_wksp(customers_wksp); init_control_wksp(control_wksp); . . . data_missing = get_initial_fields(session_ptr->resv_form, sites_wksp, reservations_wksp, customers_wksp); . . . if (!data_missing) { . . . /* ** Move the data collected in the workspace structures ** to the location of the original receive records */ unload_sites_wksp(sites_wksp, receive_record[0].data_record); unload_reservations_wksp(reservations_wksp, receive_record[1].data_record); unload_customers_wksp(customers_wksp, receive_record[2].data_record); unload_control_wksp(control_wksp, receive_record[3].data_record); . . . acmsdi_complete_pp(session_ptr->call_id, FORMS_NORMAL); } free(sites_wksp); free(reservations_wksp); free(customers_wksp); free(control_wksp); } |
The desktop client program allocates and manages memory while
coexisting with the TP Desktop Connector client services and
other software on the desktop platform. By default, the TP Desktop
Connector client services use malloc() and free(). However, you do
not need to use these services for environments other than DOS. 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).
6.9 Building and Debugging Motif Desktop Client Programs
For guidelines for building TP Desktop Connector client
programs for OpenVMS and Tru64 UNIX, see the
makefiles in the appropriate directory for your platform.
6.9.1 Debugging the Nonblocking Desktop Client Program with Tasks
Debug the presentation code on the desktop system. When the presentation code runs, debug the desktop client program with the ACMS software. Follow these guidelines:
To get a better feel for the flow of a nonblocking Desktop application, use a debugger to step through the Motif sample provided on the kit. Set breakpoints in the various files at the following functions:
This chapter describes how to use the forced nonblocking feature of TP Desktop Connector client services to create applications using presentation packages like Visual Basic and PowerBuilder.
The topics in this chapter are:
Certain desktop application development tools, such as Visual Basic and PowerBuilder, cannot handle ACMS exchange steps, because these tools do not support callbacks from environments such as the C language. Because these tools do not support pointer types, they cannot accept arguments that are passed by reference, such as form records. TP Desktop Connector exchange step callbacks expect the called presentation procedures to accept arguments that are passed by reference. The TP Desktop Connector portable API nonblocking execution is activated by passing the address of a completion routine to the acmsdi_call_task service.
The TP Desktop Connector portable API has been extended to
support both exchange steps and nonblocking execution of task calls for
development tools that do not support pointer data types, or whose
memory management routines relocate data. In addition, the extension to
the portable API allows support of acmsdi_cancel service, which must be
issued in a nonblocking environment. The extension to the portable API
provides a way for these tools to obtain a pointer (a 32-bit integer)
to their workspace buffers using the acmsdi_return_pointer service.
7.2 Portable API Extensions for Forced Nonblocking
The extensions to the portable API are:
Value | Description |
---|---|
ACMSDI_CANCEL_DONE | Task cancel call complete. |
ACMSDI_DONE | Sign-in, sign-out, or task call complete. |
ACMSDI_ENABLE_EXCH | Enable exchange step has arrived. |
ACMSDI_EXEC | Call still executing; no message available. |
ACMSDI_READY | No call executing; no message available. |
ACMSDI_RECV_EXCH | Receive exchange step has arrived. |
ACMSDI_REQUEST_EXCH | TDMS request exchange step has arrived. |
ACMSDI_SEND_EXCH | Send exchange step has arrived. |
ACMSDI_TDMS_READ_EXCH | TDMS read exchange has arrived. |
ACMSDI_TDMS_WRITE_EXCH | TDMS write exchange has arrived. |
ACMSDI_TRCV_EXCH | Transceive exchange step has arrived. |
In a forced nonblocking session, the acmsdi_disable callback to the client application does not occur as part of the acmsdi_sign_out processing. Instead, the client application must clear the structures used by presentation procedures without being prompted by the acmsdi_disable callback. In addition, in a forced nonblocking session, the acmsdi_check_version callback to the client does not occur. Instead, TP Desktop Connector adds the form version to the argument list acquired by the application using the acmsdi_bind_enable_args service. The client application can do the version checking during enable exchange step processing.
See Compaq TP Desktop Connector for ACMS Client
Services Reference Manual for the syntax and description of these
client services for the forced nonblocking environment.
7.3 Forced Nonblocking Programming Considerations
The following sections discuss programming considerations when using
forced nonblocking mode.
7.3.1 Establishing a Forced Nonblocking Session
To establish a forced nonblocking session, you request nonblocking calls without specifying a completion address with the acmsdi_sign_in service call. Instead, you specify the ACMSDI_OPT_NONBLK option. If the sign-in succeeds, all calls for the session are nonblocking. Follow these steps to initialize call options to request forced nonblocking calls. (Note the example code, written in Visual Basic syntax, has been altered to make it more readable.)
Static options(2) As ACMSDI_OPTION |
options(0).option = ACMSDI_OPT_NONBLK options(1).option = ACMSDI_OPT_END_LIST |
status = acmsdi_sign_in(submitter_node$, ' node to sign-in to username_str$, ' user signing in password_str$, ' user's password options(0), ' options array submitter_id, ' submitter identifier ByVal 0%, ' final completion status 0&, ' completion routine ByVal 0&) ' call context |
The session is a nonblocking call even though the completion routine address is 0, because the options argument is set to ACMSDI_OPT_NONBLK. Also, all subsequent calls for the session are nonblocking. A successful return status is ACMSDI_PENDING, indicating that the sign-in call has been sent to the back end.
The final completion status is passed as a long integer with a value of 0, which TP Desktop Connector interprets as a null pointer. This is done because its memory location can change before the task completes. When the task completes, you acquire the final completion status with the acmsdi_complete_call service.
Previous | Next | Contents | Index |