Как создать поток, сохранить файл без перезаписи существующего? - PullRequest
4 голосов
/ 30 мая 2011

до сих пор, когда бы я ни захотел создать файл без перезаписи существующего, я делал что-то вроде этого:

if not FileExists(filename) then
  stream := TFileStream.Create(filename, fmCreate);

Но это не потокобезопасно.Поэтому сейчас я ищу безопасную версию.

Возможно, я смогу объединить некоторые режимы так, чтобы TFileStream.Create(filename, fmCreate +fm???); не сработал, если файл существует?

Мне это нужно для связи блокировок каталогов со старыми программами DOS.Но программы DOS не держат файлы открытыми.: - (

Ответы [ 2 ]

7 голосов
/ 30 мая 2011
Конструктор на основе имени файла

TFileStream использует вызов API WIndows CreateFile для создания дескриптора файла, который будет использоваться для доступа к файлу. Сам API имеет несколько параметров, и особенно интересным для вас является Создать расположение : если вы укажете CREATE_NEW, функция завершится ошибкой, если файл уже существует. Вы можете воспользоваться этим, вызвав CreateFile самостоятельно, а затем используя возвращенный дескриптор для создания TFileStream. Вы можете сделать это, потому что TFileStream наследует от THandleStream, наследует свой конструктор на основе дескриптора и владеет дескриптором (вызывает CloseHandle на дескрипторе, который вы передаете конструктору).

Поскольку это зависит от предоставляемой ОС функции CreateFile, она будет безопасна с точки зрения безопасности (без условия гонки между FileExists() и фактическим созданием файла. Она также блокирует доступ старого приложения к вновь созданному файлу до вы на самом деле закрываете ручку.

var FH: NativeUInt;

// ...

  FH := CreateFile('FileName', GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_NEW, 0, 0);
  if FH = INVALID_HANDLE_VALUE then
  begin
    // Creating the new file failed! I'm raizing an exception, but you can do something
    // better-suited for your case, like trying a new file name.
    RaiseLastOSError;
  end;
  // Create the stream using the prepared Handle
  HS := TFileStram.Create(FH);
4 голосов
/ 30 мая 2011

Я бы сохранил проверку FileExists, потому что она обрабатывает большинство случаев, не полагаясь на обработку исключений. Для граничных случаев вы должны правильно обрабатывать исключения в конструкторе TFileStream. Второй поток, пытающийся создать файл, потерпит неудачу, если вы используете его в режиме общего доступа fmShareDenyWrite.

...