Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi E/A-Fehler in Wrapperklasse (https://www.delphipraxis.net/166597-e-fehler-wrapperklasse.html)

uligerhardt 21. Feb 2012 12:49

Delphi-Version: 2007

E/A-Fehler in Wrapperklasse
 
Hallo zusammen!

Ich bastele mir gerade eine kleine Klasse zusammen, die u.a. ein TextFile enthält. Dummerweise reagiert mein Konstrukt anders als erwartet, wenn ich versuche, eine nicht vorhandene Datei zu öffnen. Hier mal meine Test-Unit:
Delphi-Quellcode:
{$IOCHECKS ON}
unit Unit2;

interface

type
  TTest = class
  private
    f: TextFile;
  public
    constructor Create(const AFileName: string);
    destructor Destroy; override;

    class procedure Execute(const AFileName: string); static;
  end;

procedure Test(const AFileName: string);

implementation

{ TTest }

constructor TTest.Create(const AFileName: string);
begin
  inherited Create;
  AssignFile(f, AFileName);
  Reset(f);
end;

destructor TTest.Destroy;
begin
  CloseFile(f);
  inherited;
end;

class procedure TTest.Execute(const AFileName: string);
var
  t: TTest;
begin
  t := TTest.Create(AFileName);
  try
    //
  finally
    t.Free;
  end;
end;

{ Test }

procedure Test(const AFileName: string);
var
  f: TextFile;
begin
  AssignFile(f, AFileName);
  Reset(f);
  try
    //
  finally
    CloseFile(f);
  end;
end;

end.
Wenn ich nun
Delphi-Quellcode:
Test('C:\Gibts.nicht');
aufrufe, kriege ich wie erwartet "Datei nicht gefunden." (E/A-Fehler 2). Führe ich allerdings
Delphi-Quellcode:
TTest.Execute('C:\Gibts.nicht');
aus, so kommt die Meldung "E/A-Fehler 103.". Kann mir das jemand erklären?

Bummi 21. Feb 2012 13:03

AW: E/A-Fehler in Wrapperklasse
 
Delphi-Quellcode:
constructor TTest.Create(const AFileName: string);
begin
  inherited Create;
  AssignFile(f, AFileName);
  if FileExists(AFileName) then Reset(f) else Rewrite(f);
end;
zudem ist C: direkt seit Vista eine schlechte Idee

Klaus01 21. Feb 2012 13:14

AW: E/A-Fehler in Wrapperklasse
 
Hallo,

die 103 erhälst Du weil im Create kein CloseFile ausgeführt wurde.

Das Filehandle existiert und Du versuchst die Datei erneut zu öffnen.
Wenn Du mit der Execute Methode eine andere nicht existierende Datei öffnste solltest
Du dann auch den Fehlercode 2 bekommen.

Grüße
Klaus

uligerhardt 21. Feb 2012 13:42

AW: E/A-Fehler in Wrapperklasse
 
Zitat:

Zitat von Bummi (Beitrag 1152115)
Delphi-Quellcode:
constructor TTest.Create(const AFileName: string);
begin
  inherited Create;
  AssignFile(f, AFileName);
  if FileExists(AFileName) then Reset(f) else Rewrite(f);
end;

Damit erzeuge ich die Datei aber, wenn sie nicht da ist, oder? Das will ich nicht, eine Exception ist schon in Ordnung.

Zitat:

Zitat von Bummi (Beitrag 1152115)
zudem ist C: direkt seit Vista eine schlechte Idee

Der Pfad war natürlich ein Beispiel.

Zitat:

Zitat von Klaus01 (Beitrag 1152118)
Hallo,

die 103 erhälst Du weil im Create kein CloseFile ausgeführt wurde.

Das Filehandle existiert und Du versuchst die Datei erneut zu öffnen.
Wenn Du mit der Execute Methode eine andere nicht existierende Datei öffnste solltest
Du dann auch den Fehlercode 2 bekommen.

Grüße
Klaus

Wahrscheinlich bin ich heute blind. Also nochmal: Ich habe folgenden Test-Code:
Delphi-Quellcode:
procedure TForm1.FormDblClick(Sender: TObject);
begin
  Test('C:\Gibts.nicht');
  //TTest.Execute('C:\Gibts.nicht');
end;
Wenn ich den laufen lasse, kommt Fehler 2 (wie erwartet). Wenn ich den Quellcode in
Delphi-Quellcode:
procedure TForm1.FormDblClick(Sender: TObject);
begin
  //Test('C:\Gibts.nicht');
  TTest.Execute('C:\Gibts.nicht');
end;
ändere und neu ausführe, kommt Fehler 103. Warum einmal 2 und einmal 103? Es wird doch in beiden Läufen auf eine Variable
Delphi-Quellcode:
f: TextFile
erst ein
Delphi-Quellcode:
AssignFile(f, AFileName);
und direkt danach ein
Delphi-Quellcode:
Reset(f);
losgelassen.

himitsu 21. Feb 2012 13:57

AW: E/A-Fehler in Wrapperklasse
 
Wenn der Constructor fehlschlägt (Exception), dann wird die Klasse gleich wieder freigegeben (Destroy wird aufgerufen).
(Ob das soweit stimmt, würde einem der Debugger verraten)

Die Klasse entspricht also eher diesem: (man beachte das Reset)
Delphi-Quellcode:
procedure Test(const AFileName: string);
var
  f: TextFile;
begin
  AssignFile(f, AFileName);
  try
    Reset(f);
    //
  finally
    CloseFile(f);
  end;
end;
Wie sieht denn die Fehlermeldung hier aus?



Und was passiert bei Folgendem?
Delphi-Quellcode:
destructor TTest.Destroy;
begin
  if TFileRec(f).Mode <> fmClosed then
    CloseFile(f);
  inherited;
end;

// oder
destructor TTest.Destroy;
begin
  //if TFileRec(f).Mode in [fmInput, fmOutput, fmInOut] then
  if TFileRec(f).Mode = fmInput then
    CloseFile(f);
  inherited;
end;

Klaus01 21. Feb 2012 14:03

AW: E/A-Fehler in Wrapperklasse
 
Error Code 103:
ERROR_TOO_MANY_SEM_REQUESTS - The semaphore cannot be set again.

die Fehlermeldung bekommst Du weil schon eine Verbindung (Semaphore) zu dem Dateinamen besteht. Weil das CloseFile fehlt.

Wohingegen:
Error Code 2:
ERROR_FILE_NOT_FOUND - The system cannot find the file specified.
Quelle

Lösungen um das zu umgehen stehen bereits in den Posts über diesen.

Grüße
Klaus

uligerhardt 22. Feb 2012 08:47

AW: E/A-Fehler in Wrapperklasse
 
Zitat:

Zitat von himitsu (Beitrag 1152132)
Wenn der Constructor fehlschlägt (Exception), dann wird die Klasse gleich wieder freigegeben (Destroy wird aufgerufen).
(Ob das soweit stimmt, würde einem der Debugger verraten)

Die Klasse entspricht also eher diesem: (man beachte das Reset)
Delphi-Quellcode:
procedure Test(const AFileName: string);
var
  f: TextFile;
begin
  AssignFile(f, AFileName);
  try
    Reset(f);
    //
  finally
    CloseFile(f);
  end;
end;
Wie sieht denn die Fehlermeldung hier aus?

OK, das ist es. Jetzt, wo ich es auf dem Silbertablett serviert kriege, sehe ich es auch.:oops:

Zitat:

Zitat von himitsu (Beitrag 1152132)
Und was passiert bei Folgendem?
Delphi-Quellcode:
destructor TTest.Destroy;
begin
  if TFileRec(f).Mode <> fmClosed then
    CloseFile(f);
  inherited;
end;

// oder
destructor TTest.Destroy;
begin
  //if TFileRec(f).Mode in [fmInput, fmOutput, fmInOut] then
  if TFileRec(f).Mode = fmInput then
    CloseFile(f);
  inherited;
end;

Ich habe die erste Variante genommen (mit TTextRec statt TFileRec) und kriege jetzt das erhoffte Ergebnis. Ist eigentlich logisch - ist ja der gleich Grund, aus dem man (mindestens) in Destruktoren Free statt Destroy aufrufen sollte - damit nur tatsächliche allozierte Ressourcen freigegeben werden.

Vielen Dank euch allen!

himitsu 22. Feb 2012 09:59

AW: E/A-Fehler in Wrapperklasse
 
Bei dem <>fmClose muß nur uf jeden Fall die File-Variable initialisiert sein, bevor das FileClose auch nur daran denken könnte man dranzukommen.

Es ist oftmals zwar eher unwahrscheinlich, daß ein "billiges" Create (inherited) fehlschlagen kann, aber dennoch würde
Delphi-Quellcode:
AssignFile(f, AFileName);
noch davor hingehören.
AssignFile selber macht noch keine Dateizugriffe, es initialisiert nur die Variable und legt den Dateinamen (als ShortString) darin ab.
Ohne sichere Initialisierung wäre die Prüfung auf fmInput, fmOutput und fmInOut noch am Sichersten.
Im Falle des Objektes und da dessen Speicher automatisch mit 0/nil initialisiert wird, könnte man auch auf
Delphi-Quellcode:
if (TFileRec(f).Mode <> 0) or (TFileRec(f).Mode <> fmClosed) then
prüfen und das AssignFile so belassen. :angle:

uligerhardt 22. Feb 2012 10:07

AW: E/A-Fehler in Wrapperklasse
 
Zitat:

Zitat von himitsu (Beitrag 1152225)
Bei dem <>fmClose muß nur uf jeden Fall die File-Variable initialisiert sein, bevor das FileClose auch nur daran denken könnte man dranzukommen.

Die Dateivariable ist als Member einer Klasse ja null-initialisiert. Das ist also kein Problem.

himitsu 22. Feb 2012 10:13

AW: E/A-Fehler in Wrapperklasse
 
fmClose ist aber nicht 0 :zwinker:
Und der Delphi-Code prüft auch nicht auf 0.

fmClose und Co. sind "komische" Magicnumbers. (als Enum, mit fmClose=0 wären die praktischer gewesen)

gammatester 22. Feb 2012 10:20

AW: E/A-Fehler in Wrapperklasse
 
Zitat:

Zitat von himitsu (Beitrag 1152233)
fmClose und Co. sind "komische" Magicnumbers. (als Enum, mit fmClose=0 wären die praktischer gewesen)

Nein wäre es nicht, weil man dann nicht unterscheiden könnte, ob schon ein assign gemacht wurde oder nicht!

uligerhardt 22. Feb 2012 10:29

AW: E/A-Fehler in Wrapperklasse
 
Zitat:

Zitat von himitsu (Beitrag 1152233)
fmClose ist aber nicht 0 :zwinker:

Uppsi. Wer rechnet denn mit sowas? :angle2:
Zitat:

Zitat von himitsu (Beitrag 1152233)
fmClose und Co. sind "komische" Magicnumbers. (als Enum, mit fmClose=0 wären die praktischer gewesen)

Muss von mir aus kein Enum sein, aber der Ordinalwert 0 wäre vielleicht schon günstig gewesen. Naja gut, dann halt
Delphi-Quellcode:
if (TTextRec(FFile).Mode <> 0) and (TTextRec(FFile).Mode <> fmClosed) then
.

uligerhardt 22. Feb 2012 10:43

AW: E/A-Fehler in Wrapperklasse
 
Zitat:

Zitat von gammatester (Beitrag 1152236)
Zitat:

Zitat von himitsu (Beitrag 1152233)
fmClose und Co. sind "komische" Magicnumbers. (als Enum, mit fmClose=0 wären die praktischer gewesen)

Nein wäre es nicht, weil man dann nicht unterscheiden könnte, ob schon ein assign gemacht wurde oder nicht!

Stimmt auch wieder. Mir fällt zwar auf die Schnelle kein Grund ein, warum ich das wissen wollte, aber da gibt es bestimmt "kreativere" Menschen. :lol:

Übrigens ist mir gerade beim Betrachten der Implementation von AssignFile (also System._Assign) ein Lapsus aufgefallen: Die Länge des übergebenen Strings wird nicht geprüft. Führt man folgende Routine aus
Delphi-Quellcode:
procedure Test2;
var
  Opfer: ShortString;
  f: TextFile;
begin
  Opfer := StringOfChar('.', 255);
  AssignFile(f, StringOfChar('X', 500));
end;
so verstümmelt der Aufruf von AssignFile kommentarlos die Variable Opfer. Klar ist der übergebene String kein gültiger Dateiname, aber eine Exception wäre da wohl schon angebracht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:10 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz