Häufig ist es nötig, exclusiven Zugriff auf eine Datei zu haben.
Wenn z.B. 20 Rechner im Netzwerk in eine Log-Datei schreiben, darf immer nur ein Prozess die Datei offen haben. (siehe auch
Mutex)
Dazu gibt es nun die Klasse
TExclusiveFileStream.
Zu
TFileStream gibt es folgende Unterschiede:
* Falls die Datei noch nicht exisitiert, wird sie erzeugt
* der Zugriff erlaubt immer Lese- & Schreibzugriff
* kein anderer Prozess kann die Datei lesen oder schreiben, solange ein Objekt der Klasse existiert
* es kann ein Timeout angegeben werden. Innerhalb dieser Zeit (in ms) wird versucht die Datei zu öffnen/erzeugen. übliche Zeiten liegen zwischen 200ms und 10s.
* Während des Wartens, bis die Datei frei wird, wird das Event OnWait "abgefeuert". Dies erlaubt dem Programm, das Warten sichtbar zu machen
* Falls die Datei nicht geöffnet werden konnte, enthält die
Exception ein aussagekräfte Meldung, warum es nicht geklappt hat
TExclusiveFileStream wurde sorgfältig (von mir
getestet.
Es existiert dazu eine Testanwendung (multithreaded), deren Sourcecode sich im Anhang befindet.
Die Funktion GetTickCount wurde so verwendet, dass nach 49,7 Tagen kein Overflow Problem entsteht.
Delphi-Quellcode:
TExclusiveFileStream =
class(THandleStream)
private
FFileName :
string;
public
constructor Create(
const FileName:
string; timeout:Cardinal; OnWait:TNotifyEvent=nil);
destructor Destroy;
override;
procedure SeekToEnd;
property FileName:
string read FFileName;
end;
{ TExclusiveFileStream }
constructor TExclusiveFileStream.Create(
const filename:
string; timeout: Cardinal; OnWait: TNotifyEvent);
const
FILE_SHARE_EXCLUSIVE = 0;
var
hnd : THandle;
curtime, starttime : Cardinal;
error : DWORD;
begin
FFileName := FileName;
starttime := GetTickCount;
while true
do
begin
hnd := CreateFile(PChar(filename), GENERIC_READ
or GENERIC_WRITE, FILE_SHARE_EXCLUSIVE,
nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if hnd <> INVALID_HANDLE_VALUE
then
begin
inherited Create(hnd);
Exit;
end;
error := GetLastError;
curtime := GetTickCount;
if ((curtime - starttime) >= timeout)
or
((error<>ERROR_SHARING_VIOLATION)
and (error<>ERROR_LOCK_VIOLATION))
then
raise EFOpenError.CreateFmt(SFOpenError+#13#10+
SysErrorMessage(error), [FileName]);
if Assigned(OnWait)
then
OnWait(Self);
Sleep(100);
end;
end;
destructor TExclusiveFileStream.Destroy;
begin
if DWORD(
Handle) <> INVALID_HANDLE_VALUE
then
FileClose(
Handle);
end;
procedure TExclusiveFileStream.SeekToEnd;
begin
Seek(0, soFromEnd);
end;