Adding a Master Password to a Paradox Table

How do I add a password to a Paradox table using nothing but code?


It is said that necessity is the mother of invention. And I've found that saying to ring true time and time again. With respect to the subject at hand, I didn't need to know how to do this until I actually had to do it with a program that I needed to write.

But before I started out, I did the usual thing by asking myself some questions:

  • Is there a method in an existing component (for our purposes TTable) that can do this? Why reinvent the wheel?
  • If not, is there any resource available that might be able to do this? Again, why reinvent the wheel?

If the answer to both questions is "No," then I know I have to build the capability myself.

You might be thinking, why did I spend time with the discussion above? Well folks, we live in an object-oriented world; moreover, we live in a world where there are a lot of software developers. Someone, somewhere had to think the along the same lines. So as a rule of thumb, before I make an attempt to write a specialized function or component, I always do research to make sure it hasn't been created elsewhere. That said, let's move on, shall we?

First of all, let's talk about Paradox passwords. Paradox has a hierarchical password system. Each table can have a master password that defines any access to it. In addition a table can have several auxilliary passwords that define table and field rights, limiting access in very specialized ways.

In general, though, I've found that encrypting a table with just a master table is adequate because most of the programs I've created that require data encryption only require "all or nothing" security. Besides, having to cover the ins and outs of auxilliary passwords in Delphi would have created an artilce that was just too long. In any case, let me list the code below that will add a master password to a Paradox table, then we'll discuss particulars following it. Here goes...

procedure EncryptPDOXTable(TableName, 
                           Password : String);
var
  TblDesc: CRTblDesc;
  LocDB : TDatabase;
begin
  //Initialize the BDE
  Check(DBIInit(nil));

  //Initialize random number generator
  Randomize;
  //Create a local, non-owned database object that
  //points to the path associated with the table.
  LocDB := TDatabase.Create(nil);
  with LocDB do begin
    Params.Add('path=' + ExtractFilePath(TableName));
    DatabaseName := 'PDOXEncryptDB' + IntToStr(Random(50));
    DriverName := 'STANDARD';
    Connected := True;
  end;

  //Now, initialize the Table Descriptor with the values
  //required to set a master password.
  FillChar(TblDesc, SizeOf(CRTblDesc), 0);
  StrPCopy(TblDesc.szTblName, ExtractFileName(TableName));
  with TblDesc do begin
    bProtected := True;
    StrPCopy(TblDesc.szPassword, Password);
  end;

  //Now do the restructure.
  try
    Check(DbiDoRestructure(LocDB.Handle, 1, @TblDesc, 
          nil, nil, nil, False));
  finally
    LocDB.Free;
    DBIExit;
  end;
end;

I think you've figured out by now that to create a master password in a Paradox table, you have to use direct BDE calls. And while it may seem a bit complex, it's actually pretty easy. You'll notice that I put a couple of words in the code in boldface type. These are the two things that you really have to worry about as far as setting a password. The other stuff is pretty routine stuff. So looked at from that perspective simplifies the process entirely. Why don't we discuss the code in a bit more detail.

The first things that I do in the procedure is to initialize the BDE and create a TDatabase object. You'll find that almost all things that you do in the BDE require a database handle of some sort. DbiDoRestructure is no exception. With respect to initializing the BDE, that's purely an option, but something that I've done as a habit primarily because some of my programs don't make use of any data aware components, and thus won't initialize the BDE by default. If you make calls to the BDE without it having been initialized in some way, you'll get an initialization error. So it's a good idea to do this.

The next thing that happens in the code is that I initialize a table descriptor structure. This is a standard structure defined in the BDE that tells BDE functions that use it, like DbiDoRestructure, about the table that they're going to work with. CRTblDesc is a fairly complex structure that has substructures attached to it. If you want to know more about what kinds of fields are in this structure, I suggest looking in the BDE help file in the BDE directory on your hard disk. But the way cool thing about CRTblDesc is that you only need to fill in the fields that are pertinent to the operation you want to perform. In the case of adding a password to a table, all you need to fill in are the szTblName field and szPassword field. That's it.

Then, once the structure has been filled with proper values, it's a simple matter of calling DbiDoRestructure to restructure the table. We supply the handle to the database that was created at the top, the number of table descriptors we're using (1), the address to the table descriptor, then set the next three parameters to "nil" and the last parameter to False. Easy.

If you want to know more about using DbiDoRestructure (since it's obviously used to do many more table operations than what I just discussed) I encourage you to study the online help, or obtain a copy of the "Borland Database Engine Developer's Guide" which you can purchase directly from Borland. Cheers!