Introduction

If you are working on software which accesses a Paradox database, you are probably using BDE (Borland Database Engine). This article shows how to use Paradox database files and read some data from them directly. You can also use the primary index to find specific records you need. This could be useful if BDE doesn't cooperate with you or there is another reason why you cannot or don't want to use BDE.

Background

Every developer sometimes has to deal with some kind of black-box - piece of technology which is not opened, and can be accessed only from some external interface, but there is a problem - the black-box is quite mysterious to us - one time it works, another time it doesn't, and whatever you do, it seems to be having its own moods. And then I always wish it would be opened so I could debug it, trace into it, and look at its teeth. Are you also a developer who has Reflector between quick links? Then you will probably understand why I started to search for some .NET native Paradox database reader. I couldn't find any, but I have found some Paradox format specifications, so I decided to write one myself and make it public for others who could use it. Many thanks to Randy Beck and Kevin Mitchell for sharing the Paradox internal structure, and because their materials are online, I will focus my efforts to describing my own code.

Paradox splits data to files using database objects - every table has its own file, and every index too. Data files and indexes have very similar structure, so we can handle them both in one class - ParadoxFile. BLOB values are also stored in their own files, but I haven't implemented the structure.

ParadoxFile - base class working with common structures from Paradox data files and indexes

ParadoxFile.DataBlock - represents one block of data

ParadoxFile.FieldInfo - data type of the field

ParadoxFile.V4Hdr - structure which is present only in certain Paradox files/versions

ParadoxTable - represents table data file

ParadoxPrimaryKey - represents table index

ParadoxFileType - enum with all file types

ParadoxFieldType - enum with all data types

ParadoxRecord - represents a data record

ParadoxDataReader - standard IDataReader implementation

The index file is, in fact, simply a table with indexes of datablocks and related indexed values. You can read it directly, but I also created a mechanism which browses through the index tree for you and picks up the desired record. You will have nothing more to do but specify a condition which you would like to apply on your data.

ParadoxCondition - base class for conditions which can be used for searching in index data

Using the code

One way to go is just open a table and start enumerating. This is done by the following piece of code. We will create a ParadoxTable object and use the Enumerate method to traverse through records. Data is retrieved synchronously as needed, so if we stop reading after a few records, no redundant read operations are taken.

There are, of course, scenarios where this simple method will not suffice. Sometimes we also have to read some data in the middle of a large database, so we need to use an index to minimize disk operations. In the next sample, we will use a primary key index to find records with key values in the range between 1750 and 1760. At first, the index file has to be opened, then we will create a condition composed from two compare operations. We can browse data by calling the Enumerate method on the index. In this case, we will use the ParadoxDataReader class with the the IDatareader implementation so we don't need to rewrite a lot of code if we have used BDE before.

Comments and Discussions

I recently installed the Paradox reader and it worked almost all the time. I really appreciate the effort you put in to help us. But I am having trouble with some data, like a negative dollar amount, ($0.01). When I view the data from Borland Database Desktop program, the data type is '$' and the value shown as ($0.01). When I use the following code to read data,

Sometimes the read of a database-row get an error.The problem are in the DataValues-method (conversion an String/ Memos)if a String has only one character and end NOT with an Character-zero, you get an error.the getString-Methods search for an char(0).

If you start the debugger, the memories are now different.Therefore you get another error.

First and foremost thank you for your hard work. I did have a few issues when converting Paradox 5 to SQL server when I had negative Currency amounts.

In the ParadoxRecord.DataValues class I had to modify the FieldType.Currency so that it used the ConvertBytesNum method like the number datatype uses. I haven't fully tested the changes, but a sampling of the data indicates that the totals now add up.

Also the Time field to Timepsan when exported to as text to string (bulk file load) results ina number with 7 digits which SQL couldn't handle. So I formatted the Timespan to HH:mm:ss. I didn'tneed to preserve the milliseconds, but I think it can handle 3 digits for those who need it:

What a fantastic solution. Exactly what the .NET programmer needs to import Paradox databases into new applications without the headache and overhead of the antiquated BDE. Can't thank you enough. Beers on me if you ever get to the States!

Hy,i've this problem: sometimes the blocks inside a db file are not in order, in the sense that looking at the specifications every block has two indexes, previous and next, in order to build a linked list.There is also a list of free blocks.... if you open a db file and you move, delete and insert some records you'll see that the order you find reading each block is not equal to the order of the records.

I am attempting to read a database with a field type of "Graphic"The containing data is either a JPG or ZIP file in binary.I used the same code that is used for a Blob and have had some success getting a readable JPG file, but the data that I think is ZIP data, I have had no success at all.I beleive it is possibly zLib data.I have also used a tool called PxView and it extracts the JPG files OK, but the ZIP data is also unreadable.Any sugestions would be appreciated.

Ok, so I put break points at the reads when the records in question entered the errors. So when I put a break point and tried to view the DataValues property of the paraRead it was just an error but then if I waited a second or two then the DataValues property showed me all the results except the last column where the value was null. So could there be some race conditions?

Hi everyone,First of all, thank you Petr, the library is really helpful. I have spent hours on trying to access Paradox with no luck, your library is the first solution which really works I am using the sample code from CodeProject to instantiate a ParadoxTable linked to one of the tables in my database. Everything works, but some of the numeric fields return null while they actually do have a non-null value.Any ideas ?Thank you.