Using Server DLLs

<< Click to Display Table of Contents >>

Navigation:  Apollo VCL Components > Apollo VCL Component Reference > TApolloServerDLL >

Using Server DLLs

 

Server DLLs are DLLs developed using Delphi/C++Builder that are used by the Apollo Database Server to execute remotely. This technology is unique to Apollo and Apollo Database Server. Developers can create Server DLLs that manage data files on the server as if the files were local. Once deployed onto the server and registered with Apollo Database Server, Server DLLs can be accessed using the TApolloServerDLL component.

Simple use of Server DLLs can make client/server applications run faster. Since a Server DLL is executable code that resides and runs on the server machine, it can dramatically reduce data transfer requirements.

How it Works

A Server DLL is registered to the Apollo Database Server using the Apollo Server Manager. An Apollo Client application has a TApolloServerDLL and a TApolloConnection component, which point to the Apollo Database Server. The client application can then select which Server Procedures to run and define an parameters to pass (Note: A Server Procedure is a procedure contained in a Server DLL). Once called, the procedure is executed on the server – retrieving, creating, modifying, deleting records, or performing whatever is defined in the procedure. Once complete, the Server Procedure optionally returns parameters back to the client.

Creating Server DLLs (Formerly Stored Procedures)

Apollo Database Server Server DLLs can be developed using Delphi or C++Builder. These DLLs get deployed on the server compurter in the directory that holds the database that the DLL will operate on. The DLLs can have any name, and multiple DLLs may be placed in each database.

Multiple DLLs can be used and deployed on the server, but note that server DLLs located in the same data directory must have unique exported procedure names. All exported functions or procedures within the DLL files within each database are assumed to be available for use by the Apollo Database Server and any connected client applications.

image\tip.gif The Server DLL function names are case-sensitive and must be called by your client application exactly as they are shown in this window.

Server DLL Template

The format of the exported functions within the Server DLL must follow this template:

procedure MyProcName(

         SessionData: TSessionData; // 1. Session Data object

         ExecType: TExecType);  // 2. Reserved for future use

         stdcall;      // 3. Exported function

begin

 // write custom code here

end;

Preparing Server DLLs for use:

1.Create a Server DLL using the template format above.

2.Copy the .DLL file into the directory on the server that contains the tables that the DLL will be accessing.

3.Run the Apollo Server Manager (ADSManager.exe) and select the Manage Server DLLs tab (shown above).

4.Select the Database Name listed on the left pane and all .DLLs located in the databse directory will be listed in the right window.

5.Check all Server DLLs in that you wish the Apollo Server to recognize as available. Only DLLs that have been checked will be considered for use by the server.

image\svrmgr4-256.gif

Apollo Server Manager – Manage Server DLLs tab

 

All exported procedures in the Server DLLs are displayed under the database name in the left window. These function names are read directly from the .DLL. therefore if a Server DLL function is not displayed, then the DLL does not export the function or you are attempting to register the wrong DLL. This provides instant verification of the available functions in a Server DLL and greatly helps the debugging process.

Executing Procedures in Server DLLs

The TApolloServerDLL component is used to access the the Server DLLs' procedures, including the parameter values to pass to the procedure. The TApolloServerDLL component connects to the server via a TApolloConnection component. Set the TApolloServerDLL.ApolloConnection property to the TApolloConnection component associated with the database that contains the tables to be used with the Server DLL's procedure.

Parameter(s) that will pass to the Apollo server must be defined and created. These paramaters will in turn be passed to the Server DLL. Once these paramaters are created, they will remain on the server so there is need to create them again.

ProcName := 'XXXX' automatically fetches the parameter list from the server. This list can be checked using the FindParam method.  If FindParam returns nil, it signifies the initial call (i.e. the Apollo Server does not have the parameter yet) therefore we should create it. If the FindParam return value is not nil, then the Apollo Server already has this parameter defined and therefore there is no need to re-create it.

Example #1

The following is a client-side procedure that gives all employees in the specified state ("CA" in this case) a 10% raise in their salary.

 

procedure TForm1.SalaryUpdate;

var

 iCount: Integer; 

begin

 // Use the Server DLL 

 with ApolloServerDLL1 do 

 begin 

         DataBaseName := 'SampleData'; 

           // Always set ProcName first

         ProcName  := 'SalaryUpdate';  

 

         // Check if the server has a copy of all parameters needed 

         // Do not create the params if they already exist 

         if ProcParams.FindParam('STATE') = nil then 

                 ProcParams.CreateParam(ftString, 'State', ptInput); 

 

         // Note that this is a ptOutput paramater that the 

         // Server Procedure uses 

          if ProcParams.FindParam('COUNT') = nil then 

                   ProcParams.CreateParam(ftInteger, 'Count', ptOutput);

 

         // Call the Server Procedure that will 

           // increase the salary for Californians

         ProcParams.ParamByName('STATE').AsString := 'CA'; 

         ExecProc; 

         iCount := ProcParams.ParamByName('COUNT').AsInteger; 

         ShowMessage( IntToStr( iCount ) + ' record(s) were updated'); 

 end; 

end;

 

The corresponding procedure in the Server DLL would look like this:

 

uses

 …, ApoDSet, ApolloEngInt;

 

// This example makes use of direct Apollo API function

// calls to access a table already opened by the client.

procedure SalaryUpdate( SessionData: TSessionData; ExecType: TExecType ); stdcall;

var

 dVal: Double; 

 iCount, SavedWA, i, iWA : Integer; 

 sState : String; 

begin

 

 // In this example, we are operating on a table that is  

         // find the workarea of the table. Check here to see

         // table is open or not -- Table must be open

 iWA := 0; 

 for i := 0 to 254 do 

 begin 

         if (SessionData.TableName[i] = 'EMPLOYEES') or (SessionData.TableName[i] = 'EMPLOYEES.DBF') then 

         begin 

                 iWA := SessionData.WASelected[i]; 

                 break; 

         end; 

 end; 

 

 // If table was not found (or open) exit now 

 if iWA < 1 then 

         exit; 

 

 // For fastest data access, use low-level Apollo calls 

 SavedWA := sx_Select(iWA); 

         // Get the paramater passed by the client

 sState := TParams(SessionData.Params).ParamByName('STATE').AsString; 

 sx_Query(PChar('STATE = "' + sState + '"')); 

 iCount := 0; 

 if (sx_QueryRecCount > 0) then 

 begin 

         sx_GoTop; 

         while (not sx_Eof) do 

         begin 

                    // Increase the salary

                 dVal := sx_GetDouble('SALARY') * 1.1;  

                 sx_Replace('SALARY', R_DOUBLE, @dVal); 

                           // Skipping will automatically commit the record (Post)

                 sx_Skip(1);  

                 Inc(iCount); 

         end; 

                   // Explicitely commit (Post)

         sx_Commit;

 end; 

 

 // Update the output paramater with the 

 // number of records modified

         TParams(SessionData.Params).ParamByName('Count').AsInteger := iCount;

 // reset the work area

 sx_Query(''); 

         sx_Select(SavedWA);

end;

image\tip.gif At least once before making any direct calls to Apollo API functions (sx_*), you must first call 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.

An easy way to insure that your Server DLLs are initialized properly in order to call the Apollo API functions, is to add a single call to LoadDLL at the bottom of the .DLL source in an external begin or initialization section as follows:

 

...

exports

InsertRecord, 

SalaryUpdate; 

 

Begin

         // Important! Make sure Apollo is loaded for low-level sx_* API calls

 LoadDLL;  

end.

 

See the included SampleProc.dll source (SampleProc.dpr) for a complete example of this (located in the ..\ServerDLL directory.

image\tip.gif The Server side DLL procedure example above makes use of direct API calls to the Apollo DLLs. For additional information on this, see the Direct Calls To Apollo API Functions section.

Example #2

Here's another procedure on the client that will add a new record to a table that is not already opened by the client and replace some field values.

 

procedure TForm1.InsertRecord;

begin

 with ApolloServerDLL1 do 

 begin 

                  // Always set ProcName first

         ProcName := 'InsertRecord';  

 

         // Check to see if server has a copy of all the  

                  // parameters needed. Do not create them if

                  // they already exist

         if ProcParams.FindParam('First') = nil then 

                 ProcParams.CreateParam(ftString, 'First', ptInput); 

 

         if ProcParams.FindParam('Last') = nil then 

                 ProcParams.CreateParam(ftString, 'Last', ptInput); 

 

         if ProcParams.FindParam('Age') = nil then 

                 ProcParams.CreateParam(ftInteger, 'Age', ptInput); 

 

         if ProcParams.FindParam('State') = nil then 

                 ProcParams.CreateParam(ftString, 'State', ptInput); 

 

         if ProcParams.FindParam('Salary') = nil then 

                 ProcParams.CreateParam(ftFloat, 'Salary', ptInput); 

 

                   // Return Value

         if ProcParams.FindParam('Success') = nil then 

                 ProcParams.CreateParam(ftBoolean, 'Success', ptOutput);  

 

         // Set values to be replaced 

         ProcParams.ParamByName('First').AsString := 'John'; 

         ProcParams.ParamByName('Last').AsString := 'Williams'; 

         ProcParams.ParamByName('Age').AsInteger := 36; 

         ProcParams.ParamByName('State').AsString := 'CA'; 

         ProcParams.ParamByName('Salary').AsFloat := 567.89; 

 

         ExecProc; 

         if ProcParams.ParamByName('Success').AsBoolean then 

                 ShowMessage('Insert was successfull') 

         else 

                 ShowMessage('Error inserting record'); 

         end; 

end;

 

The corresponding procedure in the Server DLL would look like this:

 

uses

 …, ApoDSet, ApolloEngInt;

 

// This example dynamically creates a TApolloTable

// object to open the table on the server and perform

// the update. In this example, the name of the table is

// hardcoded in the procedure. However, the table name

// could also be made an input parameter.

procedure InsertRecord( SessionData: TSessionData; ExecType: TExecType); stdcall;

var

 ApTable : TApolloTable; 

 i : Integer; 

 sPath : String; 

begin

 TParams(SessionData.Params).ParamByName('SUCCESS').AsBoolean := FALSE; 

 

         // Find the physical path of the table and open it

 sPath := ''; 

 for i := 0 to 254 do 

 begin 

         if (SessionData.TableName[i] = 'EMPLOYEES') or (SessionData.TableName[i] = 'EMPLOYEES.DBF') then 

         begin 

                 sPath := SessionData.DataPath[i]; 

                 break; 

         end; 

 end; 

 

 if sPath = '' then 

         exit; 

 

 // Create a TApolloTable object in the Server DLL

 ApTable := TApolloTable.Create(nil); 

 try 

         with ApTable do 

         begin 

                 // Use SpeedMode to speed up data processing 

                    SpeedMode := True; 

                 DataBaseName := sPath; 

                 TableName := 'EMPLOYEES.DBF'; 

                 TableType := ttSXFOX; 

                 Open; 

                 Append; 

 

                 with TParams(SessionData.Params) do 

                 begin 

                         if FindParam('FIRST') <> nil then 

                                 FieldByName('FIRST').AsString := ParamByName('FIRST').AsString; 

 

                         if FindParam('LAST') <> nil then 

                                 FieldByName('LAST').AsString := ParamByName('LAST').AsString; 

 

                         if FindParam('AGE') <> nil then 

                                 FieldByName('AGE').AsInteger := ParamByName('AGE').AsInteger; 

 

                         if FindParam('STATE') <> nil then 

                                 FieldByName('STATE').AsString := ParamByName('STATE').AsString; 

 

                         if FindParam('SALARY') <> nil then 

                                 FieldByName('SALARY').AsFloat := ParamByName('SALARY').AsFloat; 

 

                         Post; 

                         ParamByName('SUCCESS').AsBoolean := True; 

                 end; 

         end; 

 finally 

         ApTable.Close; 

         ApTable.Free; 

 end; 

end;

 

See Also

TApolloServerDLL, TApolloQuery, Direct Calls To Apollo API Functions , TSessionData