<< 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.
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.
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;
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.
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