Working with the C API


Introduction

Sample Code

Passing Structures to an API Call

Freeing Memory


Introduction

This document covers the process of calling the application server C API. The following section contains sample code (Application.cpp) of the steps required to call the InquireRequests API function.


Sample Code

Application.cpp code

The following sample code of Application.cpp is the base for all API examples in this chapter. (Following samples build on this one.)

{ long nAPIHandle;
  long nErrorCode;
  Connect( &nAPIHandle, "indy_pp_es1", 8000, "EXAV", NULL, &nErrorCode );
  PROBLEM_RECORD* pProblemList = NULL;
  PROBLEM_FILTER filter;
  InitProblem_Filter( nAPIHandle, &filter );
  filter.system = new char[16]; 
  strcpy( filter.system, "PC APPLICATIONS" );
  filter.component = new char[13]; 
  strcpy( filter.component, "WEB BROWSERS" ); 
  filter.item = new char[9]; 
  strcpy( filter.item, "NETSCAPE" );
  long max_requested = -1; // no restriction on number to retrieve 
  int nResult = InquireRequests( nAPIHandle, &filter, &pProblemList, &max_requested, 
      & nErrorCode ); 
  FreeRequestList( nAPIHandle, &pProblemList );
  Disconnect( nAPIHandle ); }

After the InquireRequests function is complete, the return code may be checked and the linked list pProblemList can be “traversed” like any other singly-linked list:

if ( nResult == SAI_OK ) 
{ char myBuffer[512]; 
PROBLEM_RECORD* pProblemRecord;
for ( pProblemRecord = pProblemList; pProblemRecord != NULL; 
pProblemRecord = pProblemRecord->next ) 
{ sprintf( myBuffer, "%s", pProblemRecord->problem_id );
 ...
 /* do some other processing */
 ...} }

This sample only uses a small number of the fields in the PROBLEM_RECORD and PROBLEM_FILTER structures. For a complete list of fields and error codes in these structures, see Appendix A.

Note: The code sample called Connect and Disconnect functions. It is not necessary or recommended that these functions be called every time a function call is made. They should only be called once for each application.


Passing Structures to an API Call

Overview

Structures must be initialized before being passed to the API. In the previous example, a PROBLEM_FILTER structure was passed to the InitProblemFilter function.

This function sets the structure members to default values, the way C++ provides constructors for class objects. Because the API can be used in C++ and non-C++ applications it is not possible to provide a constructor.

The Init functions default char* variables to NULL, long variables to -1, and TRI_BOOL variables to BOOL_UNDEFINED. If these variables are not changed from the default values, they are ignored when the structure is sent to the application server for processing. This is done so NULL values can be supported in the database.

For example, in the previous PROBLEM_FILTER structure, begin_date_range and end_date_range remain set to NULL so problems of any date range can be selected. In a CONTACT_FILTER, if the active TRI_BOOL member is set to BOOL_UNDEFINED, the application server does NOT filter out contacts based on the active field. For a complete list of structures and their data members see Appendix A.

Initializing the API

Because internals for the C API application server are written in C++, the most important part of using the API is to initialize the Connect function. This function creates an internal APIManager object that processes API calls. Connect instantiates a new APIManager and assigns the pAPIManager variable the address of this object. Future calls to the API require this initialized variable as the first parameter so method calls can be made to the APIManager.

Freeing the API

When a calling application no longer requires the use of operations provided by the API, it must make a call to Disconnect. Created in the Create call, the APIManager object is present while the DLL is used by an application.

Note: It is important that the Disconnect function be called so it can delete the internal APIManager. This avoids memory leaks.

Using extended data

Passing extended data to an API call is done with the creation of a list of extended data objects that are passed to the API call. The following code shows how the example, Application.cpp, can be modified:

{ long nAPIHandle; 
long nErrorCode; 
Connect( &nAPIHandle, "indy_pp_es1", 8000, "EXAV", NULL, &nErrorCode ); 
PROBLEM_RECORD* pProblemList = NULL;
PROBLEM_FILTER filter; 
EXTENDED_DATA* user_data = NULL; 
InitProblemFilter( nAPIHandle, &filter ); 
memset( &filter, '\0', sizeof( PROBLEM_FILTER ) ); 
filter.system = new char[16]; 
strcpy( filter.system, "PC APPLICATIONS" ); 
filter.component = new char[13]; 
strcpy( filter.component, "WEB BROWSERS" ); 
filter.item = new char[9]; 
strcpy( filter.item, "NETSCAPE" ); 
user_data = new EXTENDED_DATA; 
user_data->pszName = new char[15]; 
strcpy( user_data->pszName, "REQUEST_STATUS" ); 
user_data->pszValue = new char[9]; 
strcpy( user_data->pszValue, "INACTIVE" ); 
user_data->next = NULL; 
long max_requested = -1; // no restriction on number to retrieve 
int nResult = InquireRequests( nAPIHandle, &filter, &pProblemList, 
    &max_requested, & nErrorCode, user_data ); 
FreeRequestList( nAPIHandle, &pProblemList ); 
Disconnect( nAPIHandle ); }

The resulting pProblemList has an additional member for each PROBLEM_RECORD that contains a list of EXTENDED_DATA structures. Accessing this additional data is done similar to the following:

if ( nResult == SAI_OK )
{ char myBuffer[512];
PROBLEM_RECORD* pProblemRecord; 
for ( pProblemRecord = pProblemList; 
      pProblemRecord != NULL; 
      pProblemRecord = pProblemRecord->next ) 
{ sprintf( myBuffer, "%s", pProblemRecord->problem_id );
...
 /* do some other processing */ 
...
if ( pProblemList->user_data != NULL ) 
{ EXTENDED_DATA* pExtendedData; 
for ( pExtendedData = pProblemList->user_data; 
      pExtendedData != NULL; 
      pExtendedData = pExtendedData->next ) 
{ sprintf( myBuffer, “%s”, pExtendedData->pszValue );
...
/* do some more processing */
...
} { } }

Status codes

The previous example showed how an API call returns a status code and stores it in nResult. The following table lists possible status codes for an API call:

Defined Name Value Error Description
SAI_OK 0 No error.
SAI_ERROR 1 General error in the C API.
SAI_SQL_ERROR 2 An SQL error occurred.
SAI_CLIENT_ERROR 3 An error occurred during netclient communication with the application server.

When making an API call, it is recommended that you check the status code against SAI_OK. If an error is found, check the error code parameter (nErrorCode in the previous example) for the error number. If an SQL error occurs, refer to your database manual for a description.

Note: A common client error is a connection failure -13.


Freeing Memory

Overview

Memory "owned" by an application cannot be deleted by another application. The same is true for DLLs that are also applications. Because the C DLL allocates memory used by the PROBLEM_RECORD list that is returned to the calling applications, the DLL must provide a way for the application to release the used list. This is done by calling FreeRequestList:

FreeRequestList( nAPIHandle, &pProblemList );

FreeRequestList requires the APIHandle, and the address of the list as parameters.

Other functions that free memory

In addition to FreeRequestList, there are several C API functions that clear memory. Two are FreeSolutionList and FreeHistoryList. These functions free lists of SOLUTION_RECORD and HISTORY_RECORD structures respectively.

Note: As a precaution, the API releases allocated memory when Disconnect is called, making it unnecessary to call these functions.

Free functions are provided as a convenience if you need to conserve memory while executing an application using the API.