gpt4 book ai didi

delphi - 即使使用 TFile 标记为共享读取,也无法再次打开文件

转载 作者:行者123 更新时间:2023-12-03 15:39:52 27 4
gpt4 key购买 nike

鉴于此代码:

  FN := 'c:\temp\test_file.log';
AFile := TFile.Open(FN, TFileMode.fmOpenOrCreate, TFileAccess.faReadWrite, TFileShare.fsRead);
try
with TFile.OpenRead(FN) do
try

finally
Free;
end;

finally
AFile.Free;
end;

尝试在 TFile.OpenRead(FN) 行打开时出现错误:
enter image description here

使用:
with TFile.Open('c:\temp\test_file.log', TFileMode.fmOpen, TFileAccess.faRead, TFileShare.fsRead) do
try

finally
Free;
end;

也会导致同样的错误。就像:
  FS := TFileStream.Create('c:\temp\test_file.log', fmOpenRead or fmShareDenyWrite);
try

finally
FS.Free;
end;

但是,我可以很高兴地在记事本中打开文件(如只读),或者如果我将初始 TFileShare.fsRead 更改为 TFileShare.fsNone,则无法按预期打开它(在记事本中)。

但是,如果我运行这个虚拟应用程序的两个实例,第一个使用 TFileShare.fsRead 打开我可以打开它。那么我是否无法在同一个应用程序中重新打开文件两次?好像不太对

如果我最初使用以下命令打开文件:
  FS := TFileStream.Create('c:\temp\test_file.log', fmOpenReadWrite or fmShareDenyWrite);
try

finally
FS.Free;
end;

我可以使用上述方法再次打开它(使用 fsRead)。令人困惑的是单步执行 TFile.Open 代码,它最终执行与上述 TFileStream.Create 完全相同的代码。

最后注意。如果我使用顶部(第一个)方式打开但将其分配给“全局”变量,请删除内部 TFile.OpenRead(FN) 调用,然后尝试通过另一个按钮单击打开文件,例如,错误仍然存​​在。这证明它与嵌套调用无关。

最佳答案

你打电话时

TFile.OpenRead(Path)

这是由
TFileStream.Create(Path, fmOpenRead, 0)

这反过来又导致调用
FileOpen(Path, fmOpenRead or 0)

最后调用 CreateFile路过 0dwShareMode .以及 CreateFile 的文档说 dwShareMode0方法:

Prevents other processes from opening a file or device if they request delete, read, or write access.



换句话说, TFile.OpenRead(Path)正在尝试以独占共享模式打开文件。由于文件已经打开,这显然会失败。

我认为 TFile.OpenRead(Path)正在使用错误的共享模式。它应该允许读取访问。但是,即使是这种情况,它也无济于事,因为您的另一个句柄具有写访问权限。

避免问题解决 TFile.OpenRead .而是像这样打开它:
TFileStream.Create(Path, fmOpenRead or fmShareDenyNone)

您必须通过 fmShareDenyNone .您无权拒绝任何形式的共享,因为您已经打开它进行阅读和写作。

还有一个问题是我最初写这个答案时没能理解的。确实如此 TFile.OpenRead()总是试图获得独占访问。但是您使用 TFile.Open() 也是事实。 ,您调用的第一个电话也可能导致独占访问。即使您指定了 TFileShare.fsRead .
TFile.Open()中的代码创建文件流的内容如下:
if Exists(Path) then
Result := TFileStream.Create(Path, LFileStrmAccess, LFileStrmShare)
else
Result := TFileStream.Create(Path, fmCreate, LFileStrmShare);

马上这是一场灾难。在文件存在检查中切换文件创建行为是简单且完全错误的。文件创建需要是一个原子操作。如果文件是在 Exists 之后创建的怎么办返回,但在调用 CreateFile 之前里面做的 TFileStream.Create ?但是我猜代码写成这样的原因是没有办法使用 TFileStream.Create并有 OPEN_ALWAYS传递给 CreateFile .因此,这个可怕的拙劣。

事实证明,如果 fmCreate选项被选中,因为 Exists()返回 False ,则您的共享选项将被忽略。那是因为它们被传递给 Rights TFileStream.Create的参数而不是与 fmCreate 结合使用.如 documentation说,在 Windows 上 Rights参数被忽略。

所以正确的代码应该是:
Result := TFileStream.Create(Path, fmCreate or LFileStrmShare);

那么 if 的另一个分支呢?这当然也是错误的。由于值传递给 Rights被忽略,那么肯定是 LFileStrmShare 的值被忽略。好吧,事实证明文档撒了谎。 TFileStream.Create中的代码读到:
constructor TFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
var
LShareMode: Word;
begin
if (Mode and fmCreate = fmCreate) then
begin
LShareMode := Mode and $FF;
if LShareMode = $FF then
LShareMode := fmShareExclusive; // For compat in case $FFFF passed as Mode
inherited Create(FileCreate(AFileName, LShareMode, Rights));
if FHandle = INVALID_HANDLE_VALUE then
raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);
end
else
begin
inherited Create(FileOpen(AFileName, Mode or Rights));
if FHandle = INVALID_HANDLE_VALUE then
raise EFOpenError.CreateResFmt(@SFOpenErrorEx, [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);
end;
FFileName := AFileName;
end;

else分行所在 Mode or Rights传递给 FileOpen .那看起来不太像 Rights正在被忽视。

所有这一切都解释了为什么在您调用 TFile.Open 时正确设置了共享模式。当且仅当文件已经存在。

所以,你不仅不能使用 TFile.OpenRead ,但是 TFile.Open也出来了。当你领先时退出并放弃 TFile共。我不知道 Embarcadero 的 QA 发生了什么 TFile被引入,但显然有一个重大的失败。将该失败与 TFileStream.Create 的奇怪设计缺陷结合起来你有一个名副其实的错误工厂。

我提交了QC报告: QC#115020 .非常有趣的是 TFileStream.Create 中的错误行为, 其中 Rights在不应该使用的时候使用,是 XE3 的新手。我相信这是试图处理 TFile.Open 中的假代码。已被报告为 QC#107005 ,它被错误地标记为固定。可悲的是试图修复 TFile.Open叶子 TFile.Open还是坏了,反过来又坏了 TFileStream.Create这曾经有效!

关于delphi - 即使使用 TFile 标记为共享读取,也无法再次打开文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16027127/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com