Когда вы звоните
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
. Как указывает documentation, в Windows параметр Rights
игнорируется.
Так правильный код должен быть:
Result := TFileStream.Create(Path, fmCreate or LFileStrmShare);
А как насчет другой ветви, если? Наверное, это тоже неправильно. Поскольку значение, переданное в 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
. Я не знаю, что случилось с QA в Embarcadero, когда был введен TFile
, но, очевидно, был серьезный провал. Объедините эту неудачу с необычными недостатками дизайна от TFileStream.Create
, и у вас есть настоящая фабрика ошибок.
Я представил отчет о контроле качества: QC#115020. Весьма интересно, что ошибочное поведение в TFileStream.Create
, где Rights
используется, когда оно этого не должно, является новым для XE3. Я считаю, что это попытка разобраться с фиктивным кодом в TFile.Open
, который уже был указан как QC#107005, который неправильно помечен как Исправлено. К сожалению, попытка исправить TFile.Open
оставляет TFile.Open
еще сломанной и, в свою очередь, ломает TFileStream.Create
, который раньше работал!
Ваша проблема заключается в флагов fmOpenOrCreate. Сделайте это в 3 шага: проверьте, существует ли файл, если не сделать fmCreate. закрыть файл, открыть сейчас для readwrite и второй раз только для чтения ... – whosrdaddy
@whosrdaddy Нет, это не оно. –
@whosrdaddy, как сказал Дэвид, это не поможет. TFile.Open проверяет существование при передаче в параметре fmOpenOrCreate, поскольку я был – Jason