你打电话时
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
调用该文件之前呢?但我猜代码之所以写成这样,是因为没有办法使用,已经传到. 因此,这个可怕的拙劣。CreateFile
TFileStream.Create
TFileStream.Create
OPEN_ALWAYS
CreateFile
事实证明,如果选择了该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 Rights
FileOpen
Rights
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
以前的工作!