テキストへの二重アクセス

解決


駆け出し  2005-08-12 17:32:55  No: 17023  IP: 192.*.*.*

いつもお世話になっております。
下記のような感じで、テキストにログを書いていますが、
二重起動させると、同じテキストを開く仕様の為、
「I/O エラー (32)」が出てしまいます。
出来れば「Writeln」を使い、同じテキストに書き込みたい
のですが、なにか良い解決方法はないでしょうか?
よろしくお願いします。

var
  LogFH : TextFile; //ログ用
begin
  AssignFile(LogFH, strLogFile);
  if FileExists(strLogFile) then
     Append(LogFH)   //ログ追加
  else
     Rewrite(LogFH); //ログ新規
  Writeln(LogFH, 'ログ内容');
  CloseFile(LogFH);
end;

編集 削除
メラトニン  2005-08-12 20:31:55  No: 17024  IP: 192.*.*.*

同じファイルに同時にアクセスはできません。
IOエラーが出たら少し時間を置いて書き込みを再挑戦するくらいしかないと思います。

編集 削除
あえて  2005-08-12 21:57:06  No: 17025  IP: 192.*.*.*

アプリを二重起動させて同じファイルを開きたいなら、FileOpen のような Pascalネイティブでないファイル変数ハンドラを使いましょ。
FileHandle := FileOpen(strLogFile, fmOpenReadWrite or fmShareDenyNone);
※PascalネイティブなTFileStreamを使っても可。

編集 削除
moro  2005-08-13 00:30:55  No: 17026  IP: 192.*.*.*

ファイル末にログを追加するだけなら、リダイレクトの">>"が使えるかも。

それでだめなら、
ほかからの書き込みを禁止するモードでファイルを開く。
ファイルのオープンに失敗した場合は、他のアプリが書き込み中と判断して書き込みをしない。
代わりにSleep()で一定時間待機してから再度書き込みを行う。

できれば、ミューテックスを使って同期処理を入れたほうがいいけど初心者には難しい。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/jpdndllpro/htm/metrsect.asp

編集 削除
deldel  2005-08-13 08:42:57  No: 17027  IP: 192.*.*.*

.iniファイルなら同時にアクセスできますが、
これはこれで、色々と(データ設計など)工夫が要りそうですね・・・

編集 削除
駆け出し  2005-08-15 13:05:59  No: 17028  IP: 192.*.*.*

メラトニンさん、あえてさん、moroさん、deldelさん、ありがとうございます。
AssignFile・Append・Writelnでは、やはり不可能なんですね。
仕方ないので、ログのデータを保持して、プログラム最後に一気に書き込むようにしようかと思います。

1)「CreateFile・SetFilePointer・WriteFile・CloseHandle」を使い、「CreateFile・SetFilePointer」の時点で処理1を止め、処理2が同じ行動をとると、ログファイルの内容が破壊されてしまいます。ファイルポインタは終点を指定していますが、その辺あたりのことでしょうか・・・

2)以下のコードだと、うまく書き出せません(空のファイルが出来上がってしまう)。

function WriteData(strFileName: string):Boolean;
var
  FileHandle : Integer;
  chrBuf: array[0..2048] of char; //文字列データ(関数呼び出し用途)
begin
  //ファイル存在確認
  if not FileExists(strIniFile) then
    FileCreate(strFileName)
  else
    FileHandle := FileOpen(strFileName, fmOpenWrite);
  //書き込み
  if FileHandle > 0 then begin
     StrCopy(chrBuf, PChar(strLogBuf));
     FileWrite(FileHandle, chrBuf[0], SizeOf(strLogBuf));
  end;
  //ファイルクローズ
  FileClose(FileHandle);
end;

3)TStrings.SaveToFileを使うと、以前のログが消え、新規のファイルにログが残ってしまう。

1〜3のうち、どれかだけでも解決すればよいのですが><
ぼすけて・・・

編集 削除
駆け出し  2005-08-15 13:14:37  No: 17029  IP: 192.*.*.*

間違えて 2)をまんま貼り付けてしまいましたが、strLogBufは外でstring型で宣言しています。
また、FileWrite(FileHandle, chrBuf[0], SizeOf(strLogBuf));は
FileWrite(FileHandle, chrBuf, SizeOf(strLogBuf));の間違いです。
すみません。

編集 削除
ぼすけたいけど...  2005-08-15 15:23:10  No: 17030  IP: 192.*.*.*

>ぼすけて・・・
ぼすけてあげてもいいけど、やめとく。
2)のコードが期待通りに動かないのは、コードに間違いがあるから。
どこが間違っているかを自力で見つけ出せれば「駆け出し」から半歩前進。

編集 削除
駆け出し  2005-08-15 18:27:49  No: 17031  IP: 192.*.*.*

FileWriteの記述間違いかと思って、そっちばかり気を配っていました。
FileHandle:= FileCreate(strFileName);
ハンドル受け取ってなかったですね・・・  ぼす。

function WriteData(strFileName: string):Boolean;
var
  FileHandle : Integer;
begin
  //ファイル存在確認
  if not FileExists(strIniFile) then
    FileHandle:= FileCreate(strFileName)
  else
    FileHandle := FileOpen(strFileName, fmOpenWrite);
  //書き込み
  if FileHandle > 0 then begin
     FileWrite(FileHandle, PChar(strLogBuf)^, length(strLogBuf));
  end;
  //ファイルクローズ
  FileClose(FileHandle);
end;

今後のために1)と3)の不具合の原因を勉強できたらと思うんですが、なんかヒントいただけませんか?
いろいろ調べてるんですが、調べる為のキーワードがわからなくて困っております。
3)は解決不可ですかねぇ・・・

編集 削除
駆け出し  2005-08-15 18:48:21  No: 17032  IP: 192.*.*.*

FileExists(strFileName)
今日は間違いだらけで危険なので、しっかり寝てから作り直したいと思います。
2)も結局追記になっていませんでした・・・

編集 削除
駆け出し  2005-08-15 19:31:16  No: 17033  IP: 192.*.*.*

後は自力で解決したいと思います。
お騒がせしました・・・

編集 削除
果報は寝ず待て  2005-08-15 20:19:57  No: 17034  IP: 192.*.*.*

>後は自力で解決したいと思います。
その心がけスバラシイ!

function WriteData(const strFileName, strLogBuf: string): Boolean;
var
  FileHandle : Integer;
begin
  result := False;
  if not FileExists(strFileName) then
    FileHandle := FileCreate(strFileName)
//  FileHandle := CreateFile(PChar(strFileName), GENERIC_WRITE, FILE_SHARE_WRITE, nil,
//    {OPEN_ALWAYS} CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
  else
    FileHandle := FileOpen(strFileName, fmOpenWrite or fmShareDenyNone);
  if FileHandle > 0 then begin
    // ここで停止しておけば、二重起動した別アプリがファイルに追加書き込みしても大丈夫
    SetFilePointer(FileHandle, 0, nil, FILE_END);       // ポインタをファイル末尾に
    // ここで停止させると、二重起動した別アプリがファイルに追加した時点でポインタが末尾からずれる
    FileWrite(FileHandle, PChar(strLogBuf)^, Length(strLogBuf));    // 追加書き込み
    result := True;
  end;
  FileClose(FileHandle);
end;

編集 削除
駆け出し  2005-08-16 11:06:57  No: 17035  IP: 192.*.*.*

果報は寝ず待てさん、ありがとうございます。
先ほどFileSeekで解決したのですが、SetFilePointerを使えば良かったんですね。
DelphiとAPIを混ぜるという発想がありませんでした・・・

> // ここで停止させると、二重起動した別アプリがファイルに追加した時点でポインタが末尾からずれる
二度末尾にセットされるとずれてしまうんですね。
1)がだめだったのも、それが原因なんですね。
なんとか書き込みのタイミングを計れるように、工夫したいと思います。
ありがとうございます!

編集 削除