Autorefresh a Paradox table (networking)  

Send By: Radikal (Q3 Team)
Web :
Date: 09/04/00

Tip accessed 1051 times


A lot of people ask me about this topic: How to make that a table that is being used by several stations, autorefresh by itself when another stations has changed it...

Well, investigating a little on the topic, I have developed this trick, in which I use a BDE CallBack.

Before anything, to test the trick you need two applications that share the same Paradox database with their correctly configured BDEs, that is to say, the parameters NetFileDir, PrivateDir and Local Share correctly configured.
Therefore, in this trick I give for fact that you know how to configure the BDE correctly so that two applications share the same database Paradox with their register blockings.
If it is not this way, good, soon I will be able to some other trick of how to configure this correctly (Now I don't want to translate so much text to English...).

How works the tip

The operation is the following one:
We will configure a CallBack function in the BDE by means of DBIRegisterCallBack. There are several types of CallBack functions that we can define, in short, we will use one of the type: cbTABLECHANGED that will be called when the table has been changed by another station.
Register the CallBack is a easy operation, it's enough with a call to DBIRegisterCallBack.
The problems come when trying to make something inside the function CallBack. If for example we are happened to make a Table1.Refresh inside the CallBack... we will have, in the first place an exception of ' Reentrance error' (or something like that) and then a beautiful memory violation error...
Therefore, we will have to make something to fix this circumstance. There are two methods to make it, one is redireccionate the data segment, point it to our application (complicated technique that I don't believe that it is worthwhile to develop) and other, to use an artifice: to send a message to our application.
The trick therefore is this: To register a CallBack procedure, and in her, we send a message to our application to warn it that our table has changed. Then, in the procedure of detection of our personalized message, we make the Table.Refresh.
Ah, almost forgot me, a particularity of this type of CallBack is that it is only generated by the Paradox driver. Another particularity is that we should make regularly calls to the function DBICheckRefresh of the BDE, so that the CallBack is invoked.
The standard controls of Delphi, they invoke DBICheckRefresh in some moments, but if we want a pseudo-instantaneous refresh, we don't have left more remedy than to call it periodically.
Now it is when some will wonder: ¿y por qué no pongo un Timer y dentro del timer pongo un Table.Refresh ?. The difference is in that doesn't have the same load of CPU a DBICheckRefresh that to refresh the data of a table...

Now, I will go putting step to step what you will need to go incorporating in your example (calm that is little thing).

  • In the first place, we will define our personalized message, putting this after the first uses clausule of the form:

       WM_TablaCambiada = WM_USER + $666;

  • Add BDE in the uses of your form

  • Now, we need to declare the procedure that will treat this personalized message when the form receives it. We will make it putting in the private part of the form this declaration:

         { Private declarations }
         procedure CambioDetectado(var message: TMessage); message  WM_TablaCambiada;

    and in the implementation so... their implementation:

     procedure TForm1.CambioDetectado(var message: TMessage);

    Well, we already have the invention that when receiving the personalized message, it will refresh the chart.
    Now we go with the CallBack.
  • We declare the CallBack, function putting this before the implementation part of the form:

       Form1: TForm1;
       function TableChangedCallback(ecbType: CBType;
                                     iClientData: Longint;
                                     var CbInfo: Pointer): CBRType;
                                     export; StdCall;

    and evident, we put their implementation in the implementation part :

     function TableChangedCallback(ecbType: CBType;
                                   iClientData: Longint;
                                   var CbInfo: Pointer): CBRType;
       result := cbrCONTINUE;

    If you notice, the only thing that makes is to send to the application (whose handle is in the parameter iClientData) our personalized message so that refresh the table.

  • Well, we already have defined our CallBack, now only has left us a thing: to register it. We will make it in the OnCreate of the form:

     procedure TForm1.FormCreate(Sender: TObject);
       {Defino NetDir y Private dir para que funcione el ejemplo con mi BDE
       Esto depende de tu instalacion, así que puede que no lo necesites o
       bien que necesites otras configuraciones}
       With Session do
       Check(DbiRegisterCallback( Table1.Handle,

    Well, you must notice that in the third parameter (that corresponds to the parameter of free use for the user), I pass him Form1.Handle, so that the CallBack procedure knows to where it must send the personalized message...

    Lastly, have I mentioned that we should not make calls to DbiCheckRefresh?. We have two options, or do we put it in a Timer, or use the event Application.OnIdle.
    To my pleasure, I prefer the Timer, so much doesn't depend on what the user is making...
    Therefore, put a timer in the form, and put in their event OnTimer a call to DbiCheckRefresh:

     procedure TForm1.Timer1Timer(Sender: TObject);

    To finish, we have left a detail: unregister our CallBack. We will make it in the OnDestroy of the form:

     procedure TForm1.FormDestroy(Sender: TObject);
       Check(DbiRegisterCallback( Table1.Handle,cbTABLECHANGED,0,0,nil,nil));

    And... this is all for now...

  • You should have correctly configured the BDE to work in net.
  • This kind of CallBack only works with Paradox databases
  • This has carried out it and proven in Delphi 3 in Win98. It is possible that if you use Delphi 4 it gives you problems the type of iClientData parameter for the difference of types among the two versions. If it is this way, you will use tipecasting.