TFileStream. Кроссплатформенно открыть файл [РЕШЕНО]

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

TFileStream. Кроссплатформенно открыть файл [РЕШЕНО]

Сообщение MageSlayer » 14.02.2010 22:20:01

Хай all

Бьюсь над задачкой:
Как _кроссплатформенно_ открыть файл лога на запись, а остальным процессам дать возможность читать его, пока он еще не закрыт.
Ну и естественно, файл лог должен сам создаваться, если еще не был создан и урезаться до нулевой длины при открытии на запись.
Хочется сделать без лишних хаков - и никак :(

Ниже варианты и результаты.
can't read - одновременно файл лога читать не получается
file absent - не создается файл, если уже не создан.

Код: Выделить всё
//linux - ok. win32 - can't read
  F:=TFileStream.Create( GlobalConfig.DebugLogFile,
                         fmCreate or
                         fmOpenWrite or
                         fmShareDenyWrite );
                         
  //linux - ok. win32 - can't read
  F:=TFileStream.Create( GlobalConfig.DebugLogFile,
                         fmCreate or
                         fmShareDenyWrite );

  //linux - ok. win32 - can't read
  F:=TFileStream.Create( GlobalConfig.DebugLogFile,
                         fmCreate or
                         fmOpenReadWrite or
                         fmShareDenyWrite );

  //linux - file absent. win32 - file absent
  F:=TFileStream.Create( GlobalConfig.DebugLogFile,
                         fmOpenReadWrite or
                         fmShareDenyWrite );


Может я не понимаю чего-то?

И еще - может есть какой-нибудь способ сбросить буфер TFileStream не убивая класс? Я имею ввиду по типу старого доброго Flush(var f:TextFile) ?

Спасибо.
Последний раз редактировалось MageSlayer 04.05.2011 21:42:24, всего редактировалось 1 раз.
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение Odyssey » 15.02.2010 00:39:18

fmCreate or XXX = fmCreate, см. http://forum.vingrad.ru/forum/act-ST/f- ... 75810.html
Я бы сделал с помощью FileExists, т.е. если exists то вызывал бы с "fmOpenWrite or fmShareDenyWrite", а если нет, то с fmCreate. Правда тогда получается что установить режим совместного доступа при fmCreate не удастся. Странно :(

И еще - может есть какой-нибудь способ сбросить буфер TFileStream не убивая класс? Я имею ввиду по типу старого доброго Flush(var f:TextFile) ?

В документации об этом ничего нет.

Может быть, имеет смысл создать собственный, более гибкий класс на базе элементарных кроссплатформенных функций, используя код TFileStream как подсказку?
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение MageSlayer » 15.02.2010 10:34:58

Odyssey писал(а):Правда тогда получается что установить режим совместного доступа при fmCreate не удастся. Странно :(

Да. Именно в этом и проблема.
Вопрос пока открыт.

И еще - может есть какой-нибудь способ сбросить буфер TFileStream не убивая класс? Я имею ввиду по типу старого доброго Flush(var f:TextFile) ?

В документации об этом ничего нет.

Может быть, имеет смысл создать собственный, более гибкий класс на базе элементарных кроссплатформенных функций, используя код TFileStream как подсказку?[/quote]

Ну, видно что придется :)
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение alexs » 15.02.2010 20:22:26

Я использую вот такой код - обычно он меня не подводит. Единственный недостаток - нагрузка открытие/закрытие файла при массовой записе в лог:
Код: Выделить всё
procedure WriteLog(const S: string);
var
  F:TextFile;
begin
  Assign(F, LogFileName);
  if FileExists(LogFileName) then
    Append(F)
  else
    Rewrite(F);
  Writeln(F,S);
  CloseFile(F);
end;
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение MageSlayer » 15.02.2010 21:06:11

alexs писал(а):Я использую вот такой код - обычно он меня не подводит. Единственный недостаток - нагрузка открытие/закрытие файла при массовой записе в лог


Собственно, как раз в случае TextFile переоткрывать файл необязательно. Достаточно делать Flush. Но этот вариант, как раз проблему с просмотром под Виндой, не решает. Фактически, это вариант, который раньше я и использовал (с TextFile).
Я и перешел на TFileStream в надежде, что можно нормально "расшарить" файл без хаков.

Вообще-то все больше склоняюсь к тому, что это баг/фича.
Вопрос все-еще открыт.
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение alexs » 15.02.2010 21:30:11

закрываю/открываю файл из-за того, чтобы не держаь долго лог откртым (просто обём информации, поступающей в логе) не очень большой.
По виндой такой файл прекрасно смотрится - проверено не раз через FAR.
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение alexrayne » 16.02.2010 00:57:49

WinAPI имеет для етого FlushFileBuffers(handle). если вы пользуете TFileStream - то он наследник от THandleStream вроде, handle можно взять из его свойств.

Добавлено спустя 4 минуты 6 секунд:
вот еще чего я нарыл по поводу совместимости для расшаривания. в дельфе и фрюхе оно одинаково - конструктор TFileStream.Create перегружен:
constructor TFileStream.Create(const AFileName: string; Mode: Word);
constructor TFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
2й вариант позволяет передать в Rights права доступа при создании, а 1й вариант нет, как их не комбинируй. сам на етом подскользнулся.
alexrayne
постоялец
 
Сообщения: 125
Зарегистрирован: 03.12.2008 16:56:26

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение Alexx2000 » 17.02.2010 01:36:23

MageSlayer писал(а):И еще - может есть какой-нибудь способ сбросить буфер TFileStream не убивая класс? Я имею ввиду по типу старого доброго Flush(var f:TextFile) ?

Я для этого использую вот такую функцию:
Код: Выделить всё
function FileFlush(Handle: THandle): Boolean; 
{$IFDEF MSWINDOWS}
begin
  Result:= FlushFileBuffers(Handle);
end;
{$ELSE} 
begin
  Result:= (fpfsync(Handle) = 0);
end; 
{$ENDIF}

Вызываю примерно так:
Код: Выделить всё
var
  fs: TFileStream;
............
// пишем
............
  FileFlush(fs.Handle);
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 489
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение MageSlayer » 17.02.2010 10:40:50

alexrayne писал(а):WinAPI имеет для етого FlushFileBuffers(handle). если вы пользуете TFileStream - то он наследник от THandleStream вроде, handle можно взять из его свойств.

Добавлено спустя 4 минуты 6 секунд:
вот еще чего я нарыл по поводу совместимости для расшаривания. в дельфе и фрюхе оно одинаково - конструктор TFileStream.Create перегружен:
constructor TFileStream.Create(const AFileName: string; Mode: Word);
constructor TFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
2й вариант позволяет передать в Rights права доступа при создании, а 1й вариант нет, как их не комбинируй. сам на етом подскользнулся.


Чего я не понимаю, причем здесь права доступа? Они тоже влияют на расшаривание файла?

Alexx2000 писал(а):
MageSlayer писал(а):И еще - может есть какой-нибудь способ сбросить буфер TFileStream не убивая класс? Я имею ввиду по типу старого доброго Flush(var f:TextFile) ?

Я для этого использую вот такую функцию:
Код: Выделить всё
function FileFlush(Handle: THandle): Boolean; 
{$IFDEF MSWINDOWS}
begin
  Result:= FlushFileBuffers(Handle);
end;
{$ELSE} 
begin
  Result:= (fpfsync(Handle) = 0);
end; 
{$ENDIF}

Вызываю примерно так:
Код: Выделить всё
var
  fs: TFileStream;
............
// пишем
............
  FileFlush(fs.Handle);


Во. За это спасибо. Было бы классно эту функцию положить в сам класс TFileStream.
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение alexrayne » 17.02.2010 23:48:41

Чего я не понимаю, причем здесь права доступа? Они тоже влияют на расшаривание файла?

именно так. об етом я и говорю.
короткая версия конструктора непозволяла передать права доступа для создаваемого файла (как ранее говорили с чем FFFF ни орь - получиш один ....), и насколько я помню ети права даже для олткрываемых файлов не передавались далее в систему в системный вызов винАПИ
вторая длинная версия конструктора уже нормально передает права доступа при вызове АПИшных функций, и она работает.

Добавлено спустя 2 минуты 13 секунд:
вобчем попробуйте ваш изначальный пример чуть поправить
/linux - ok. win32 - can't read
F:=TFileStream.Create( GlobalConfig.DebugLogFile,
fmCreate or
fmOpenWrite, //запятую тут
fmShareDenyWrite );

//linux - ok. win32 - can't read
F:=TFileStream.Create( GlobalConfig.DebugLogFile,
fmCreate, //запятую тут
fmShareDenyWrite );

//linux - ok. win32 - can't read
F:=TFileStream.Create( GlobalConfig.DebugLogFile,
fmCreate or
fmOpenReadWrite, //запятую тут
fmShareDenyWrite );

//linux - file absent. win32 - file absent
F:=TFileStream.Create( GlobalConfig.DebugLogFile,
fmOpenReadWrite, //запятую тут
fmShareDenyWrite );
alexrayne
постоялец
 
Сообщения: 125
Зарегистрирован: 03.12.2008 16:56:26

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение MageSlayer » 18.02.2010 23:22:10

alexrayne писал(а):Добавлено спустя 2 минуты 13 секунд:
вобчем попробуйте ваш изначальный пример чуть поправить
/linux - ok. win32 - can't read
F:=TFileStream.Create( GlobalConfig.DebugLogFile,
fmCreate or
fmOpenWrite, //запятую тут
fmShareDenyWrite );


Нет, не работает. В Rights (третьем аргументе) передаются именно права доступа. В примере выше, файл создается с правами на чтение только для группы :)
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение fl@nker » 02.02.2011 11:07:06

О как, прошёл годик и я наступил на те же грабли:
F:=TFileStream.Create( GlobalConfig.DebugLogFile, fmCreate or fmOpenWrite, fmShareDenyWrite );
Win32 - не работает ... почему, может потому, что:
внутри вызывается
FHandle:=FileCreate(AFileName,Rights)
в тоже время в sysutils:
Код: Выделить всё
Function FileCreate (Const FileName : String) : THandle;
Var
  FN : string;
begin
  FN:=FileName+#0;
  Result := CreateFile(@FN[1], GENERIC_READ or GENERIC_WRITE,
                       0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
end;

Function FileCreate (Const FileName : String; Mode:longint) : THandle;
begin
  FileCreate:=FileCreate(FileName);
end;
fl@nker
незнакомец
 
Сообщения: 8
Зарегистрирован: 23.06.2009 11:31:12

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение Odyssey » 02.02.2011 12:01:40

См. http://www.freepascal.org/docs-html/rtl ... reate.html
В вольном переводе:
Необязательный параметр Mode работает только в unix, и позволяет установить права доступа (флаги read, write, execute, sticky bit, setgid и setuid) создаваемого файла. На других платформах параметр Mode игнорируется.
Odyssey
энтузиаст
 
Сообщения: 580
Зарегистрирован: 29.11.2007 17:32:24

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение Mr.Smart » 02.02.2011 12:22:05

fl@nker замечу, что выражение fmCreate or fmOpenWrite вообще не имеет никакого смысла!
Если посмотреть значения констант, всё будет понятно:
Код: Выделить всё
const
  fmCreate        = $FFFF;
  fmOpenRead      = 0;
  fmOpenWrite     = 1;
  fmOpenReadWrite = 2;
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: TFileStream. Кроссплатформенно открыть файл

Сообщение Maxizar » 02.02.2011 12:27:49

Стаю, на этих же граблях. Задача: Иметь возможность прочитать файл, который уже используется. А именно: прочитать файл типа wave, который в данный момент может использоваться, скажем проигрывается плеером. Более всего удобно воспользоваться TFileStream потому что он, позволяет перемещаться по файлу от бита к биту. Лично мне нужно перебрать все Чанки (Chank) из которых состоит музыкальный файл в поисках нужного.
Вот код, который работает при условии что файл больше никто не использует.
Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
  var
  f             : TFileStream;
begin
if OpenDialog1.Execute then
   Try
     f := TFileStream.Create(OpenDialog1.FileName,fmOpenRead, fmShareCompat); //уже вылетает при проигрывании файла плеером.
     f.Seek(0, soFromBeginning);
     //Выполняем нужные действия
   Finally
     f.Free;
   End;
end;

Если же файл проигрывается ну скажем AIMP-ом то вылетает ошибка:
Исключение : External: SIGSEGV

Но ведь скажем программы Audacity спокойно открывает файл при проигрывании его AIMP-ом. Как мне сделать подобное.
Мне даже его изменять не нужно, Только прочитать.
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

Рейтинг@Mail.ru