Direct Calls To Apollo API Functions

<< Click to Display Table of Contents >>

Navigation:  Apollo VCL Components > Optimizing Performance >

Direct Calls To Apollo API Functions

 

When making repeated calls to TApolloTable methods that read or write .DBF data, additional performance enhancements can be achieved by bypassing the object layer and going directly from your code to the Apollo API functions. This is generally not as convenient as simply going through the TApolloTable object. However, for optimizing tight loops, this is one method that Apollo VCL power-users can employ to make their applications perform as fast as possible.

 

The following is a listing of the Delphi declarations for Apollo API functions that would most commonly be used for data storage and retrieval. These functions are declared in the ApolloEngInt unit included with Apollo VCL, and the ApolloEngInt.PAS file can be referenced for a complete listing of the API function syntax. By simply including ApolloEngInt in the uses line of your unit, any of these direct function calls can be used within your Delphi code. Under C++Builder, just add #include "ApolloEngInt.hpp" to the top of the calling .CPP file.

 

The TApolloTable method name is generally the same as the sx_* function name. One exception is TApolloTable.GetString, which has no direct sx_* function equivalent. This is due to Delphi's 255-byte string length limitation. The TApolloTable.GetStringEx method corresponds directly to the sx_GetString function.

 

procedure sx_Append;

procedure sx_AppendBlank;

function sx_AppendFrom( cpFileName: PChar; iSourceType: Integer;

cpScopeExpr: PChar ): Bool;

function sx_BlobToFile( cpFieldName: PChar; cpFileName: PChar ): Bool;

function sx_Bof: Bool;

procedure sx_Delete;

function sx_Deleted: Bool;

function sx_Eof: Bool;

function sx_GetBlob( cpFieldName: PChar; vpVar: Pointer ): LongInt;

function sx_GetBlobLength( cpFieldName: PChar ): LongInt;

function sx_GetByte( cpFieldName: PChar ): PChar;

function sx_GetDateJulian( cpFieldName: PChar ): LongInt;

function sx_GetDateString( cpFieldName: PChar ): PChar ;

function sx_GetDouble( cpFieldName: PChar ): Double ;

function sx_GetInteger( cpFieldName: PChar ): Integer;

function sx_GetLogical( cpFieldName: PChar ): Bool;

function sx_GetLong( cpFieldName: PChar ): LongInt;

function sx_GetMemo( cpFieldName: PChar; uiLineWidth: Integer ): PChar;

procedure sx_GetRecord( cpRecord: PChar );

function sx_GetString( cpFieldName: PChar ): PChar;

function sx_GetTrimString( cpFieldName: PChar ): PChar;

procedure sx_Go ( lRecNum: LongInt);

procedure sx_GoBottom;

procedure sx_GoTop;

function sx_KeyAdd(cpTagName : PChar) : Bool;

function sx_KeyDrop(cpTagName : PChar) : Bool;

function sx_PutBlob( cpFieldName: PChar; vpVar: Pointer; lSize: LongInt ):

LongInt;

procedure sx_PutRecord( cpRecord: PChar );

function sx_RecNo: LongInt;

procedure sx_Replace( cpFieldname: PChar; iDataType: Integer; vpData: Pointer );

function sx_RLock( lRecNum: LongInt ): Bool;

function sx_Seek( cpKeyValue: PChar): Bool;

function sx_Select( uiBaseArea: Integer ): Integer;

procedure sx_Skip( lNumRecs: LongInt );

procedure sx_Unlock( lRecNum: LongInt );

procedure sx_WorkArea( lRecNum: LongInt );

A Simple Case

The following code uses standard TApolloTable calls to append 500 new records and replace two field (ITEM and CODE) with unique values based on the loop counter, posting each new record.

 

for x := 1 to 500 do

begin

ApTbl.Append;

ApTbl.FieldByName('ITEM').AsInteger := x;

ApTbl.FieldByName('CODE').AsString := 'ABC' + IntToStr(x);

ApTbl.Post;

end;

 

Using direct calls to the apollo9.dll functions, the same end result can be achieved like this:

 

for x := 1 to 500 do

begin

sx_AppendBlank;

sx_Replace( 'ITEM', R_INTEGER, @x );

sVal := 'ABC' + IntToStr( x ) + #0;

sx_Replace( 'CODE', R_CHAR, @sVal[1] );

end;

ApTbl.Post;

 

Notice that a call to ApTbl.Post was not included within the loop in the second example. Instead, only a single call to ApTbl.Post is made after the loop has completed. This is due to another optimization technique that many Xbase developers are unaware of. When performing sequential AppendBlank calls, a newly-appended record commits the changes from the previously-appended record. So, adding a Post (or Commit) following each Append within the loop is redundant code and can severely slow performance.

 

The same applies to the implicit record lock that is applied by the Append/AppendBlank call. There is no need to call Unlock before appending the next record, since the Append call also releases any implicit record lock that was applied by a previous Append.

When Using Multiple Tables

If your project uses multiple tables, making direct calls to the Apollo API functions is a bit more complex. The use of TApolloTable objects keeps each work area encapsulated. However, when calling the sx_* functions directly, since they are not attached to the TApolloTable object, you must take steps to insure that the proper work area is selected before performing an operation.

 

Selecting a work area is handled by the sx_Select() function. This function takes the desired TApolloTable.Handle property, which must be cast as a Word, as a parameter.

 

For example, using the example above, if you wanted to perform these operations on the table attached to the ApTbl object:

Delphi:

sx_Select( Word( ApTbl.Handle ));

for x := 1 to 500 do

begin

 sx_AppendBlank; 

 sx_Replace( 'ITEM', R_INTEGER, @x ); 

 sVal := 'ABC' + IntToStr( x ) + #0; 

 sx_Replace( 'CODE', R_CHAR, @sVal[1] ); 

end;

sx_Commit;

C++Builder:

sx_Select( Word( ApTbl->Handle ));

for (x = 0; x < 500; x++)

{

 sx_AppendBlank; 

 sx_Replace( "ITEM", R_INTEGER, &x ); 

 

 StrPCopy( cpVal, "ABC" + IntToStr( 100 )); 

 sx_Replace( "CODE", R_CHAR, cpVal ); 

}

sx_Commit;

When Using Data-Aware Controls

If your project uses data-aware controls, such as DBGrid or DBEdit, you must also add a couple extra lines of code to allow the controls to reflect the updates caused by your direct Apollo API calls. These extra steps are handled for you automatically when you go through the TApolloTable object instead.

 

You must precede the sx_* call with a call to TApolloTable.UpdateCursorPos and follow the sx_* call with a call to TApolloTable.Refresh. For example:

 

Delphi:

var

 oldWa: Word; 

begin

 oldWa := sx_Select( Word( ApDS2.Handle )); 

 ApTbl.UpdateCursorPos; // Required before sx_Seek

 sx_Seek( 'Loren' ); 

 ApTbl.Refresh; // Required after sx_Seek

 sx_Select( oldWA ); 

end;

C++Builder:

Word oldWa;

oldWa = sx_Select( Word( ApDS2->Handle ));

ApTbl->UpdateCursorPos(); // Required before sx_Seek

sx_Seek( "Loren" );

ApTbl->Refresh(); // Required after sx_Seek

sx_Select( oldWA );

 

This is required for any operation that would change the display of a data-aware control, including moving to a different record with sx_Skip or sx_Seek, adding or replacing data, and so forth.

 

Note that if you are performing multiple operations within a loop, you would not need, or want, to do this for each and every sx_* call. Instead, you would call TApolloTable's UpdateCursorPos once before the set of sx_* calls, and follow them up with a single call to TApolloTable's Refresh method. Unless you specifically want or need to watch the data in the data-aware controls during this process, it is also a good idea to completely disable them during the process. See the Disabling Data-Aware Controls section for details on this technique.

 

 

Important: At least once before making any direct calls to Apollo API functions (sx_*), you must first make a call to LoadDLL. This procedure is inside the ApolloEngInt.PAS file and loads the apollo9.dll into memory. Failure to load the apollo9.dll before calling an sx_* function directly will result in an Access Violation error.

 

An easy way to insure that your stored procedure DLLs are initialized properly to call Apollo API functions, you can add a single call to LoadDLL at the bottom of the .unit source in an external begin or initialization section, like this:

 

...

begin

LoadDLL; // Important!

 

end.