C Main API Reference

Using the C Main API
C Main API Declarations
C Main API Functions (alphabetical)
C Main API Functions (categorical)

Using the C Main API

API Function Call Sequence



Instance Handles

An instance handle (similar in concept to a file handle) represents a program's access to the API, and distinguishes the program-specific resources and settings used within the API. This identification is necessary for dynamic shared libraries, which may be accessed by several different programs simultaneously. When a program initializes the API by calling EssInit(), an instance handle is returned.

Using the Instance Handle in an Application

An instance handle is declared as type ESS_HINST_T in C programs.

The instance handle must be passed to the EssLogin() call, which returns a context handle, and also to the API terminate function EssTerm() to free any program-specific resources used within the API.

Instance handles may be passed to other programs, child processes, or threads, which can then log in independently of the original using the same API resources and settings. Make sure that all programs, processes or threads using the same instance handle log out before they can terminate the API.

Note: A thread may require its own instance handle (phInstance) to avoid overwriting another thread's networking status information.



Context Handles

A context handle represents a single, valid login by a user onto the system. A successful call to EssLogin() returns a context handle, which can be passed to other API calls which require a context handle as an argument.

Using Context Handles in an Application

Context handles are defined as type ESS_HCTX_T in C programs. In general, a context handle is valid for as long as the user remains logged in to that server (that is, until after a successful EssLogout() call). However, in case such as a server shutdown, a context handle can become invalid. Your program should therefore provide some way for the user to log back in during a session (for example, through a menu option or function key).

Note: A context handle is specific to an instance of the API, and contains an implied reference to the resources and settings for the appropriate instance.

Multiple Context Handles

A single instance of an API program may make multiple calls to EssLogin(), using the same user name or different user names on one or more Essbase servers. Each call to EssLogin() returns a unique context handle, and your program must keep track of each context handle returned. You may have up to 255 context handles per client application in use simultaneously, but if a program performs all its processing on a single server, in general it is easier to use only one context handle and to switch between different applications and/or databases as required, using either the EssSetActive() function or the EssAutoLogin() function.

Sharing Context Handles

In general, it is not advisable to share context handles between multiple programs, processes, or threads, unless such use is guaranteed to be exclusive. A better approach is to use the same instance handle and log in each process or thread separately. Essbase ensures that multiple logins using the same user name on the same server will only occupy one port on that server.

Local Context Handles

Operations on local objects and files (on the client) can use a local context handle. Refer also to Local Contexts.



File Objects

An Essbase object is simply a file (in 8 by 3 alphanumberic character format) Essbase uses, such as a database outline, a calc script, or other data. Essbase has an object system which allows you to refer to such files through the API simply by the name, the file type, and the application and database with which they are associated. This allows objects to be manipulated independently of the underlying file system (which may vary between different platforms and implementations of Essbase).

Objects can reside on any Essbase server or client, and can be copied between them. A locking mechanism on the server controls access to objects, so that users with sufficient privilege can lock server objects and copy them to the client (using the EssGetObject() function), edit them, and save them back to the server (using the EssPutObject() function). Server objects can also be opened without locking for read-only access, but then cannot be saved back to the server. A user can also create or edit objects on client workstations for their personal use, or save them to the server for other users to share.

Accessing Objects

When you access objects through the API, the object name refers to the file name of the object (without an extension). The object types are declared in the API header file in the form ESS_OBJTYPE_xxx (where xxx represents the specific type, as in ESS_OBJTYPE_REPORT). Most objects are associated with an application and database, but some objects such as calc scripts and rules files can be stored at the application level and used with any database within the application.

Database outline files are different from other objects, and cannot be deleted, renamed, copied, or saved using the API.

Server object files are physically located in the corresponding application or database sub-directory. However, it is not generally advisable to manipulate server object files directly. Always use the appropriate API functions to copy the files locally.

Client object files are also stored by default in application and database sub-directories of the directory specified by the LocalPath setting of ESS_INIT_T. You can freely manipulate and edit these files, but you should ensure your program is well-behaved when locking and unlocking server objects which are being edited on the client (always lock an object before editing and unlock it afterwards, whether or not changes are saved).

You can bypass the client object system and go directly to the file system by setting the application and database to NULL. This makes the object field the entire path.

Local Contexts

If you intend to access file objects on a client machine through the API, you need to create a local context handle for the API object functions to use. To create a local context, use the EssCreateLocalContext() function, which returns a context handle. This handle can be passed to any of the object API functions instead of a login context handle, and causes the API to perform the requested operation on the local client object system instead of the server. You only need to create a local context once, immediately after your program first initializes the API.

If you create a local context, your program should clean up by calling the EssDeleteLocalContext() function before terminating the API.



Using Memory in C Programs

All programs perform some form of memory allocation. The Essbase API allocates memory internally, some of which is returned in the form of pointers to the calling program. The calling program can also allocate memory, which is passed as pointers to the API. To avoid potential conflicts between different memory management schemes, the API provides the following two mechanisms for integrating the memory management in your application:

Using the C API's Memory Management Scheme

The API provides a set of memory management functions, EssAlloc(), EssRealloc(), and EssFree(). These functions (plus all internal API memory allocations) call memory allocation routines pointed to by the AllocFunc, ReallocFunc, and FreeFunc fields of the ESS_INIT_T initialization structure. If you pass NULLs into these fields, you use the default allocation routines supplied with the API, which use native memory application routines appropriate to the target platform.

The native memory allocation routines called by all platforms call the C standard library calls malloc(), realloc(), and free(). The C standard library calls accommodate the operation of the Outline API, which uses many small allocations of memory during normal usage. Unlike GlobalRealloc(), realloc() does not initialize new buffer areas to NULLs.

Note: If you are using a compiler for an Intel X86-based Microsoft Windows platform, remember that the API exclusively uses the large memory model.

Customizing the Memory Management Scheme

If you do not want to call the API's memory management functions, or you want to ensure that the same allocation scheme is used consistently throughout your application, you can define your own set of memory management functions for the API to use. To do this, you can write your own custom functions to allocate, reallocate, and free memory, and make your functions available to the API. Usually these functions internally call the corresponding memory management functions used within your application.

Defining Custom Memory Management Functions in C Programs

To define your own custom memory management functions in a program, you write the functions and set the Allocfunc, ReallocFunc, and FreeFunc fields in the API initialization structure to point to your custom function before calling EssInit(). You can use any names you wish for these functions and their arguments, but you must use the following form to declare them:

ESS_FUNC_M CustomAlloc   (ESS_SIZE_T BufSize, ESS_PPVOID_T ppBuffer);
ESS_FUNC_M CustomRealloc (ESS_SIZE_T BufSize, ESS_PPVOID_T ppBuffer); 
ESS_FUNC_M CustomFree    (ESS_PVOID_T pBuffer);

In this code, the fields are defined as follows:

Pointers to these three functions should then be assigned to the AllocFunc, ReallocFunc, and FreeFunc fields of the initialization structure before it is passed to the EssInit() function (see Initializing the Essbase API). In the MS-Windows environment, you will first need to create pointers to the functions by calling the Windows API function MakeProcInstance. For example, to assign your custom functions to the initialization structure fields:

ESS_INIT_T               InitStruct;
InitStruct.AllocFunc =   (ESS_PFUNC_T)MakeProcInstance((FARPROC)CustomAlloc,hInst);
InitStruct.ReallocFunc = (ESS_PFUNC_T)MakeProcInstance((FARPROC)CustomRealloc,hInst);
InitStruct.FreeFunc =    (ESS_PFUNC_T)MakeProcInstance((FARPROC)CustomFree,hInst);

Note: If you decide to define your own custom memory management functions, you must create and assign functions for all three structure fields.

After you have defined your own custom memory management functions, you cannot use the default API memory management within that application, as any calls made to the Essbase memory management API functions, EssAlloc(), EssRealloc(), and EssFree(), from within your code will automatically invoke the equivalent custom functions you defined. However, any other applications simultaneously using the API will not be affected; each application which calls EssInit() can independently choose whether to define its own custom functions or use the default ones.

Note: You should not attempt to call any Essbase API functions from within your custom message function, with the exception of the memory management API functions, EssAlloc(), EssRealloc(), and EssFree().



Message Handling

When your program calls the API, system messages and error messages are generated. Some of those messages are returned by the Essbase server, and others are internal to the API. Your program must process these messages in some way, and if there is an error which causes the operation in progress to abort, the user may need to be informed.

This section explains the API's message handling scheme, and then shows what C developers can do to implement custom message processing in their programs.

How the Essbase API Handles Messages

The following message levels are supported in Essbase:

When your program uses Essbase API default message handling, all messages of level Error or higher (Serious or Fatal) are displayed on the current application screen.

Defining a Custom Message Function in C Programs

The C API allows you to supply a custom message handling function which you can use to trap error messages before they are processed by the API. You may want to code a custom message handling function, either to trap particular error conditions, or to ensure uniform processing and display of all user messages throughout your program. If you choose not to supply a custom message function, all message processing is handled by the API default message handler.

To define a custom message function in a program, you must write the function and set the MessageFunc field in the API initialization structure to point to your custom function before calling EssInit().

Coding the Custom Message Handling Function

You can use any name you wish for this function and its arguments, but it must be declared in the following form:

ESS_FUNC_M      CustomMessage (
ESS_PVOID_T     UserContext,     /* user context pointer */
ESS_LONG_T      MessageNumber,   /* Essbase message number */
ESS_USHORT_ T   Level,           /* message level */
ESS_STR_T       LogString,       /* message log string */
ESS_STR_T       MessageString    /* message string */
);

In this code, the fields are defined as follows:

[Date & Time] Server/Application/Database/Username/Thread/Message#

For example:

[Fri Feb 04 11:51:18 1994]Elm/Sample/Basic/Admin//1012550
Total Calc Elapsed Time : [46] seconds [Fri Feb 04 11:51:18 1994]Elm/Sample/Basic/Admin//1012550 Total Calc Elapsed Time : [46] seconds

Setting the MessageFunc Field to Point to Your Function

Pointers to the custom message function must be assigned to the MessageFunc field of the initialization structure passed to the EssInit() function (see Initializing the Essbase API). In the MS-Windows environment, you first need to create a pointer to the function by calling the Windows API function MakeProcInstance(). For example, to assign your custom function to the structure field:

ESS_INIT_T               InitStruct;
InitStruct.MessageFunc = (ESS_PFUNC_T)MakeProcInstance((FARPROC)CustomMessage,hInst); 

Using a Custom Function to Control Message Processing

The custom message function is called before an Essbase server returns a message or the Essbase API returns an error. When the function is called, the arguments passed to it contain the message number, message level, log string, and error string for that particular message. For each message, the function can use these argument values to choose whether to process the message, ignore it, or return it to the API for default processing.

What the Return Code Means to the API

A return value of zero denotes that the function processed the message successfully and that no further action needs to be taken by the API. If the return code is non-zero, the message is passed to the default API message handling function for further processing and display. To have your program ignore a message, simply return a zero from the custom message function.

Note: The API automatically frees the log and message strings when it has finished processing the message. Do NOT attempt to free them within your code.

Determining which Return Code your Function Should Generate

To determine which return code to generate, you can code the custom message function to check the MessageNumber argument, and/or the Level argument. For example, a program might ignore all information messages, and possibly also any warning messages (you can make this a user-definable setting) by testing the Level argument against the appropriate constant defined in ESSAPI.H (for example, ESS_LEVEL_WARNING), and returning zero if the value is equal to or below the required value. For other messages the function should either process them internally and return a zero value, or return a non-zero value to ensure that they are processed by the default API message handler.

Note: You should not attempt to call any Essbase API functions from within your custom message function, with the exception of the memory management API functions, EssAlloc(), EssRealloc(), and EssFree().

If you define your own custom message handling function, any other applications simultaneously using the API will not be affected; each application which calls EssInit() can independently choose whether to define its own custom message function or just use the default message handler.



Choosing a Network Protocol

Essbase supports several different network protocols and different network vendor implementations by providing a number of different Essbase network drivers. The driver you need to install depends on the exact hardware, operating system, and network platform of the client machine, and on the Essbase server machine it is connecting to.

You need to determine the required network configuration and install the appropriate driver file.



Calling API Functions

This section describes calling API functions, using instance and context handles, and handling return code.

Function Declarations

The API uses the ESS_FUNC_M macro to declare C API functions. This declares them to be of type unsigned long for all supported platforms. You must also use this macro to declare any custom functions which you pass to the API, such as custom memory management or message handling functions.

Passing the Instance Handle or Context Handle

You must pass the instance handle returned by the initial call to EssInit() in calls to EssLogin() or EssTerm(). You must pass the context handle returned by EssLogin() in any function calls associated with a specific login.

Handling the Return Code

All Essbase API functions return a status code of type ESS_STS_T. A return code of zero indicates that the function was executed successfully, and a non-zero value indicates an error condition. A full list of error return constants is contained in the header file esserror.h. The corresponding message text is in messages.txt.

Note: You should always check the return code from any Essbase API function. If the return code is non-zero, any pointers or values returned by the function are undefined.

Internal Message Handling

Essbase uses an internal message handling function for non-custom message hadling. If an error event is incountered under a 32-bit Windows system, a text error message is generated.



A Typical API Task Sequence

The API requires that your program call certain functions before others. The basic ordering rules are:

This is the typical order of operations for a simple API application:

  1. Create and initialize an ESS_INIT_T structure.
  2. Initialize the API by calling EssInit().
  3. Allocate any local static or global structures.
  4. Log in to the required server by calling EssLogin() or EssAutoLogin().
  5. Select an active application and database by calling EssSetActive() or EssAutoLogin().
  6. Retrieve (or lock) data by calling EssReport() or related functions.
  7. Update data by calling EssUpdate() or related functions.
  8. Recalculate the database by calling EssCalc() or related functions.
  9. Produce reports against the data by calling EssReport() or related functions.
  10. Log out from the server by calling EssLogout().
  11. Free any local static or global structures.
  12. Terminate the API by calling EssTerm().


Initializing the Essbase API

A program must initialize the API by calling the EssInit() function before calling any other Essbase API functions. EssInit() initializes all internal API state variables, and also allows you to tailor the API to your program's requirements.

The calling program must pass the EssInit() function an initialization structure. This structure is defined in ESSAPI.H as type ESS_INIT_T. It contains a series of fields which are used to customize the API and set up certain API defaults. You must declare an instance of this structure and initialize the relevant fields before calling EssInit().

The EssInit() function returns an instance handle, which should then be passed as an argument to the API login function.



Declaring the Initialization Structure

The initialization structure passed to EssInit() can usually be declared as a local (i.e. stack) variable in the calling function, as it is usually not required once it has been passed to EssInit(). Alternatively, you can allocate the structure before calling EssInit(), then free it after returning.

If the initialization structure points to custom memory management functions in the initialization call, make sure your program frees the structure using the correct memory allocation scheme.

If any of the fields of the initialization structure are set to zero values or NULL pointers, the API will use the internal default values for those fields.

It is a good idea to clear out all structures (set to 0) before setting fields and calling the API function.


Logging in to an Essbase Server

In general, the first thing your program should do after calling EssInit() is to prompt the user for a server name, user name, and password (or use predefined defaults), then attempt to log in to that server by calling EssLogin(). Alternatively, use the encapsulated login function, EssAutoLogin(). If this call is successful, then the returned context handle should be stored and used for all subsequent API calls.



Selecting an Active Application and Database

In addition to the context handle, the login functions also return a list of the applications and databases to which the logged in user has access (a program can obtain this list at any time by calling the EssListDatabases() function. The program allows the user to select a specific application and database by calling the EssSetActive() function.

If EssAutoLogin() is used to log in, it can optionally set the active application and database.

To get information about an Essbase application (e.g. whether or not it is already loaded), call the EssGetApplicationState() or EssGetApplicationInfo() functions. To get information about a specific database, call the EssGetDatabaseState() or EssGetDatabaseInfo() functions. You can call these functions before setting the active application and database.



Retrieving and Updating Data

Retrieving Data

To retrieve data from an Essbase database, either for reporting or for subsequent updating, your program needs to use a report specification. Report specifications can be in the form of a single text string (if it is less than 32 Kbytes in length), a series of text strings, or a file. Report files can reside either on the client machine, or on the Essbase server.

Sending a Report Specification as a Single String

To send a report specification as a single string, have the program call EssReport() passing the entire report string, not greater than 32 Kbytes long, as an argument. If the Output flag is set to TRUE in the call to EssReport(), the program must also read the returned report data by calling EssGetString() repeatedly until a NULL string is returned. The returned data can then be displayed, written to a file, or printed, as required.

Sending a Report Specification as a Series of Strings

To send a report specification as a series of strings, first call EssBeginReport(), then call EssSendString() repeatedly to send each string in the report specification (note that in Windows, each individual string must not be greater than 32 Kbytes long). Finally, terminate the report specification by calling EssEndReport(). If the Output flag is set to TRUE in the call to EssBeginReport(). The program must also read the returned report data by calling EssGetString() repeatedly until a NULL string is returned. The returned data can then be displayed, written to a file, or printed, as required.

Sending a File as a Report Specification

To send a file as a report specification, use the EssReportFile() function, passing the report file name. If the Output flag is set to TRUE in the call to EssReportFile(), the program must also read the returned report data by calling EssGetString() repeatedly until a NULL string is returned. The returned data can then be displayed, written to a file, or printed, as required.



Updating Data

To update data in the database, you should first lock the blocks in the database which you are going to update. To do this, do either of the following:

The database can be updated either from a single string, a series of strings, or a file. Update data files can reside either on the client machine, or on the Essbase server.

Sending Update Data as a Single String

To send an update as a single string, call EssUpdate() passing the entire string as an argument. (Note that in MS-Windows, the string must not be greater than 32 Kbytes long). Set the Store flag to TRUE in the call to EssUpdate() so that the database will be updated. If the Unlock flag is also set to TRUE, any locked data blocks in the database will be unlocked once the data is updated, to allow other users to update those blocks.

Sending Update Data as a Series of Strings

To send an update as a series of strings, first call EssBeginUpdate(), then call EssSendString() repeatedly to send all the data (note that in MS-Windows, each individual data string must not be greater than 32 Kbytes long). Finally, terminate the update by calling EssEndUpdate(). Set the Store flag to TRUE in the call to EssUpdate() so that the database will be updated. If the Unlock flag is also set to TRUE, any locked data blocks in the database will be unlocked once the data is updated.

Sending Update Data as a File

To send an update as a file, use the EssUpdateFile() function, passing the data file name. Set the Store flag to TRUE in the call to EssUpdate() so that the database will be updated. If the Unlock flag is also set to TRUE, any locked data blocks in the database will be unlocked once the data is updated.



Recalculating the Database

After updating any data in the database it is essential to perform a recalculation to ensure that the consolidated totals are correct. To recalculate a database, you can either perform the default calculation, or send a specific calculation script. You can also set a calculation script to be the default calc script. Calc scripts can be sent either as a single string, a series of strings, or a file. Calc script files can reside either on the client machine, or on the Essbase server.

Sending a Calc Script as a Single String

To send a calc script as a single string, call EssCalc() passing the entire string as an argument (note that in MS-Windows, the string must not be greater than 32 Kbytes long). Set the Calculate flag to TRUE in the call to EssCalc() so that the calc script will be executed. You will then need to check on the progress of the calculation at regular intervals.

Sending a Calc Script as a Series of Strings

To send a calc script as a series of strings, first call EssBeginCalc(), then call EssSendString() repeatedly to send all the strings in the calc script (note that in MS-Windows, each individual string must not be greater than 32 Kbytes long). Finally, terminate the script by calling EssEndCalc(). Set the Calculate flag to TRUE in the call to EssBeginCalc() so that the database will be recalculated. You will then need to check on the progress of the calculation at regular intervals (see "Checking the Progress of Calculations").

Sending a Calc Script as a File

To send a calc script as a file, use the EssCalcFile() function, passing the calc script file name. Set the Calculate flag to TRUE in the call to EssCalcFile() so that the database will be recalculated. You will then need to check on the progress of the calculation at regular intervals (see "Checking the Progress of Calculations").

Using the Default Calc Script

To recalculate a database using the current default calc script, use the EssDefaultCalc() function. To set the default calc script for a database, use EssSetDefaultCalc(), passing the calc script as a single string. To set the default calc script from a file, use the EssSetDefaultCalcFile() function, passing the calc script file name. Use EssGetProcessState() to determine when the calculation is finished (see "Checking the Progress of Calculations").

Checking the Progress of Calculations

After a database calculation is started, check the progress of the calculation at regular intervals (five seconds is recommended) by calling the EssGetProcessState() function. This function returns a structure indicating the calculation state. Call EssGetProcessState() until it indicates that the calculation is finished or that an error has occurred. You may also cancel a calculation in progress with the EssCancelProcess() function.

Warning: While a calculation is in progress, do not attempt to call any API functions other than EssGetProcessState() or EssCancelProcess() using the same context handle, until the calc operation has completed successfully or has been canceled. After EssGetProcessState() indicates the calc has finished, your program may continue performing other API operations with that context handle.



Logging Out from the Essbase Server and Terminating the API

When all database operations are complete, the application should first log out by calling EssLogout(). This frees up any internal resources reserved within the database, and may also free the login port on the server for use by another user.

When an application program is about to terminate, it should call the EssTerm() function, passing the instance handle which was returned from the original call to EssInit(). This releases all resources used by the Essbase API. After calling this function, no other API calls can be made, unless EssInit() is called again to reinitialize the API.



Common Problems and Solutions

The Essbase API gives you unrestricted access to many of the same functions that the Essbase Application Manager uses. To ensure that the design of an application does not result in data loss or interrupted operation, it is recommended that you become familiar with the sequence of operations the Application Manager uses to complete tasks.

This section is a quick reference to help you in identifying and solving the most common problems.

Problem

Your program is generating protection faults when allocating or freeing memory.

Solution

Here are some things C programmers can check:

Even if the errors are occurring when accessing memory not used by Essbase, there may be some interference between the Essbase memory management scheme and your own. You might consider defining your own custom memory management functions.

Problem

Your program generates an Essbase error when calling an API function.

Solution

Most of the Essbase error messages are self-explanatory, and it should be fairly obvious where the problem lies. However a couple of common errors to watch out for are (%n indicates a message argument which is replaced by a context-specific string):

Problem

Your program is consistently receiving an Essbase error return code from an API function, but no message is displayed, or a message saying "No message for message #%1 in message database" is generated.

Solution

Certain internal API errors cannot display a message, typically because the user's context information is not available when the message occurs. In these cases, make a note of the error code returned from the function, then refer to the list of error messages in messages.txt to find the corresponding message text. The error constants themselves are contained in esserror.h.

Problem

When accessing fields in API-defined structures, they appear to contain the wrong values, or the values seem to be "shifted" by a few bytes.

Solution

Check your compiler defaults to ensure you have structures aligned on byte boundaries. If the problem still occurs, make sure you are compiling with the most recent versions of the API header files, and linking with the most recent API DLLs.