Loosing Paradox data without CachedUpdates

Hi all,

my situation can be reproduced with a little example:

An app with 1 Paradox table (CachedUpdate=False) I have a button to append/post 1 record each click.
If Windows or power goes off with the app opened, I loose all the records appended since the last application close.
It doesn't seem to happen with Interbase or others Databases I work with.

Am I doing smth wrong ? If not, is there any BDE API call to force a disk write ?
I cannot tell my customers that the technical solution is a no-break ;(

Who is Participating?

Improve company productivity with a Business Account.Sign Up

ronit051397Connect With a Mentor Commented:
The problem with Paradox is that it does some internal caching for the indexfiles. This does that the data is not written to disk immediatly. You can use the FlushBuffers call to write the buffers to disk.
From Lloyd's Help File:
Q:  How do I flush the data buffer to the table?  (i.e. hard write to disk)

A:  This will allow you to commit changes to disk without incurring the performance hit of LocalShare.


{This code came from Lloyd's help file!}
The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

I'm not quite sure if that will work with Paradox tables. I had some trouble with them myself, and the only solution I found was the FlushBuffers
Perheps you are right, I didn't check the code.
itamarAuthor Commented:
Hi Ronit & BlackMan,

I'll try both solutions tomorrow.
For FlushBuffers I guess that the correct code would be smth like
dbiFlushBuffers(Table1.DBHandle); am I right ? Well, I'll try.

Thanks a lot !

Hahaha your both right...from dbtables.pas

procedure TBDEDataSet.FlushBuffers;

itamarAuthor Commented:
Hi all,

thanks for the comments/answers ! If I could, I'd share the points among you three. All the comments were usefull.

I'll wait a bit more for some interesting comments and choose one to grade.

To BlackMan: Don't be upset. I'm just rejecting for now to make the question more visible to others experts. After 1 or 2 days I'll ask for an answer submission.

itamarAuthor Commented:

In Delphi 3 help file, FlushBuffers seems to be very obvious. The problem is that I'm using Delphi 2 (OK, I know I didn't say it....) So dbiSaveChanges seems to be the better solution.
This is from the NewsGroups, there is also the option to set in the BDE:

I am quite new to Delphi database development and I use Flushbuffers
in every AfterPost of my tables to write the cach. Is it good or not ?
What's the difference between LocalShare = true and the use of

>If you are using local tables and you have Local share set to true
>Post will write out the records to disk
>>So if there are a power cut directly after, FAT's etc
>>have been updated so there will be no corrupted files??
>Paradox tables seem to hold up pretty well if the power is cut after a
>post but dbase tables seem to loose records.

>I was told to set LOCAL SHARE to TRUE to fix the following problem. This
>setting only fixes the problem when the program is halted unintentionally.
>But if the the computer halts (e.g. power failure), all changes made to
>database during the session are lost.
if you are loosing records after a power outage then you have some
kind of write caching in effect that BDE has no control over.

Well known bug. dbiSaveChanges works sometimes, using idle time works
sometimes, FlushBuffers works sometimes. Only thing that works all the time is
closing the TDatabase (you should use at least one for each app). That forces
the write to disk.

As far as I know, the BDE will always force writes to disk if you call
DbiSaveChanges or have Local Share set to True. I have never experienced
anything that makes me doubt that. Note that Windows or whatever operating
system you are using may still be caching writes. If you want to ensure
that changes are written to disk turn Windows write caching off.
Sorry to muscle in on the action, but here is a cure all solution for this type thing. Brace yourselves...

1. add dbiProcs to your uses statement

2. Create the following procedure

procedure DoIdle(Sender:TObject; var Done: Boolean);
  Done := true;

3. Add to the OnCreate event of your MAIN form only

  Application.OnIdle := DoIdle;

This will use any idle time to carry out all database maintenance, including writing to disk. It is very reliable and has solved this problem for me. Much more favourable than adding the other solutions to each OnPost event, neh ?
itamarAuthor Commented:
Hi Marcius,

interesting approach, but could you elaborate more about "dbiUseIdleTime", what does it do ?

Another thing: I have a TBase class that comes (inherited)  from TTable, so I can't have a solution based on TApplication events. Any ideas ?

About LOCALSHARE: What is the syntax for stting it to true ?
Many thanks !

itamarAuthor Commented:
Hi all,

Where can I find BDE API documentation ?

Why can't you use TApplication based event ? I don't understand why using a class which inherits TTable would stop that from working.

dbiUseIdleTime is a little bit of a mystery to me, I know that it works ! The dbiProcs unit is very poorly documented, so I cannot give you much more information. You can rest assured that it will save your cached Paradox data to disk and you will not suffer from disappearing records anymore.
Marcius & Itamar...
Taken from BDE help

C syntax

DBIResult DBIFN DbiUseIdleTime (VOID);

Delphi syntax

function DbiUseIdleTime: DBIResult stdcall;


This function is no longer supported. Use DbiSaveChanges instead.

So you should use DbiSaveChanges(Table1.handle) instead...
(from BDE Help)

DbiSaveChanges forces all updated records associated with hCursor to disk.

Rick Peterson
Hey, what can I tell you, it still works !!
But it may not in future upgrades of the BDE...

what happens if they remove the feature in the next upgrade of the bde...

One of your customer upgrades...because he has to have the latest and greatest...whamo...unknowning to you or him...disaster is waiting just around the corner, because Borland stops supporting the api call.

That is true, but a little dramatic ;) I still think that dbiUseIdleTime is a good solution, and since everyone is falling over themselves to support backward compatibility, I wouldn't worry.
Your probably right as Delphi 3 is concerned...but Delphi 4 is a whole new ball game...(if you use it you may want to put a Great BIG comment warning that it may be gone in Delphi 4)

Okay, you are correct. But since I don't use Paradox any longer, this doesn't effect me too much. I am now using Oracle, which causes me an entirely new set of headaches :)
The BDE Help file is located on:
C:\Program Files\Borland\Common Files\BDE\Bde32.hlp
Do you need a code or just explenation how to set localshare?
to set LOCALSHARE manually enter the BDE/Configuration\System\INIT.
to set it by code see the Borland example below:

Set a particular value in the IDAPI.CFG configuration file (32-Bit Only; All Versions).

NOTE: You must use this procedure version if you are using BDE v4.50 and earlier

This exmaple uses the following inupt: SetConfigParameter2(LOCALSHARE, 'TRUE')
NOTE: Param (in this case LOCALSHARE) must be a string that contains the path to the
node and the node item separated by a semi-colon. At the bottom of this page are some
of the more popular paths and items that are declared as constants for use with all
these examples.

           procedure SetConfigParameter2(Param: string; Value: string);
             hCfg: hDBICfg;
             Config: SYSConfig;
             Path, Option: string;
             ParamCount, I: word;
             pFields, pFld: pFLDDesc;
             pRecBuf, pRec: pBYTE;
             Found, SelfInitialized: boolean;
             rslt: DBIResult;

             {$Ifdef WIN32}
             hCfg := nil; pFld := nil; pRec := nil; Found := False; SelfInitialized := False;
               if Pos(';', Param) = 0 then
                 raise EDatabaseError.Create('Invalid parameter passed to function.  There must ' +
                    'be a semi-colon delimited sting passed');
               Path := Copy(Param, 0, Pos(';', Param) - 1);
               Option := Copy(Param, Pos(';', Param) + 1, Length(Param) - Pos(';', Param));

               rslt := DbiGetSysConfig(Config);
               if rslt <> DBIERR_NONE then
                 if rslt = DBIERR_NOTINITIALIZED  then // Engine not initialized error...
                   SelfInitialized := True;
               (* DbiOpenConfigFile is defined as such:
                   function DbiOpenConfigFile (            { Open/Create configuration }
                         pszDirPath    : PChar;            { Directory }
                         bCreate       : Bool;             { TRUE to create/overwrite }
                   var   hCfg          : hDBICfg           { Handle to config }
                                      ): DBIResult stdcall; *)
               Check(DbiOpenConfigFile(Config.szIniFile, FALSE, hCfg));

               (* DbiCfgGetRecord is defined as such:
                   function DbiCfgGetRecord (              { Get a record }
                         hCfg          : hDBICfg;          { Config Handle/NULL }
                         pszCfgPath    : PChar;            { Path }
                   var   iFields       : Word;             { Returned nbr of fields }
                         pfldDesc      : pFLDDesc;         { Field descriptors }
                         pRec          : Pointer           { Field values }
                                      ): DBIResult stdcall; *)
               { Call it without the field and record buffer to get the count... }
               Check(DbiCfgGetRecord(hCfg, PChar(Path), ParamCount, nil, nil));

               pFields := AllocMem(ParamCount * sizeof(FLDDesc));
               pFld := pFields;
               pRecBuf := AllocMem(10000);
               pRec := pRecBuf;

               { Get the node values... }
               Check(DbiCfgGetRecord(hCfg, PChar(Path), ParamCount, pFields, pRecBuf));

               for I := 0 to ParamCount - 1 do
                 if pFields^.szName = Option then
                   StrPCopy(PChar(pRecBuf), Value);

                   (* DbiCfgModifyRecord is defines as such:
                        function DbiCfgModifyRecord (           { Modify a record }
                              hCfg          : hDBICfg;          { Config Handle/NULL }
                              pszCfgPath    : PChar;            { Path }
                              iFields       : Word;             { Nbr of fields }
                              pfldDesc      : pFLDDesc;         { Field descriptors }
                              pRec          : Pointer           { Data values }
                           ): DBIResult stdcall; *)
                   Check(DbiCfgModifyRecord(hCfg,  PChar(Path), ParamCount, pFld, pRec));

                   Found := True;
                 Inc(pRecBuf, 128);
               if Found = False then
                 raise EDatabaseError.Create(Param + ' entry was not found in configuration file');

               if pFld <> nil then
               if pRec <> nil then
               if hCfg <> nil then

               (* DbiCloseConfigFile is defined as such:
                      function DbiCloseConfigFile (           { Close the config file }
                      var   hCfg          : hDBICfg;          { Handle }
                            bSave         : Bool;             { To save the changes }
                            bDefault      : Bool;             { To make this file the default }
                            bSaveAs16     : Bool              { To save as a 16-bit config file }
                         ): DBIResult stdcall; *)
                 { Close and save the config file... }
               Check(DbiCloseConfigFile(hCfg, TRUE, TRUE, FALSE));
               if SelfInitialized = True then
                raise EDatabaseError.Create('Non supported function in 16 bit');
itamarAuthor Commented:
Hi all,

Oh, This thread is getting BIG !

I'm done with it. All the solutions are great and have advantages. My particular approach leads me to use ronit's first comment. That's the one I put in my component.

Ronit please, post your comment as an answer so I can grade it.

To Marcius: the problem concerning the TApplication event is that I have to put the code in my component. The programmer wil use this component without writting anything else in his application, of course it wouldn't stop it from working, but it must be transparent.

Many thanks to everybody
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.