Search code examples
delphidelphi-xe2delphi-xe

Is there a way to solve an I/O error 6 in Delphi?


procedure TfrmSongs.Display;
var
  i: Integer;
begin
  redOutput.Clear;
  redOutput.Lines.Add('The TOP 10');
  for i := 1 to iCount-1 do
  begin
    redOutput.Lines.Add(IntToStr(i)+arrSongs[i]);
  end;
end;

procedure TfrmSongs.FormActivate(Sender: TObject);
var
  tSongList: TextFile;
  sSong: string;
begin
  iCount := 0;
  AssignFile(tSongList, ExtractFilePath(Application.ExeName)+'Songs.txt');
  Reset(tSongList);
  while not EOF do
  begin
    Readln(tSongList, sSong);
    arrSongs[iCount] := sSong;
    Inc(iCount);
  end;
  CloseFile(tSongList);
  Display;
end;

I'm trying to display the array I tried to create via a text file in a rich edit. But every time I run the app, it gives me an 'I/O error 6' error and nothing displays. I don't know if it's something with the text file or if it's something with the display procedure.


Solution

  • There are a few problems with your code, but regarding the I/O error specifically, error 6 means "invalid file handle".

    Since you are getting a popup error notification, you clearly have I/O checking enabled, which it is by default.

    I/O error 6 is not typical for a failure on System.Reset(), and you are not seeing any other kind of error related to a failure in opening a file, so we can safely assume that the file is being opened successfully, and that System.Readln() and System.CloseFile() are not being passed an invalid I/O handle.

    So that leaves just one line that could be receiving an invalid I/O handle:

    while not EOF do

    System.Eof() has an optional parameter to tell it which file to check. Since you are omitting that parameter, Eof() will use System.Input instead. And a GUI process does not have a STDIN handle assigned by default. So that is likely where error 6 is coming from.

    That line needs to be changed to this instead:

    while not EOF(tSongFile) do

    UPDATE: given the declaration of arrSongs you have shown in comments (arrSongs: array[1..MAX] of string;), there are additional problems with your code. You need to make sure the reading loop does not try to store more than MAX strings in the array. Also, your reading loop is trying to store a string at index 0, which is not a valid index since the array starts at index 1. Also, Display() is skipping the last string in the array. See what happens when you omit important details?

    Try this instead:

    private
      arrSongs: array[1..MAX] of string;
    
    ...
    
    procedure TfrmSongs.Display;
    var
      i: Integer;
    begin
      redOutput.Clear;
      redOutput.Lines.Add('The TOP 10');
      for i := 1 to iCount do
      begin
        redOutput.Lines.Add(IntToStr(i) + arrSongs[i]);
      end;
    end;
    
    procedure TfrmSongs.FormActivate(Sender: TObject);
    var
      tSongList: TextFile;
      sSong: string;
    begin
      iCount := 0;
      AssignFile(tSongList, ExtractFilePath(Application.ExeName) + 'Songs.txt');
      Reset(tSongList);
      try
        while (not EOF(tSongList)) and (iCount < MAX) do
        begin
          Readln(tSongList, sSong);
          arrSongs[1+iCount] := sSong;
          Inc(iCount);
        end;
      finally
        CloseFile(tSongList);
      end;
      Display;
    end;
    

    That being said, I would suggest getting rid of the reading loop completely. You can use a TStringList instead:

    uses
      ..., System.Classes;
    
    ...
    
    private
      lstSongs: TStringList;
    
    ...
    
    procedure TfrmSongs.Display;
    var
      i: Integer;
    begin
      redOutput.Clear;
      redOutput.Lines.Add('The TOP 10');
      for i := 0 to lstSongs.Count-1 do
      begin
        redOutput.Lines.Add(IntToStr(i+1) + lstSongs[i]);
      end;
    end;
    
    procedure TfrmSongs.FormCreate(Sender: TObject);
    begin
      lstSongs := TStringList.Create;
    end;
    
    procedure TfrmSongs.FormDestroy(Sender: TObject);
    begin
      lstSongs.Free;
    end;
    
    procedure TfrmSongs.FormActivate(Sender: TObject);
    begin
      lstSongs.LoadFromFile(ExtractFilePath(Application.ExeName) + 'Songs.txt');
      Display;
    end;
    

    Or, you can use TFile.ReadAllLines() instead:

    uses
      ..., System.IOUtils;
    
    ...
    
    private
      arrSongs: TStringDynArray;
    
    ...
    
    procedure TfrmSongs.Display;
    var
      i: Integer;
    begin
      redOutput.Clear;
      redOutput.Lines.Add('The TOP 10');
      for i := 0 to High(arrSongs) do
      begin
        redOutput.Lines.Add(IntToStr(i+1) + arrSongs[i]);
      end;
    end;
    
    procedure TfrmSongs.FormActivate(Sender: TObject);
    begin
      arrSongs := TFile.ReadAllLines(ExtractFilePath(Application.ExeName) + 'Songs.txt');
      Display;
    end;