Download Code Samples (zip file)
Installing and maintaining Delphi applications that use the BDE in a network environment is too costly if you install the BDE and the application on every PC. It is easy to solve half the problem by putting your applications EXE on the file server so that all users share a single copy. When you need to install an update you only have to do it once and the new version is immediately available to everyone. But what about the BDE?
Borland recommends that the BDE be installed on each PC to improve performance. However, the only performance gain is that the BDE DLLs can probably be read into memory faster from a local hard drive than from the file server. Since the load time of the DLLs is generally a minor component of overall application performance, unless the network is very slow, little is gained by installing the BDE on each PC.
The alternative is to install the BDE on the file server. Installing a single copy of the BDE on the file server offers several major benefits. First, you only have to install the BDE one time in one location. Second, when you need to upgrade to a new version of the BDE you only need to install the update one time in one location. Third, you can be certain that all users are running the same version of the BDE at all times. Fourth, having all users share a single copy of the BDE configuration file guarantees that all users are using the same BDE settings. Sixth, if you need to add an alias or change any other BDE configuration setting you only need to make the change once and it immediately effects all users. Finally, a centralized BDE installation makes it easy to have your BDE applications configure the PC they run on to use the BDE the first time they run.
Using the technique described in this article you can take a new PC out of the box, connect it to the network and the only thing you have to do to run a BDE application is create a shortcut to the EXE. There are other benefits as well. You can have different applications use different BDE configuration files or even different versions of the BDE. The interesting thing is that there are no tricks involved. The BDE was designed from the beginning to allow a single central copy to be shared by multiple users.
There are two phases to the process of getting applications to automatically configure and use a central copy of the BDE. The first is getting the BDE installed on the file server and the second is adding code to your program so it will automatically create the BDE registry entries when it starts.
Before you can use a file server BDE installation you have to get the BDE installed on the server. Neither the version of InstallShield Express that comes with Delphi or the commercial version of InstallShield Express allow you to install the BDE anywhere but the users local hard drive. One solution is to install the BDE on one workstation then copy the BDE directory to the file server. The disadvantage of this technique is that it leaves the BDE registry entries on the workstation pointing to the BDE on the local hard drive. This can be easily overcome by letting your application reset the BDE registry entries when it runs. While you can change the registry entries to point to the server, you can also avoid the problem by creating a set of installation diskettes that install the BDE files as though they were an application. This lets you put the BDE where you want it and does not create the undesired registry entries. If your development machine is connected to the network you are deploying on, you can also copy the BDE directory from your machine to the file server. If you use one of the Wise installation programs you have no problem since Wise lets you install the BDE in any directory you wish.
Once the BDE is installed on the file server you need to set the BDE configuration options in the BDE configuration file on the file server. Choose Object | Open Configuration to open the configuration file in the network BDE folder. Next choose Object | Options to display the dialog shown in figure 1.

Figure 1 The BDE Administrator Options dialog
Make sure that the Windows 3.1 and Windows 95/NT radio button in the Save For Use With group box is selected. If you do not set this option some of the configuration parameters will be stored locally in the Windows registry instead of in the configuration file. If settings are stored in the registry they must be changed on every workstation. However, choosing the Windows 3.1 compatibility option causes all settings to be stored in the configuration file and causes the configuration file settings to be used even if conflicting settings exist in the registry. You can also create aliases or change any other settings at this time.
The next step is to get your application to automatically create the BDE registry entries each time it runs. This system must meet the following requirements.
This requires two INI files. The first has the same name as your applications EXE but with the extension .INI and is located in the same directory as the EXE. This file can contain any of the following entries in its [BDE] section.
[BDE] ConfigPath=f:\foo\bde\idapi.cfg BDEPath=f:\foo\bde Overwrite=True IniPath=f:\foo\bde.ini
The ConfigPath entry contains the full path to and name of the BDE configuration file. The BDEPath entry supplies the path to the BDE directory. By default the code described later in this article will only create the BDE registry entries if they do not exist. This prevents your program from changing existing entries. However, what if you need to move the BDE to a new directory when a new file server is installed or for some other reason? The Overwrite entry solves this problem by telling your program to create the BDE registry entries using the information in the INI file even if they already exist. Note that by setting Overwrite to true you could have several versions of the BDE installed on your server and have different applications use different versions. You could also have all applications share the same BDE but have different applications use different BDE configurations files. The only disadvantage to this system is that if you have 20 different Delphi applications and you need to make a change you will have to change 20 INI files. The IniPath entry addresses this problem. If the IniPath entry is present all other entries in the BDE section of the application INI file are ignored and the BDE settings are read from the INI file in the IniPath entry. This allows you to have a single INI file that controls the BDE settings for many applications. This shared INI file also contains a [BDE] section and can contain all of the values shown above except IniPath.
Figure 2 shows the code for the TApplicationIniFile component that automatically configures the BDE each time your application runs. Although this is a component and can be installed on the component palette you will probably not want to do so. Instead, copy the unit file into your project directory and add it to your project. Since it is likely that you will have other information unique to each application to store in the applications INI file, adding this unit to each project lets you easily customize the component to handle application specific settings.
unit AppIniFl;
{
The application INI file must be located in the same directory as the
EXE file. However, the IniPath entry in the [IniFile] section can be
used to redirect the application to another ini file in a different
location.
Application INI file structure.
[BDE]
ConfigPath=f:\foo\bde\idapi.cfg //Path and name of BDE config file.
BDEPath=f:\foo\bde //BDE directory.
Overwrite=True //Existing registry entries will be
//replaced.
IniPath=f:\foo\bde.ini //Use this ini file for BDE settins only.
//All other BDE entries in this fill will
//be ignored.
[IniFile]
IniPath=f:\foo //Use this INI file. All other entries
//will be ignored.
To create the BDE registry entries add the following code to the
main form unit's initialization section.
initialization
AppIniFile := TdgAppIniFile.Create(Application);
try
AppIniFile.CreateBDEKeys;
finally
AppIniFile.Free;
end; //try
}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, IniFiles, Registry;
type
TApplicationIniFile = class(TComponent)
private
{ Private declarations }
AppIni: TIniFile;
BDEIni: TIniFile;
AppIniPath: String;
function IsBdeInstalled(var Reg: TRegistry): Boolean;
procedure OpenAppIniFile;
procedure OpenBDEIniFile;
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure CreateBDEKeys;
published
{ Published declarations }
end;
procedure Register;
implementation
const
cRemoteIniPathTag = 'IniPath';
cRemoteIniSection = 'RemoteIniFile';
cBDESection = 'BDE';
cOverwriteBDETag = 'Overwrite';
cIniConfigPathTag = 'ConfigPath';
cIniBDEPathTag = 'BDEPath';
cDatabaseEngineKey = '\Software\Borland\Database Engine';
cLanguageDriverKey = '\Software\Borland\BLW32';
cLanguageDriverTag = 'BLAPIPATH';
cConfigPathTag = 'configfile01';
cDllPathTag = 'dllpath';
constructor TdgAppIniFile.Create(AOwner: TComponent);
begin
inherited;
OpenAppIniFile;
end;
destructor TdgAppIniFile.Destroy;
begin
AppIni.Free;
inherited;
end;
procedure TdgAppIniFile.OpenAppIniFile;
begin
AppIniPath := ChangeFileExt(Application.ExeName, '') + '.ini';
AppIni := TIniFile.Create(AppIniPath);
{If there is a remote ini file path in the ini file open
the remote ini file.}
AppIniPath := AppIni.ReadString(cRemoteIniSection,
cRemoteIniPathTag, '');
if AppIniPath <> '' then
begin
AppIni.Free;
AppIni := TIniFile.Create(AppIniPath);
end; //if
end;
procedure TdgAppIniFile.OpenBDEIniFile;
var
BDEIniPath: String;
begin
{If there is a remote ini file path in the BDE section
open that file else set BDEIni to point to AppIni.}
BDEIniPath := AppIni.ReadString(cBDESection,
cRemoteIniPathTag, '');
if BDEIniPath <> '' then
BDEIni := TIniFile.Create(BDEIniPath)
else
BDEIni := AppIni;
end;
function TdgAppIniFile.IsBdeInstalled(var Reg: TRegistry): Boolean;
begin
Result := True;
{See if the Database Engine keys exist.}
if Reg.ReadString(cConfigPathTag) = '' then
Result := False
else if Reg.ReadString(cDllPathTag) = '' then
Result := False;
{See if the language driver keys exist.}
Reg.OpenKey(cLanguageDriverKey, True);
if Result then
if Reg.ReadString(cLanguageDriverTag) = '' then Result := False;
end;
procedure TdgAppIniFile.CreateBDEKeys;
{Creates the BDE Registry entries using values found in an ini file
with the same name as the application's EXE file and located in
the same directory. The ini file can contain the following sections
and entries.
[BDE]
ConfigPath=f:\foo\bde\idapi.cfg
BDEPath=f:\foo\bde
Overwrite=True
IniPath=f:\foo
[IniFile]
IniPath=f:\foo
If the Overwrite=True entry is present any existing BDE entries will
be overwritten with the entries from the ini file. If the IniPath
entry is present the BDE entries will be read from the file at that
location with the same name as the EXE and the extension .INI. This
allows a central ini file to be used even if the app is installed on
each workstation.
}
const
cIniTrue = 'TRUE';
cBllCount = 7;
var
Reg: TRegistry;
LanguageKeys: TStringList;
ConfigPath: String;
BDEPath: String;
OverwriteBDEStr: String;
S: String;
BLL: array[1..cBllCount] of String;
OverwriteBDE: Boolean;
I: Integer;
begin
BLL[1] := 'USA.BLL';
BLL[2] := 'EUROPE.BLL';
BLL[3] := 'OTHER.BLL';
BLL[4] := 'CHARSET.BLL';
BLL[5] := 'CEEUROPE.BLL';
BLL[6] := 'FAREAST.BLL';
BLL[7] := 'JAPAN.BLL';
{Open the Ini file that contains the BDE information.}
OpenBDEIniFile;
{Read the BDE DLL and Config file paths from the ini file.}
ConfigPath := BDEIni.ReadString(cBDESection, cIniConfigPathTag, '');
BDEPath := BDEIni.ReadString(cBDESection, cIniBDEPathTag, '');
if Copy(BDEPath, Length(BDEPath), 1) = '\' then
BDEPath := Copy(BDEPath, 1, Length(BDEPath) - 1);
{Determine if the BDE section contains and Overwrite parameter.
If so and if the value is True the BDE registry settings in the
ini file will overwrite any existing settings on the user's
computer.}
OverwriteBDEStr := BDEIni.ReadString(cBDESection, cOverwriteBDETag, '');
if UpperCase(OverwriteBDEStr) = cIniTrue then OverwriteBDE := True;
Reg := TRegistry.Create;
Reg.LazyWrite := False;
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
Reg.OpenKey(cDatabaseEngineKey, True);
{If the BDE is not installed or if the ini file's BDE section
contains the Overwrite=True entry create the BDE keys.}
if (not IsBdeInstalled(Reg)) or (OverwriteBDE) then
begin
{Write the BLW32 language driver subkeys.}
if BDEPath <> '' then
begin
LanguageKeys := TStringList.Create;
{If there are no language driver keys add the
BLAPIPATH key to the list so it will be created.}
try
Reg.GetValueNames(LanguageKeys);
if LanguageKeys.Count = 0 then
begin
{The language driver keys do not exist so create them.}
{Write the BLAPIPATH entry first.}
Reg.WriteString(cLanguageDriverTag, BDEPath);
{Write the language driver keys next.}
for I := Low(BLL) to High(BLL) do
if BLL[I] <> '' then
Reg.WriteString('LOCALE_LIB' + IntToStr(I),
BDEPath + '\' + BLL[I]);
end else begin
{The language driver keys already exist so just
update the path to the BDE directory in each entry.}
for I := 0 to Pred(LanguageKeys.Count) do
begin
{If this entry is the BLAPIPATH entry just write the
BDEPath. If it is the entry for one of the language
driver files extract the file name from the existing
entry and add it to the end of the BDE path.}
if UpperCase(LanguageKeys[I]) = cLanguageDriverTag then
begin
S := BDEPath;
end else begin
S := Reg.ReadString(LanguageKeys[I]);
S := BDEPath + '\' + ExtractFileName(S);
end; //if
Reg.WriteString(LanguageKeys[I], S);
end; //for
end; //if LanguageKey.Count
finally
LanguageKeys.Free;
end; //try
{Write the DatabaseEngine subkeys.}
Reg.OpenKey(cDatabaseEngineKey, True);
if ConfigPath <> '' then
Reg.WriteString(cConfigPathTag, ConfigPath);
Reg.WriteString(cDllPathTag, BDEPath);
end; //if BDEPath <> ''
end; //if
finally
Reg.Free;
end; //try
end;
procedure Register;
begin
RegisterComponents('DGI', [TdgAppIniFile]);
end;
end.
Figure 2 The TApplicationIniFile component
The overridden constructor of TApplicationIniFile calls its OpenAppIniFile method. OpenAppIniFile gets the path to the applications INI file by using the Application.ExeName property and changing the file extension to INI. It then checks for an IniPath entry in the [RemoteIniFile] section. If it finds this entry, it opens the file it points to. This allows you to have a family of related applications share a common INI file.
The heart of TApplicationIniFile is the CreateBDEKeys method. This method begins by setting the Boolean variable BDEInstalled to True and calling OpenBDEIniFile, a method that is almost identical to OpenAppIniFile except that it looks for the IniPath entry in the [BDE] section of the application INI file. Next the method retrieves and saves the value of the Overwrite entry. The registry is accessed and updated using an instance of TRegistry. After the TRegistry object is created its LazyWrite property is set to False to ensure that changes to the registry are saved immediately.
The next step is to determine if the BDE is already installed on this machine. The BDE is properly installed if the ConfigFile01 and DLLPath entries exist under KEY_LOCAL_MACHINE\Software\Borland\Database Engine and the BLAPIPath entry exists under KEY_LOCAL_MACHINE\Software\BLW32. If any of these keys are missing the BDEInstalled variable is set to False. First Reg.OpenKey is called and passed Software\Borland\Database Engine key. Next Reg.ReadString is used to read the ConfigFile01 and DLLPath keys. OpenKey is called a second time to move to the Software\BLW32 key and ReadString is used to read the BLAPIPath value.
The final step is to create the BDE registry entries if the BDE is not installed or if the INI file contains the Overwrite=True entry. If you have ever looked at the Software\BLW32 key using RegEdit you have seen that in addition to the BLAPIPath value there are entries for each of the BDE Language drivers. You do not have to create these keys when you install the BDE, however, if they are present you need to change the path in each entry. The code creates a TStringList object named LanguageKeys and loads all of the value names under the Software\BLW32 key into the StringList by calling the TRegistry objects GetValueNames method. Next it searches the StringList by calling its IndexOf method to determine if the BLAPIPath entry is present. If not, it is added to the list. Finally, a for loop iterates through the StringList and changes the path for each key to the BDE path that was read from the INI file earlier. Another call to Reg.OpenKey opens the Software\Borland\Database Engine key. Two calls to Reg.WriteString create the ConfigFile01 and DLLPath entries.
For CreateBDEKeys to work you must call it before the programs default BDE session is created. Therefore, the only place you can put this call is in the initialization section of one of your programs unit files. The most natural location is the initialization section of the main forms unit. Figure 3 shows an example.
initialization
AppIni := TApplicationIniFile.Create(Application);
try
AppIni.CreateBDEKeys;
finally
AppIni.Free;
end;
end.
Figure 3 Calling CreateBDEKeys
Conclusion
The advantages of installing the BDE on the file server and sharing it across all users are so great it is surprising that Borland does not recommend, or at least document, this option. It reduces the cost of installing the BDE and every application that uses the BDE initially and the cost of installing BDE updates in the future. Having your applications automatically create the BDE registry entries offers similar benefits. You will never again get a call from a user who just got a new PC and none of the BDE applications run even though the support techs copied all of the files from the old hard drive. With self configuring apps and a central BDE installation anyone who can create a shortcut can install a BDE application on a new workstation.
The only trick to making this system work is to make sure that the Win 3.1 and Win 95/NT compatibility option is set in the BDE Administrator and that is unfortunate. The Win 95/NT only option offers no benefits and, in my opinion, should be removed so that all BDE configuration information is always stored in the BDE configuration file. This would ensure that any BDE configuration file could be safely shared.
Copyright 1998
The Database Group, Inc.
All rights reserved.