你打电话时
TFile.OpenRead(Path)
这是由
TFileStream.Create(Path, fmOpenRead, 0)
这反过来又会导致调用
FileOpen(Path, fmOpenRead or 0)
最终将CreateFile传递0称为dwShareMode. 并且文档CreateFile说dwShareMode的0意思是:
阻止其他进程在请求删除、读取或写入访问权限时打开文件或设备。
换句话说,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调用该文件之前呢?但我猜代码之所以写成这样,是因为没有办法使用,已经传到. 因此,这个可怕的拙劣。CreateFileTFileStream.CreateTFileStream.CreateOPEN_ALWAYSCreateFile
事实证明,如果选择了该fmCreate选项,因为Exists()返回False,那么您的共享选项将被忽略。那是因为它们被传递给Rights参数TFileStream.Create而不是与fmCreate. 正如文档所说,在 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 RightsFileOpenRights
TFile.Open因此,所有这一切都解释了为什么当且仅当文件已经存在时,才能在您的调用中正确设置共享模式。
所以,不仅不能用TFile.OpenRead,而且TFile.Open也out了。在你领先时退出并完全放弃TFile。我不知道 Embarcadero 的 QA 在TFile引入时发生了什么,但显然有一个重大失败。将这种失败与奇怪的设计缺陷结合起来TFileStream.Create,你就有了一个名副其实的错误工厂。
我提交了一份质量控制报告:QC#115020。TFileStream.Create非常有趣的是,在不应该使用的地方使用的错误行为Rights对于 XE3 来说是新的。我相信这是一种尝试处理TFile.Open已被报告为QC#107005的虚假代码,该代码被错误地标记为已修复。TFile.Open可悲的是,修复叶子的尝试TFile.Open仍然坏了,反过来又打破了TFileStream.Create以前的工作!