错误:“无法打开文件“20210609.log”。进程无法访问该文件,因为它正被另一个进程使用“

时间:2021-06-16 18:29:45

标签: windows delphi delphi-10.2-tokyo

我有一些第三方代码可以使用以下代码一次一行地写入日志文件:

procedure TLog.WriteToLog(Entry: ansistring);
var
    strFile: string;
    fStream: TFileStream;
    strDT: ansistring;
begin
    if ((strLogDirectory<>'') and (strFileRoot<>'')) then
    begin
        if not(DirectoryExists(strLogDirectory)) then
            ForceDirectories(strLogDirectory);
        strFile:=strLogDirectory + '\' + strFileRoot + '-' + strFilename;
        if FileExists(strFile) then
            fStream:=TFileStream.Create(strFile, fmOpenReadWrite)
        else
            fStream:=TFileStream.Create(strFile, fmCreate);
        fStream.Seek(0, soEnd);
        if blnUseTimeStamp then
            strDT:=formatdatetime(strDateFmt + ' hh:mm:ss', Now) + ' ' + Entry + chr(13) + chr(10)
        else
            strDT:=Entry + chr(13) + chr(10);
        fStream.WriteBuffer(strDT[1], length(strDT));
        FreeandNil(fStream);
    end;
end;

这之前在客户网站上运行良好,但在过去几周内,它现在在标题中出现错误。

没有其他进程应该打开文件。我怀疑是 Anti-Virus,但客户声称他们已禁用 AntiV,但仍然出现错误。

错误似乎只在代码处于循环中并且快速写行时发生。

我想知道的: 假设不是反病毒软件(或类似软件)导致了问题,是否可能是因为操作系统在下次尝试写入文件之前没有清除标志(或类似的东西)?

1 个答案:

答案 0 :(得分:1)

<块引用>

我想知道的:假设不是 Anti-Virus(或类似的)导致问题,可能是因为操作系统在下次尝试写入之前没有清除标志(或类似的东西)到文件?

不,这不是速度问题,也不是缓存问题。这是共享冲突,这意味着必须有另一个打开的句柄到同一文件,其中该句柄分配(或缺少)共享权限与此代码请求的权限不兼容。

例如,如果另一个句柄不共享读+写访问权限,那么在使用 TFileStream 创建 fmOpenReadWrite 时,此代码将无法打开文件。如果任何句柄对文件打开,当使用 TFileStream 创建 fmCreate 时此代码将失败,因为它请求对文件的独占访问默认情况下。

我会建议更像这样的:

procedure TLog.WriteToLog(Entry: AnsiString);
var
  strFile: string;
  fStream: TFileStream;
  strDT: AnsiString;
  fMode: Word;
begin
  if (strLogDirectory <> '') and (strFileRoot <> '') then
  begin
    ForceDirectories(strLogDirectory);
    strFile := IncludeTrailingPathDelimiter(strLogDirectory) + strFileRoot + '-' + strFilename;
    fMode := fmOpenReadWrite or fmShareDenyWrite;
    if not FileExists(strFile) then fMode := fMode or fmCreate;
    fStream := TFileStream.Create(strFile, fMode);
    try
      fStream.Seek(0, soEnd);
      if blnUseTimeStamp then
        strDT := FormatDateTime(strDateFmt + ' hh:mm:ss', Now) + ' ' + Entry + sLineBreak
      else
        strDT := Entry + sLineBreak;
      fStream.WriteBuffer(strDT[1], Length(strDT));
    finally
      fStream.Free;
    end;
  end;
end;

但是,请注意使用 FileExists() 会引入竞争条件。在检查存在之后和打开/创建文件之前,该文件可能被其他人删除/创建。最好让操作系统为您处理。

至少在 Windows 上,您可以将 CreateFile() 直接与 OPEN_ALWAYS 标志一起使用(TFileStream 只使用 CREATE_ALWAYSCREATE_NEW 或 {{1 }}),然后将结果 OPEN_EXISTING 分配给 THandle,例如:

THandleStream

在任何情况下,您都可以使用 SysInternals Process Explorer 之类的工具来验证是否有另一个打开文件的句柄,以及它属于哪个进程。如果有问题的句柄在您可以在 PE 中看到它之前被关闭,则使用 SysInternals Process Monitor 之类的工具实时记录对文件的访问并检查打开文件的重叠尝试。

相关问题