user2858981
user2858981

Reputation: 255

Delphi 2010 - EDirectoryNotFoundException when trying to save logs

I'm developing a program in Delphi 2010 that has to save the logs that are stored in a Tmemo. I'm trying to create a log file for everyday in which I append the logs from a memo.After I append the text I clear the content of the memo. So in the location of my app i want to create a folder named "loguri-mover_ftp" in which i want to store the log file. EX: log_mover-ftp_2-16-2015.txt

The code I use for this is:

If DirectoryExists(ExtractFilePath(Application.ExeName) + 'loguri-mover_ftp') then

begin

TFile.AppendAllText(ExtractFilePath(Application.ExeName) + 'loguri-mover_ftp\log_mover-ftp_' + datetostr(now) + '.txt',memo_loguri.lines.text, TEncoding.UTF8);

Memo_loguri.lines.text:='';
end

else

begin

CreateDir(ExtractFilePath(Application.ExeName) + 'loguri-mover_ftp');

TFile.AppendAllText(ExtractFilePath(Application.ExeName) + 'loguri-mover_ftp\log_mover-ftp_' + datetostr(now) + '.txt',memo_loguri.lines.text, TEncoding.UTF8);

Memo_loguri.lines.text:='';
end;

Because I'm interested in the stability of my application I've enabled the MadExcept debugger inside my app. After 2 hours 12 minutes i get the following error:

exception class   : EDirectoryNotFoundException
exception message : The specified path was not found.
compiled with     : Delphi 2010
program up time   : 2 hours 12 minutes
madExcept version : 4.0.7
callstack crc     : $bed2c7c0, $c58f696b, $05cb237f
count             : 5
exception number  : 1

disassembling:
[...]
005ce40a       push    eax
005ce40b       call    -$13ee30 ($48f5e0)     ; SysUtils.TEncoding.GetUTF8
005ce410       mov     ecx, eax
005ce412       pop     eax
005ce413       pop     edx
005ce414     > call    -$1144ad ($4b9f6c)     ; IOUtils.TFile.AppendAllText
005ce419 775   mov     eax, [ebp+8]
005ce41c       mov     eax, [eax+$2a0]
005ce422       xor     edx, edx
005ce424       mov     ecx, [eax]
005ce426       call    dword ptr [ecx+$2c]
[...]

What am I doing wrong?

Upvotes: 0

Views: 857

Answers (1)

David Heffernan
David Heffernan

Reputation: 612963

The exception is raised by the call to AppendAllText. If you follow the source for that function you will find a call to InternalCheckFilePathParam, the implementation of which looks like this:

class procedure TFile.InternalCheckFilePathParam(const Path: string;
    const FileExistsCheck: Boolean);
begin
  if (Length(Path) >= MAX_PATH - TFile.FCMinFileNameLen) and
     (not TPath.IsExtendedPrefixed(Path)) then
    raise EPathTooLongException.CreateRes(@SPathTooLong);
  if not TPath.HasPathValidColon(Path) then
    raise ENotSupportedException.CreateRes(@SPathFormatNotSupported);
  if Trim(Path) = '' then // DO NOT LOCALIZE
    raise EArgumentException.CreateRes(@SInvalidCharsInPath);
  if not TPath.HasValidPathChars(Path, False) then
    raise EArgumentException.CreateRes(@SInvalidCharsInPath);
  if not TDirectory.Exists(TPath.DoGetDirectoryName(TPath.DoGetFullPath(Path))) then
    raise EDirectoryNotFoundException.CreateRes(@SPathNotFound);
  if FileExistsCheck and (not Exists(Path)) then
    raise EFileNotFoundException.CreateRes(@SFileNotFound);
end;

Now, Path is the first argument that you passed to AppendAllText. Since EDirectoryNotFoundException is being raised, we can conclude that the directory containing Path does not exist.

Of course, this seems odd give that you check for its existence and then create it. I think the mystery can be solved by looking at what datetostr(now) returns. You imagine that the date separator used is -. But what if the date separator is /? In that case the / will be interpreted as a path delimiter.

The solution is to specify the date separator explicitly by using the DateToStr overload that accepts a TFormatSettings parameter.


I also cannot ignore the duplication in your code. Please don't ever repeat magic strings the way you do. The code should look like this:

LogFileDir := TPath.Combine(ExtractFilePath(Application.ExeName), 'loguri-mover_ftp');
if not DirectoryExists(LogFileDir) then
  ForceDirectories(LogFileDir);
DateStr := DateToStr(Now, ...); // you supply an appropriate TFormatSettings
LogFilePath := TPath.Combine(LogFileDir, log_mover-ftp_' + DateStr + '.txt');
TFile.AppendAllText(LogFilePath, memo_loguri.lines.text, TEncoding.UTF8);

Upvotes: 1

Related Questions