Technical Information Database
TI265B.txt Sharing Violation Error with Paradox Tables
Category :General Programming
Platform :All
Product :BDE All
Description:
This error is most commonly caused by a "Lock File
Contention". A lock file contention occurs in this
situation:
One user, User "A", is accessing one or more Paradox
tables in a directory. "A" is closing the last table
in the directory. The BDE detects that "A" is the last
program accessing the tables in the directory. Since
"A" is the last user on the tables, the .LCK files are
going to be deleted. At this time, User "B" attempts
to open a table in the same directory as "A" (who is
about to delete the .LCK files). "B" opens the .LCK
files to write an entry to. This is when "A" tries to
delete the .LCK files that "B" has now opened. This
will cause a sharing violation on the .LCK files. The
share violation usually occurs on the last client to
close the table.
Solutions:
One solution is to override Windows error routines
and ignore the error. In all test cases, then a
sharing violation occurs, the table is still opened or
closed.
Another solution is to keep the .LCK files in the
table's directories. If the BDE detects that more
than one Application (or Session) is using the tables,
it will not attempt to delete the .LCK files therefore
solving the problem.
If the BDE and your Paradox table all reside on the
same CPU another solution is turn off Local share in BDE
Adminstrator. If Local Share is off .LCK files will not
be created in the first place, so a sharing violation
should not occur. Note that if tables are on a different
CPU than the BDE setting Local Share has no significance
because .LCK files are always created on remote drives.
1) C / C++ / Delphi / Paradox:
Use the Windows API SetErrorMode passing the
SEM_FAILCRITICALERRORS constant. You would only need
to do this before opening and closing a table and
then check to see if the table has actually been open.
Please see Routine 1a and 1b below for a basic Delphi
(any version) routine or Routine 2a and 2b for
C / C++ routines. Paradox users will need to call
into the appropriate DLL where SetErrorMode resides.
2) C / C++ / Delphi / Paradox:
Create a "Dummy" Paradox table in each table
directory that the application uses. Example:
executable is in directory C:MyProg. This executable
opens tables in two different directories - C:MyTables
and D:TempTbl. Create a "Dummy" table with the
Database Desktop, or any other Paradox table creation
utility, in both the C:MyTables and D:TempTbl
directories. Once the tables are created, create an
application that opens each of the "Dummy" programs at
startup. Leave this program running at all times. If
the executable can be placed on a server machine, like
Windows NT, that is optimal. In this scenario,
whenever applications are accessing the data, the
server also has the "Dummy" tables open.
3) C / C++ / Delphi / Paradox:
Leave at least one table during the entire
application's run. Open one table during startup and
close the table at exit. You could even open a
"Dummy" table described above.
4) C / C++ / Delphi:
Use DbiAcqPersistTableLock BDE function to create
a lock on a table that does not exist (Delphi or C++
only).
Syntax:
(Delphi) Check(DbiAcqPersistTableLock(Database1.Handle,
'NoTable1.DB', szPARADOX));
(C++) rslt = DbiAcqPersistTableLock(hDb, 'NoTable2.DB',
szPARADOX);
NOTE: Each instance of the application must have a
unique, non-existent table name or an attempt to place
a lock in the directory will fail. If this method is
used, an algorithm must be used to guarantee a unique
table name. If a user can only run one instance of
the program at a time, the network user name can be
used as a table name.
5) Paradox ObjectPAL:
If your application is apt to cause .LCK files to be
created and deleted often, the easiest (and least
resource-intensive) way to prevent this is to place a
read lock on a non-existent table within the directory
when your application starts up. (Paradox allows us
to place semaphore locks on tables that don't exist.)
Since read locks don't conflict with one another, all
users can do this, and the net result will be that the
.LCK file will not be deleted until the last user
exits the system.
Syntax:
At program startup:
Table.attach("DummyTbl.db")
Table.lock(Read)
At program shut-down:
Table.unlock(Read)
Table.unAttach
NOTES - These are observations on the problem:
1) The problem does not seem to occur on NT
workstation machines. The application can be either
16 or 32 bit. SetErrorMode seems to work with both
cases.
2) The problem is more likely to occur if the
network protocol is netBEUI. If possible use netBIOS
or IPX/SPX.
3) Constant closing and opening of tables will cause
this error more often.
4) The error most commonly occurs on the close of the
table, not the open. In this situation the closing
application tries to delete files that another
application now has open.
Example Routines:
Routine 1a) (Delphi Table Open Routine)
----------------------------------------
procedure TForm1.OpenTable(MyTable: TTable);
begin
{ Set the error mode to ignore critical errors }
SetErrorMode(SEM_FAILCRITICALERRORS);
{ Open the table and check if it was opened }
MyTable.Open;
if MyTable.Active = False then
begin
{ Retry opening the table }
MyTable.Open;
{ If an error happens again, raise an exception }
if MyTable.Active = False then
raise
EDatabaseError.Create('Error Opening table');
end;
{ Set the error mode back to the default }
SetErrorMode(0);
end;
Routine 1b) (Delphi Table Close Routine)
-----------------------------------------
procedure TForm1.CloseTable(MyTable: TTable);
begin
{ Set the error mode to ignore critical errors }
SetErrorMode(SEM_FAILCRITICALERRORS);
{ Close the table and check if it was closed }
MyTable.Close;
if MyTable.Active = True then
begin
{ Retry closing the table }
MyTable.Close;
{ If an error happens again, raise an exception }
if MyTable.Active = True then
raise
EDatabaseError.Create('Error Closing table');
end;
{ Set the error mode back to the default }
SetErrorMode(0);
end;
Routine 2a) (C / C++ Table Open Routine)
-----------------------------------------
DbiResult OpenTable(hDBIDb hTmpDb, pCHAR szTblName,
phDBICur phTmpCur)
{
DBIResult rslt;
// Set the error mode to ignore critical errors
SetErrorMode(SEM_FAILCRITICALERRORS);
// Open the table and check if it was opened
rslt = DbiOpenTable(hTmpDb, szTblName, NULL,
NULL, NULL, NULL, dbiREADWRITE, dbiOPENSHARED,
xltFIELD, FALSE, NULL, phTmpCur);
if (rslt != DBIERR_NONE)
// Retry opening the table
rslt = DbiOpenTable(hTmpDb, szTblName, NULL,
NULL, NULL, NULL, dbiREADWRITE, dbiOPENSHARED,
xltFIELD, FALSE, NULL, phTmpCur);
// Set the error mode back to the default
SetErrorMode(0);
return rslt;
}
Routine 2b) (C / C++ Table Open Routine)
----------------------------------------
DBIResult CloseTable(phDBICur phTmpCur)
{
DBIResult rslt;
// Set the error mode to ignore critical errors
SetErrorMode(SEM_FAILCRITICALERRORS);
// Close the table and check if it was closed
rslt = DbiCloseCursor(phTmpCur);
if (rslt != DBIERR_NONE)
// Retry closing the table
rslt = DbiCloseCursor(phTmpCur);
// Set the error mode back to the default
SetErrorMode(0);
return rslt;
}
Reference:
7/15/98 3:24:24 PM