Advantage Concepts
Data operations performed inside interface version 2 or greater Advantage Extended Procedures will not check user privileges. This allows you to hide tables or other database objects from the user(s), yet still provide controlled access to them through AEPs.
The database management features of ARC make it very easy to register and maintain Advantage Extended Procedures. It is possible to add/remove procedures through SQL statements, but if you’d prefer a visual approach, open the database in ARC and maintain your procedures this way.
Select New from the File menu. Select the Advantage tab. You will see an AEP template project. Use this template to accelerate your AEP development.
If you choose not to use the template, be sure to set the global VCL variable "IsMultiThread" to TRUE somewhere in your DLL startup code. This variable is used by the Delphi VCL memory manager to decide if it needs to be thread-safe. Because the Advantage server, which loads your DLL, is multi-threaded, this variable must be set to TRUE in order to avoid random exceptions in your AEPs.
Select New->Project from the File menu. Select AdvantageAEP from the list of project templates.
By default, AEP containers can be updated without stopping the Advantage Database Server or disconnecting dictionary users. This is possible through the DLL Caching functionality that leaves the original AEP container available for replacement. If Advantage detects a change in the AEP container, it will automatically transition users of the old container to a copy of the new one. However, if DLL Caching is disabled and there are connected users that are using, or who have used, the AEP, they must disconnect before the AEP can be replaced. Be sure to update the Advantage Data Dictionary if exported procedures have been added or removed (see Register Your Procedure with a Data Dictionary).
Note to Linux users In Linux you can replace the AEP even while it is in use. Keep in mind, clients will not use the new AEP until the Advantage Database Server has unloaded the current version and re-loaded the AEP from disk. If DLL Caching is enabled (the default setting), this will happen once the Advantage Database Server detects a change in the AEP container. If DLL Caching is disabled, this will happen once all users that have called functions in the AEP have disconnected.
Note to .NET users The .NET environment keeps assemblies loaded in memory until the application that was using them terminates. Because the Advantage Database Server is the application using the AEPs, it must be stopped before .NET AEPs can be updated.
Open all input and output tables in a shared mode so that data can be shared with other entities.
Protect global variables with critical sections.
Export all procedures. In Delphi this means add them to the exports clause; in C this means declare them as exported entry points.
Use the connection ID passed to Startup, Shutdown, and all procedures to uniquely identify a connection.
Use the user name and password parameters when logging into a database from within a procedure.
TDataSet Descendant users: If using AEP interface version 1 set the TAdsConnection.StoredProcedureConnection property to True on all connections that live inside an AEP data module.
Visual Basic users: If using AEP interface version 1, pass the "StoredProcedureConnection=TRUE" option in your ADO connection string for any connections that live inside your AEP.
C users: If using AEP interface version 1, include the ADS_STORED_PROC_CON option in the ulOptions parameter when making Advantage connections from within an AEP.
Put the .aep file in the client directory if you plan on using AEPs when connected to a NetWare server. If the .aep file is installed on the client it will be loaded locally, instead of across the network from the data dictionary directory.
Use a local server connection to open the input and output parameter tables if you plan on using AEPs when connected to a NetWare server.
If you plan on using AEPs when connected to a NetWare server use parameters to pass stored procedure input parameters. For example, use "EXECUTE PROCEDURE myproc( :lastname )" as opposed to "EXECUTE PROCEDURE myproc( 'Anderson' )".
Use UNC (\\SERVERNAME\SHARE) when making connections or opening tables from inside an AEP. Also use UNC for any TDataSet aliases that are used from inside an AEP. Failure to do so may result in 7077 and 7008 errors.
Use the __error table to return an error code and/or error string to the client application.
When writing a procedure using the .NET Data Provider or the OLE DB provider (for ADO), remember that some statement level settings are specified via the connection string. The settings that you may need to specify explicitly are LockMode and CharType when using DBF (non-ADT) table types. These settings are not inherited from the connection and they are not stored in the data dictionary.
Change the .AEP file extension of the Advantage Extended Procedure.
If writing directly to the Advantage Client Engine API do not call AdsOpenTable or AdsCreateTable and pass a connection handle of zero. Always pass a connection handle retrieved from a previous call to AdsConnect60.
If writing an AEP using the Advantage TDataSet Descendant, always use a TAdsConnection component. Do not open tables without first pointing them to a TAdsConnection component.
Use thread local storage (threadvars). A single client can have multiple different Advantage Database Server threads performing actions on its behalf. You are never guaranteed the thread that performed work for your last request will also be performing the work for your next request.
Raise messages boxes or wait for user input from within an AEP. This will hang the Advantage worker thread, and you will have to restart the Advantage server to reclaim the thread. AEPs are a server-side process, and should never require user interaction.
If connecting to a NetWare server, do not share connections between multiple threads. Use one Advantage connection per thread.
Visual Basic 6 users: Call DoEvents or any other function/procedure that will yield control of the processor inside of an AEP. Ways in which your code can yield control of the processor include:
Calling DoEvents
Invoking the properties or methods of an object on another thread, or in another process
Raising an event that’s handled by an object on another thread, or in another process
Invoking a cross-thread or cross-process method from within a method
Showing a form
Use drive letters when making connections or opening tables from inside an AEP. Use UNC (\\SERVERNAME\SHARE) instead. Failure to do so may result in 7077 and 7008 errors.
Objects containing the AEPs must be registered on the server running Advantage Database Server. See Microsoft documentation on regsvr32.exe (for COM DLLs), regasm.exe (for .NET assemblies), and other registration methods.
If using the Advantage Local Server, the object containing the AEPs must be registered on each client machine.
Advantage uses ProgID values to identify COM and .NET classes. A ProgID is a programmer-friendly string value used to identify a class, and is easier to work with than a ClassID (GUID).
In Visual Basic 6 (as opposed to VB .NET) all ActiveX DLLs run in a single-threaded apartment. This means all global variables are safe because only one Advantage thread can be calling a stored procedure at any instance. While this makes writing an AEP simple (no need to protect global variables from other threads) it can cause major performance problems in a multi-user environment. AEPs written in VB6 will be executed in a synchronous fashion. This means only one AEP will be allowed to run at a time. It is recommended you use VB.NET or Visual C# .NET to write all Advantage AEPs.
If using Advantage Local Server the client application and the AEP must use the same "ShowDeleted" setting. If both the client and AEP are written in the same language this is usually not a problem. If the settings are mismatched you may receive a 6619 (comm layer was busy) error.
The Advantage Database Server makes a call to the COM library approximately every 10 seconds asking it to free any unused COM objects (AEP libraries). We have seen various behaviors depending on the version of Windows in use. You may have to unload ADS in order to update AEP libraries, depending on the behavior of the COM library on your server.
The .NET environment keeps assemblies loaded in memory until the application that was using them terminates. Because the Advantage Database Server is the application using the AEPs, it must be stopped before .NET AEPs can be updated.
Because AEPs run against a NetWare server scaled back to the client, any error codes returned from inside an AEP will not be included as a "native error" inside of a 7200 error code (as they will when run against other operating systems). If your code parses error strings you need to be aware of this difference when scaling to other operating systems.
If an AEP will run against a NetWare server, you must write to the __error and __output tables using navigational commands (as opposed to SQL) because the SQL processed on the server will not know about the AEP that is running locally on the client. You can change the exception handling code in the default AEP template to look similar to the code below:
except
on E : EADSDatabaseError do
begin
{* ADS-specific error, use ACE error code *}
tblError := tadstable.create(nil);
tblError.Databasename := DM1.tblInput.databasename;
tblError.TableName := '__error';
tblError.Open;
tblError.Append;
tblError.Fields[0].asinteger := e.aceerrorcode;
tblError.Fields[1].AsString := e.Message;
tblError.Post;
tblerror.Free;
end;
on E : Exception do
begin
{* other error *}
tblError := tadstable.create(nil);
tblError.Databasename := DM1.tblInput.databasename;
tblError.TableName := '__error';
tblError.Open;
tblError.Append;
tblError.Fields[1].AsString := e.Message;
tblError.Post;
tblerror.Free;
end;
Version 2 AEPs (which you will most likely be writing unless you are maintaining compatibility with an older version of Advantage) cannot start new transactions, or rollback or commit transactions using the connection handle that they are passed. You can, however, declare a new connection inside of your AEP and use transactions on that connection if necessary.
Keep in mind that all operations performed inside your AEP using the connection passed in are done under the context of the existing transaction (if one exists). This means if the client rolls back the transaction all operations performed by the AEP will also be rolled back.
While a data dictionary is required to register an AEP, the tables that the AEP works on do NOT have to be bound to the data dictionary. An AEP can open and update tables that are not part of the data dictionary that the AEP belongs to.