今更ながら BDE (Borland Database Engine)

 未だに使われる事もあります。正直な話、

 と、業務アプリプログラミングが本業の私が言ってみます。

 新規案件で BDE を使うのは流石にどうかと思うのですが、古いPCからのデータ移行で Paradox/xBASE を扱う場合には BDE を使うのが手っ取り早いのも事実です。業務で使われている MS-DOS マシンには xBASE のデータがゴロゴロしてたりしますね(Btrieveもありますけどね)。

 あ、xBASE というのは、dBASE またはその互換アプリケーション の事です。昔は "Quick Silver" なんていう dBASE 互換アプリケーションがあったんですよ。Microsoft も xBASE 製品を出してまして、"FoxPro/Visual FoxPro" ってのがあります。日本では発売されなかったようですけどね。他の xBASE 製品には "dBXL" とか "ARAGO for Windows" なんてのがあります。

BDE の概要

 BDE は、 

 のデータベースを特殊なドライバなしでアクセスできます。

 このうち、dBASE / Paradox / FoxPro は SQL 型データベースではありませんが、SQL-92 サブセットの "Borland Local SQL" を利用する事ができます (イメージ的には "BDE が SQL を翻訳してくれている" 感じです)。利用できる構文等、ローカル SQL の概要は "%ProgramFiles%\Common Files\Borland Shared\BDE" にある LOCALSQL.HLP を参照して下さい。  これらのデータベースは "SQL Link" と呼ばれるドライバを介してアクセスします。BDE に ODBC を接続して各種データベースにアクセスする事も可能です。

BDE の情報(公式)

 "CodeGear Delphi 2009 および C++Builder 2009 のリリース ノート" にも書いてありますが、Vista 環境下ではデータベースファイルをドライブルート直下("C:\"等)に配置してはいけません。

BDE / SQL Link の配布

 BDE / SQL Link は Borland 製品で作成されたアプリケーションと共に配布可能です。詳しい内容は BDE のインストールフォルダにある、"bdedeploy.txt" をお読み下さい。

 ここからが本トピックの主題です。問題は "どうやって配布するのか?" です。

BDE の単体配布

 これは明らかにライセンス違反です。ですが、この方法を知らない事にはインストーラ/インストールプログラムを作る事ができません。

 dBASE / Paradox / Access 等のネイティブドライバで動作するものを使うのなら上記だけでもいいのですが、SQL-Link ドライバが必要な場合には、別途 SQL-Link を配布しなくてはなりません。
  1. 開発環境の "%ProgramFiles%\Common Files\Borland Shared\BDE\" にある bdedeploy.txt を読み、必要な SQL-Link ドライバファイルを調べる。
  2. 調べた SQL-Link ドライバファイルをターゲット PC の "%ProgramFiles%\Common Files\Borland Shared\BDE\" へコピーする。
  3. 開発環境のレジストリ [HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Database Engine\Settings\DRIVERS] を調べ、目的の DB 用のレジストリを探す (Interbase なら "INTRBASE")。
    64bit 環境では、レジストリキーが [HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Borland\Database Engine\Settings\DRIVERS] にリダイレクトされる事があります。
  4. 調べたレジストリエントリをターゲット PC のレジストリに書き込む。
 多少面倒ですが解ってしまえば難しくはありません。

 ※ 64bit Windows で 32bit DLL を登録するための regsvr32 は %systemroot%\SysWoW64 にあります。%systemroot%\System32\にあるのは 64bit DLL 用です。

BDE をインストーラに含める

 各製品のマニュアルを参照して下さい(^^; 既に単体配布の方法が判っているのですから、どんなインストーラでも BDE を含める事は可能です。BDE を配布するなら、エリアスを作れる InstallShield が一番簡単です。他のインストーラではエリアスの作成が面倒です。

エリアスをアプリケーション内で設定する

 エリアスをアプリケーションから設定してしまうというテもあります。この方法なら、インストーラでエリアスを設定する手間が必要ありません。データベースが Paradox/dBASE/ASCII ならエリアスの作成は非常に簡単です。

uses
  ..., DBTables;

procedure AddStdAlias(Alias, Driver, Path: String);
begin
  if not Session.IsAlias(Alias) then
    begin
      Session.AddStandardAlias(Alias, Path, Driver);
      Session.SaveConfigFile;
    end;
end;

 この関数を利用して、

procedure TForm1.FormShow(Sender: TObject);
begin
  OnShow := nil;
  AddStdAlias('TEST_PARADOX''PARADOX', IncludeTarilingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'DATA');
end;

 こんな感じで使います。例では Paradox のエリアス "TEST_PARADOX" を作り、パスを "<EXEの存在するフォルダ>\DATA" に設定しています。AddStdAlias() の第2引数(Driver)に利用できる文字列は、

 のいずれかです。その他のドライバを使う場合はパラメータを指定しなくてはならないので少々面倒です。

uses
  ..., DBTables;

procedure AddSqlAlias(Alias, Driver: String; List: TStrings);
begin
  if not Session.IsAlias(Alias) then
    begin
      Session.AddAlias(Alias, Driver, List);
      Session.SaveConfigFile;
    end;
end;

 この関数を利用して、

procedure TForm1.FormShow(Sender: TObject);
const
  DriverName = 'INTRBASE';
var
  Params: TStringList;
begin
  OnShow := nil;
  Params := TStringList.Create;
  try
    Session.GetDriverParams(DriverName, Params);
    Params.Values['USER NAME'  ] := 'SYSDBA';
    Params.Values['SERVER NAME'] := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'DATA\data.fdb';
    AddSqlAlias('TEST_Interbase', DriverName, Params);
  finally
    Params.Free;
  end;
end;

 こんな感じで使います。例では Interbase のエリアス "TEST_Interbase" を作り、"SERVER NAME"パラメータ を "<EXEの存在するフォルダ>\DATA\data.fdb" に設定しています。第2引数(Driver)に利用できる文字列や、第3引数(List)に格納できるパラメータ及びパラメータの値は "BDE Administrator" で確認して下さい。

 エリアスが既に存在する場合にエリアスのパラメータを変更したい事もあるでしょう。以下にエリアスを簡単に操作できるクラスを用意してみました。

unit uBDE_CONFIG;

interface

uses
  Classes, BDE, DBTables;

type
  TBDEConfig = class(TObject)
  private
    FDriverName: string;
    FParams: TStringList;
    FAliasName: string;
    procedure SetAliasName(const Value: string);
  public
    // Constructor / Destructor
    constructor Create(aDriverName: String);
    destructor Destroy; override;
    // Methods
    property AliasName: string read FAliasName write SetAliasName;
    procedure Apply(Save: Boolean = True);
    procedure DeleteAlias(Save: Boolean = True); overload;
    class procedure DeleteAlias(aAliasName: string; Save: Boolean = True); overload;
    function IsAlias: Boolean; overload;
    class function IsAlias(aAliasName: string): Boolean; overload;
    // Properties
    property DriverName: string read FDriverName;
    property Params: TStringList read FParams write FParams;
  end;

implementation

{ TBDEConfig }

constructor TBDEConfig.Create(aDriverName: String);
begin
  FParams := TStringList.Create;
  FDriverName := aDriverName;
  if FDriverName = '' then
    FDriverName := szCFGDBSTANDARD;
end;

destructor TBDEConfig.Destroy;
begin
  FParams.Free;
  inherited;
end;

procedure TBDEConfig.Apply(Save: Boolean);
begin
  if Session.IsAlias(FAliasName) then
    Session.ModifyAlias(FAliasName, FParams)
  else
    Session.AddAlias(FAliasName, FDriverName, FParams);
  if Save then
    Session.SaveConfigFile;
end;

procedure TBDEConfig.DeleteAlias(Save: Boolean);
begin
  Self.DeleteAlias(FAliasName, Save);
end;

class procedure TBDEConfig.DeleteAlias(aAliasName: string; Save: Boolean);
begin
  Session.DeleteAlias(aAliasName);
  if Save then
    Session.SaveConfigFile;
end;

function TBDEConfig.IsAlias: Boolean;
begin
  result := Self.IsAlias(FAliasName);
end;

class function TBDEConfig.IsAlias(aAliasName: string): Boolean;
begin
  result := Session.IsAlias(aAliasName);
end;

procedure TBDEConfig.SetAliasName(const Value: string);
begin
  FAliasName := Value;
  if Session.IsAlias(FAliasName) then
    Session.GetDriverParams(FDriverName, FParams)
  else
    begin
      FParams.Clear;
      if FDriverName = szCFGDBSTANDARD then
        begin
          FParams.Values[szCFGDBDEFAULTDRIVER] := szPARADOX;
          FParams.Values[szCFGDBENABLEBCD    ] := szCFGFALSE;
        end;
    end;
end;

end.

dBASE のエリアスを設定するコードは以下のようになります。

uses
  ..., BDE, uBDE_CONFIG;

var
  BDEConfig: TBDEConfig;
  DatabsePath: string;
begin
  BDEConfig := TBDEConfig.Create('');                // Use STANDARD Driver
  try
    BDEConfig.AliasName := 'STANDARD_TEST';
    DatabsePath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'DATA';
    with BDEConfig.Params do
      begin
//      Values['DEFALT DRIVER'] := 'DBASE';         // DEFALT DRIVER = DBASE
//      Values['PATH'         ] := DatabsePath;     // PATH = APPPath
        Values[szCFGDEFDRV] := szDBASE;             // DEFALT DRIVER = DBASE
        Values[szCFGDBPATH] := DatabsePath;         // PATH = APPPath
      end;
    BDEConfig.Apply;                                // Create or Modify Alias
  finally
    BDEConfig.Free;
  end;
end;

エリアスが存在しない場合にはエリアスを新規作成し、存在する場合には指定したパラメータを更新します。

以下は、同様に Interbase のエリアスを設定するコードのサンプルです。

uses
  ..., BDE, uBDE_CONFIG;

var
  BDEConfig: TBDEConfig;
  ServerPath, DatabasePath, DatabaseName: string;
begin
  BDEConfig := TBDEConfig.Create('INTRBASE');       // Use INTERBASE Driver
  try
    BDEConfig.AliasName := 'INTERBASE_TEST';
    ServerPath   := '127.0.0.1';
    DatabasePath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'DATA';
    DatabaseName := 'DATA.IDB';
    with BDEConfig.Params do
      begin
//      Values['USER NAME'  ] := 'SYSDBA';
//      Values['SERVER NAME'] := Format('%s:%s\%s', [ServerPath, DatabasePath, DatabaseName]);
        Values[szUSERNAME  ] := 'SYSDBA';
        Values[szSERVERNAME] := Format('%s:%s\%s', [ServerPath, DatabasePath, DatabaseName]);
      end;
    BDEConfig.Apply;                                // Create or Modify Alias
  finally
    BDEConfig.Free;
  end;
end;

トラブルシューティング

おまけ

 Btrieve の話をしてしまったので、補足。

 Btrieve は、現在 "Pervasive.SQL / Pervasive PSQL / PSQL SUMMIT" と名を変えています。純粋な Btrieve とはもはや別物の感がありますが、"Pervasive.SQL / Pervasive PSQL / PSQL SUMMIT" では Btrieve を扱うことができまして、Delphi からは ActiveX 経由で "Pervasive.SQL / Pervasive PSQL / PSQL SUMMIT" を操作できます。詳しくは、"Pervasive.SQL を Delphi で使用 (PERVASIVE)" を参照して下さい。


 BACK