Last Updated 2002/01/18
Programming Tips Windows  索 引 
INIファイルのキャッシュ
2002/01/18

C:\MSDOS.SYS の READ ONLY を解除して sample の値を OK が押されるたびに +1 する.
(以下のプログラム)
Win95 上では、+1 されるのですが Win98 上では、全然値が変化しない.
(Win95上でもたまにインクリメントされない場合があるのですがこれも原因不明)
----(1) の行を ----(2) へ移動させると Win98 でも +1 される.

#include 
#include 
#define fn "C:\\MSDOS.SYS"
#define sc "Options"
#define kn  "sample"
int WINAPI WinMain(HINSTANCE hTI,HINSTANCE hPI,LPSTR lpA,int nW)
{
  int count = 0;
  char buff[20];
  do {
    SetFileAttributes(fn,GetFileAttributes(fn) & ~FILE_ATTRIBUTE_READONLY);
    count = GetPrivateProfileInt(sc, kn, 1, fn);
    WritePrivateProfileString(sc, kn, itoa(count + 1, buff, 10), fn);
    WritePrivateProfileString(NULL, NULL, NULL, fn); //write back to ini file
    // ---------(2)
    SetFileAttributes(fn, GetFileAttributes(fn) | FILE_ATTRIBUTE_READONLY);
    count=GetPrivateProfileInt(sc, kn, 1, fn);  //-------------(1)
  } while (MessageBox(NULL, itoa(count, buff, 10), "Increment test", MB_OKCANCEL) == IDOK);
  return 0;
}


インクリメントされない現象はWindows95でもかなり頻繁に発生する.
インクリメントされない現象が発生する時は必ず
  WritePrivateProfileString(sc, kn, itoa(count + 1, buff, 10), fn);
の戻り値がFALSEになっている.
(しかしGetLastError()しても0が返る)

上記を回避する方法は,
"C:\MSDOS.SYS" の ReadOnly属性を解除したあとに
  WritePrivateProfileString(NULL, NULL, NULL, ファイル名);
を実行する.
これで問題は発生しない.

下記は以上のことからの勝手な推測である.

GetPrivateProfile*()にて読み込みを行った場合,指定したファイルの内容が Windows によって自動的にキャッシングされる.
そのキャッシングはファイルの内容だけでなく,ファイルの属性もキャッシングされているのではないか?

(1)"C:\MSDOS.SYS" の ReadOnly属性を解除する
(2)GetPrivateProfileInt()にて値を読み、
WritePrivateProfileString()にて+1した値を書く
(3)WritePrivateProfileString(NULL, NULL, NULL, ファイル名)にてオンメモリのバッファからファイルに書き込ませる
(4)"C:\MSDOS.SYS" に ReadOnly属性をつけて元の状態に戻す
(5)チェックのためもう一度 GetPrivateProfileInt()にて値を読む

上記の処理では,(4)で ReadOnly属性をつけた後 (5)で値を読んでいる.
この(5)の時点で「ReadOnly属性のファイルである」という情報がキャッシングされてしまうのではないか.
その後(1)に戻って ReadOnly属性を解除しても,キャッシングされているメモリ内の情報は「ReadOnly属性のファイルの内容」をキャッシングしている状態のままなのでしょう.
(ここが Windowsのバグくさいですが)
そのため(2)での書き込みに失敗してしまう.

この「自動的にキャッシュされる内容」は,Windowsが適当なタイミングで破棄する.
(5)から(2)の直前までの間に運良く破棄されれば,(2)の段階で再度ファイルから読み込みが行われることになり,この時点で「ReadOnly属性が解除されている」ことを(Windowsが)認識してくれる.
この場合には (2)での書き込みが成功し,結果的に「成功したり失敗したり」という不安定な動作となるのでしょう。

そこで回避策として(1)で ReadOnly属性を解除した直後に,WritePrivateProfileString(NULL, NULL, NULL, ファイル名)を実行する.
これは「未書込のメモリ内の情報をディスクに書かせる」ためではなく,「オンメモリにキャッシュされている情報を無効にする」ために行う.
次回のアクセスで必ずファイルから読み込みが行われることになり,ReadOnly属性のファイルであることを正しく認識させることができる.


参照
WriteProfileString
前後のTips
INIファイルのキャッシュ

DSS ProgrammingTipsCGI Ver2.02