Zugriff auf Log-Datei ist gesperrt

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Zugriff auf Log-Datei ist gesperrt

Beitrag von OLLI_S »

Liebe Community,

in meiner Anwendung lasse ich diverse Informationen in eine Log-Datei ausgeben.

Beim Start meiner Anwendung Überschreibe ich die Log-Datei:

Code: Alles auswählen

var
  LogFileName: string;
  LogDatei: Textfile;
 
procedure CreateLogFile ( aFileName: string );
begin
  LogFileName := aFileName;
  Assign ( LogDatei, LogFileName );
  Rewrite ( LogDatei );
end;
An diversen Stellen in meiner Anwengun schreibe ich Log-Ausgaben in die Datei

Code: Alles auswählen

procedure LogText ( aString: string );
begin
  WriteLn ( LogDatei, aString );
end;
Beim Beenden meiner Anwendung sollen alle noch nicht geschriebenen Daten in die Log-Datei geschrieben werden und die Datei geschlossen werden:

Code: Alles auswählen

procedure CloseLogFile;
begin
  Flush ( LogDatei );
  Close ( LogDatei );
end;
Alles funktioniert bestens.
Beim Beenden der Anwendung stehen die Log-Informationen in der Textdatei.

Wenn ich aber die Datei öffnen will, während die Anwendung läuft, erhalte ich die Meldung:
Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird.
Das passiert sowohl beim Öffnen über den Dateimanager, als auch beim Aufruf aus meiner Anwendung.

Mache ich hier etwas falsch?
Oder gibt es einen besseren Weg um die Log-Informationen in eine Textdatei zu schreiben?

Gruß

OLLI

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von diogenes »

Meiner bescheidenen Meinung nach machst nicht Du 'was falsch. Windows macht 'was falsch. Zwischen den Aufrufen von Rewrite und Close ist die Datei offen, und Windows sperrt jedem den Zugriff, insbesondere den Schreibzugriff, solange die Datei nicht zu ist. So einfach ist das.

Du kannst Dir helfen, indem Du LogText "ein wenig" umbaust:

Code: Alles auswählen

procedure LogText (aString: string );
 var
   LogDatei: Textfile;
 begin
   Assign ( LogDatei, LogFileName); // LogFileName müßte dafür global verfügbar sein
   if FileExists( LogDatei)
    then Append( LogDatei)
    else Rewrite( LogDatei); 
   WriteLn( LogDatei, aString );
   Close( LogDatei)
  end;
Das Erzeugen mit CreateLogFile und das Schließen mit CloseLogFile kannst Du dir dann sparen.
Der Witz daran ist, daß die Logdatei bei jedem Logvorgang nur sehr kurzfristig geöffnet wird. Wenn die Datei schon existiert, dann wird durch Append der neue Text bloß hinzu gefügt, ansonsten wird mit Rewrite neu erzeugt, also eine eventuell schon vorhandene Datei überschrieben. Wenn du bei jeden Lauf des Programmes ein komplett neues Log haben willst, rate ich Dir, beim Programmstart (will heißen, dort, wo Du dein CreateLogFile stehen hast) folgenden Befehl einzubauen: DeleteFile( LogFileName). Dabei gehe ich davon aus, daß LogFileName schon global verfügbar ist. Wenn Du DeleteFile( LogFileName) nicht aufrufst, wird das Log einmal erzeugt und dann angehängt, bis die Platte voll ist (oder Du genug hast :) ).

Sowohl FileExists als auch DeleteFile befinden sich in der Unit SysUtils.
Ceterum censeo computatores per Pascal docendos esse.

HOffen
Beiträge: 16
Registriert: Do 15. Mär 2012, 09:19
OS, Lazarus, FPC: Win7 (L 0.9.30.4RC3 FPC 2.6.0)
CPU-Target: 32Bit

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von HOffen »

Du könntest alternativ mit einem TFileStream arbeiten. Damit sollte auch ein paralleles Lesen (paralleles Schreiben ist nie möglich!) möglich sein.
In der Funktionsweise unterscheiden sich die Vorgehensweisen eigentlich nicht. Im Create von TFileStream weist man einen FileHandle zu und kann dann mit den Klassenfunktionen in die Datei schreiben. Im Create von TFileStream kannst du dann auch festlegen, wie exklusiv der Zugriff sein soll.
"There are 10 kinds of human. Those who understand the binary system and those who don't."

OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von OLLI_S »

Hallo diogenes,
diogenes hat geschrieben:Der Witz daran ist, daß die Logdatei bei jedem Logvorgang nur sehr kurzfristig geöffnet wird.
Wenn bei jeder Log-Ausgabe die Datei erst geöffnet und dann wieder geschlossen wird (bei jeder Zeile, die in die Datei geschrieben werden soll), sinkt dadurch nicht die Performance?
Ich habe ja unnötige Datei-Aktivitäten.

Gruß

OLLI

Benutzeravatar
theo
Beiträge: 10861
Registriert: Mo 11. Sep 2006, 19:01

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von theo »

Das ist nun mal so. http://www.freepascal.org/faq.var#fileshare" onclick="window.open(this.href);return false;

Nimm TFileStream bzw. http://www.freepascal.org/docs-html/rtl ... eopen.html" onclick="window.open(this.href);return false;

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von Scotty »

Eine hübsche Klasse für solche Sachen ist TEventlog.

diogenes
Beiträge: 200
Registriert: So 11. Jul 2010, 18:39
OS, Lazarus, FPC: Linux
CPU-Target: 64 Bit
Wohnort: Wien
Kontaktdaten:

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von diogenes »

OLLI_S hat geschrieben:Hallo diogenes,
diogenes hat geschrieben:Der Witz daran ist, daß die Logdatei bei jedem Logvorgang nur sehr kurzfristig geöffnet wird.
Wenn bei jeder Log-Ausgabe die Datei erst geöffnet und dann wieder geschlossen wird (bei jeder Zeile, die in die Datei geschrieben werden soll), sinkt dadurch nicht die Performance?
Ich habe ja unnötige Datei-Aktivitäten.

Gruß

OLLI
Stimmt, die Performance wird sinken. Auch in der Computerei gibt es sowas wie ein gratis Mittagessen nicht :(

OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von OLLI_S »

Hallo Scotty,
Scotty hat geschrieben:Eine hübsche Klasse für solche Sachen ist TEventlog.
Danke für die Info.

An globaler Stelle habe ich definiert:

Code: Alles auswählen

var
  fEventLogger: TEventLog;
Bei OnCreate des Formulars mache ich:

Code: Alles auswählen

// Erzeuge eine Log-Datei im Verzeichnis der EXE mit dem Namen der EXE (Ersetzte EXE durch LOG)
fEventLogger := TEventLog.Create(self);
fEventLogger.LogType := ltFile;
fEventLogger.FileName := StringReplace ( Application.Params [0], '.exe', '.log', [rfReplaceAll] );
fEventLogger.Log ('-------- EVENT LOGGER --------------');
Die Log-Datei wird angelegt und der Text wird mitgeloggt, aber die Datei ist immer noch gesperrt, solange die Anwendung noch läuft.
Ich erhalte nach wie vor die Meldung: Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird.

Gruß

OLLI

Benutzeravatar
theo
Beiträge: 10861
Registriert: Mo 11. Sep 2006, 19:01

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von theo »

Da die Datei von TEventLog im Modus fmShareDenyWrite geöffnet wird, kannst du sie von einem anderen Prozess zum Lesen öffnen (fmOpenRead), nicht aber zum Schreiben (fmOpenWrite, fmOpenReadWrite).

OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von OLLI_S »

theo hat geschrieben:Da die Datei von TEventLog im Modus fmShareDenyWrite geöffnet wird, kannst du sie von einem anderen Prozess zum Lesen öffnen (fmOpenRead), nicht aber zum Schreiben (fmOpenWrite, fmOpenReadWrite).
Na toll....

Ich will doch nur Log-Daten in eine Text-Datei speichern und diese Log-Datei jederzeit (auch solange die Lazarus-Anwendung noch läuft) öffnen.
Geöffnet soll die Datei mit einem Doppelklick im Explorer oder über einen Button in meiner Anwendung.
Was kann ich machen?

Gruß

OLLI

Benutzeravatar
theo
Beiträge: 10861
Registriert: Mo 11. Sep 2006, 19:01

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von theo »

Dann öffne sie halt im Modus fmShareDenyNone, habe ich dir weiter oben schon verlinkt.

OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von OLLI_S »

theo hat geschrieben:Dann öffne sie halt im Modus fmShareDenyNone, habe ich dir weiter oben schon verlinkt.
Ich habe mir in der Unit classesh.imc (ist bei Lazarus dabei) die Syntax der Create-Methode von TFileStream angeschaut:

Code: Alles auswählen

constructor Create(const AFileName: string; Mode: Word; Rights: Cardinal);
Also habe icherst mal eine Variable vom Typ TFileStream angelegt:

Code: Alles auswählen

var
  fTestStream: TFileStream;
Anschließend habe ich bei OnCreate meines Formulares folgendes geschrieben:

Code: Alles auswählen

fTestStream := TFileStream.Create ( 'd:\Test.txt', fmOpenReadWrite, fmShareDenyNone );
fTestStream.WriteAnsiString ( 'FileStream-Test' );
Die Log-Datei d:\Test.txt habe ich manuell angelegt (das automatische Anlegen mache ich noch) und meine Anwendung gestartet.
Aber der Zugriff auf diese Datei mit einem Doppelklick im Windows Explorer ist nicht möglich, während meine Anwendung läuft.
Beende ich die Anwendung, kann ich anschließend die Log-Datei mit einem Doppelklick im Windows Explorer öffnen.
Getestet in Windows XP Professional SP3 und Windows 7 x64 SP1.

Es wäre aber sehr wichtig die Log-Datei während der Laufzeit einzusehen.
Gerade wenn ein Fehler auftritt, den man nachvollziehen will.

Was mache ich falsch?
Warum ist die Datei gesperrt, obwohl ich bei dem Parameter "Rights" den Wert fmShareDenyNone angebe?
Danke für Eure Hilfe!

Gruß

OLLI

Benutzeravatar
theo
Beiträge: 10861
Registriert: Mo 11. Sep 2006, 19:01

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von theo »

TFilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);

OLLI_S
Beiträge: 65
Registriert: Di 17. Jan 2012, 20:55

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von OLLI_S »

Hallo theo,
theo hat geschrieben:TFilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);
Danke für den Tipp.
Meine Create-Aufruf schaut jetzt so aus:

Code: Alles auswählen

fLogStream := TFileStream.Create ( tmpLogFile, fmOpenWrite or fmShareDenyNone );
Das ganze funktioniert, ich habe jederzeit Zugriff auf die Datei.

Da die Log-Datei bei dem Benutzer nicht existieren kann, habe ich folgenden Create-Aufruf probiert:

Code: Alles auswählen

fLogStream := TFileStream.Create ( tmpLogFile, fmCreate or fmShareDenyNone );
Leider wird die Log-Datei dann zwar angelegt, der Zugriff ist dann aber wieder nicht möglich.
Frage: warum funktioniert das nicht?

Damit das ganze funktioniert Lege ich die Log-Datei bei Programmstart jedes mal neu an (damit sie bei Programmstart leer ist):

Code: Alles auswählen

// Lege die Log-Datei immer neu an
tmpFileHandle := FileCreate ( tmpLogFile );
FileClose ( tmpFileHandle );
Gibt es hier einen eleganteren Weg?

Wenn ich einen Log-Eintrag in die Datei schreiben will, mache ich das so:

Code: Alles auswählen

fLogStream.WriteAnsiString ( aText + #13 + #10 );
Das "+ #13 + #10" ist leider wegen dem Zeilenumbruch am Ende einer Zeile nötig.
Leider sind in der Log-Datei am Anfang jeder Zeile einige Sonderzeichen (teilweise nicht darstellbare, teilweise andere Zahlen und Ziffern).
Zum Schreiben in den Stream habe ich nur WriteAnsiString gefunden.

Gruß

OLLI

Benutzeravatar
theo
Beiträge: 10861
Registriert: Mo 11. Sep 2006, 19:01

Re: Zugriff auf Log-Datei ist gesperrt

Beitrag von theo »

OLLI_S hat geschrieben:Gibt es hier einen eleganteren Weg?
Du solltest ganz dringend lernen, dich selber zu orientieren.
Das ist z.B. in der erwähnten Klasse TEventLog gelöst. Den Source Code hast du, brauchst nur nachschauen.
Im Prinzip könntest du sowieso die ganze Klasse kopieren und nur fmShareDenyWrite ersetzen.
Oder noch besser, das in TEventlog konfigurierbar machen und dem FPC Team einen Patch schicken.
http://wiki.lazarus.freepascal.org/Creating_A_Patch/de" onclick="window.open(this.href);return false;
OLLI_S hat geschrieben: Wenn ich einen Log-Eintrag in die Datei schreiben will, mache ich das so:

Code: Alles auswählen

fLogStream.WriteAnsiString ( aText + #13 + #10 );
Das "+ #13 + #10" ist leider wegen dem Zeilenumbruch am Ende einer Zeile nötig.
Es gibt die BS-Abhängige Konstante "LineEnding", das ist besser als #13#10.
Ansonsten würde ich die LogDatei als Textfile handhaben und keinen Binary Code reinschreiben.

Antworten