问题描述
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,但仍然出现错误。
我想知道的: 假设不是反病毒软件(或类似软件)导致了问题,是否可能是因为操作系统在下次尝试写入文件之前没有清除标志(或类似的东西)?
解决方法
我想知道的:假设不是 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_ALWAYS
、CREATE_NEW
或 {{1 }}),然后将结果 OPEN_EXISTING
分配给 THandle
,例如:
THandleStream
在任何情况下,您都可以使用 SysInternals Process Explorer 之类的工具来验证是否有另一个打开文件的句柄,以及它属于哪个进程。如果有问题的句柄在您可以在 PE 中看到它之前被关闭,则使用 SysInternals Process Monitor 之类的工具实时记录对文件的访问并检查打开文件的重叠尝试。