ホームに戻る
 12、ディスク管理

APIによるファイル管理はCのスタンダードライブラリと異なる。
まずテキストモードが存在しないので、
もし改行を出力する場合は \r\n としなければならない。
APIではファイルの同時アクセスが制御できる。
例えば複数のプログラムが同時に1つのファイルに
アクセスできないようにすることができる。
これは読み込み、書き込みごとに指定できる。
APIは読み取り専用などのファイル属性の管理ができる。

ディスクを管理する最小単位はセクタである。
読み込みや書き込みに際しても必ずセクタ単位で行われる。
例えば1バイト単位での読み込みを行ったとしても、
実際はセクタ単位でメモリ上のバッファに読み込まれたものから
APIが読み出すことになる。
よって1バイトの読み込みを連続的に行ったとしても、
そのたびにディスクアクセスされるわけではない。

もし読み込みであれば問題は無いが、
書き込みの場合はバッファを使うことで問題が起こる。
例えば通常の書き込みはバッファに蓄えられ、
バッファへの書き込みがセクタ単位になった場合にディスクに書き込まれるが、
もしバッファにあるうちに電源が切られた場合に、
ディスクへの書き込みが行われないうちに終了してしまう。
対策方法としてはファイルのオープン時に、
FILE_FLAG_NO_BUFFERING を指定してバッファを使わないようにするか、
FlushFileBuffers を用いて定期的にバッファからディスクに書き込む。
FILE_FLAG_NO_BUFFERING を指定する場合は、
書き込む量をセクタ単位にするなどの制限がある。

ファイルの書き込みはアプリケーションを閉じる際に自動的に
バッファの内容をディスクに書き込むようになっているが、
もしここで書き込みエラーがあった場合に対応できない。
ファイルを閉じる際には必ず CloseHandle を用いるようにし、
バッファの内容をディスクに書き込むようにする。
もちろん CloseHandle でエラーの場合は 0 が返るので
その場合には書き込みエラーに対処しなければならない。

HANDLE CreateFile(
  LPCTSTR lpFileName,                         // ファイル名
  DWORD dwDesiredAccess,                      // アクセスモード
  DWORD dwShareMode,                          // 共有モード
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // セキュリティ記述子
  DWORD dwCreationDisposition,                // 作成方法
  DWORD dwFlagsAndAttributes,                 // ファイル属性
  HANDLE hTemplateFile                        // テンプレートファイルのハンドル
);

dwDesiredAccess:GENERIC_READ、GENERIC_WRITE
dwShareMode:FILE_SHARE_READ、FILE_SHARE_WRITE
dwCreationDisposition
CREATE_NEW:ファイルを新たに作成する。存在する場合は失敗。
CREATE_ALWAYS:ファイルが無ければ作成し、あれば上書きする。
OPEN_EXISTING:ファイルが存在しない場合は失敗。
dwFlagsAndAttributes:
FILE_ATTRIBUTE_ARCHIVE:アーカイブ
FILE_ATTRIBUTE_HIDDEN:隠しファイル
FILE_ATTRIBUTE_NORMAL:属性を指定しない。
FILE_ATTRIBUTE_READONLY:読み込み専用

成功時にはハンドルが返る、失敗時には INVALID_HANDLE_VALUE が返る。

DWORD SetFilePointer(
    HANDLE hFile,          // ファイルハンドル
    LONG   lDistance,      // 下位オフセット
    PLONG  pDistanceHigh,  // 上位オフセットの変数
    DWORD  dwMoveMethod    // 移動開始点
);

dwMoveMethod:
FILE_BEGIN:先頭の位置
FILE_CURRENT:現在の位置
FILE_END:終端の位置

BOOL ReadFile(
  HANDLE hFile,                // ファイルのハンドル
  LPVOID lpBuffer,             // データバッファ
  DWORD nNumberOfBytesToRead,  // 読み取り対象のバイト数
  LPDWORD lpNumberOfBytesRead, // 読み取ったバイト数
  LPOVERLAPPED lpOverlapped    // オーバーラップ構造体のバッファ
);

lpOverlapped:非同期I/O用。通常は NULL。

戻り値は、
非同期で無い時成功時は 0 で無い数字、失敗時は 0 を返す。
EOF 時には 0 で無い数字を返し、読み取ったバイト数も 0 である。

BOOL WriteFile(
  HANDLE hFile,                    // ファイルのハンドル
  LPCVOID lpBuffer,                // データバッファ
  DWORD nNumberOfBytesToWrite,     // 書き込み対象のバイト数
  LPDWORD lpNumberOfBytesWritten,  // 書き込んだバイト数
  LPOVERLAPPED lpOverlapped        // オーバーラップ構造体のバッファ
);

lpOverlapped:非同期I/O用。通常は NULL。

戻り値は、
非同期で無い時成功時は 0 で無い数字、失敗時は 0 を返す。

BOOL FlushFileBuffers(
  HANDLE hFile  // ファイルのハンドル
);

戻り値は、
成功時は 0 で無い数字、失敗時は 0 を返す。

BOOL CloseHandle(
  HANDLE hObject   // オブジェクトのハンドル
);

戻り値は、
成功時は 0 で無い数字、失敗時は 0 を返す。

 非同期I/O

ファイル処理は同期I/Oの場合、
読み込み、書き込みについて終わるまで処理を待つ。
ただファイルの処理はその間CPU資源を無駄にするため、
非同期で同時に処理を行うことができる。

まずイベントオブジェクトを作成。

CreateEvent(NULL, TRUE, FALSE, NULL);

成功時はイベントハンドル、失敗時は 0 を返す。
次に CreateFile でファイルをオープン。
dwFlagsAndAttributes に FILE_FLAG_OVERLAPPED を指定。
OVERLAPPED 構造体に値をセット。
OVERLAPPED.Offset と OVERLAPPED.OffsetHigh にファイル位置を指定する。
OVERLAPPED.hEvent でさきほど作成したイベントハンドルを指定。
ReadFile か WriteFile の lpOverlapped に
OVERLAPPED 構造体のアドレスを渡す。
すぐにファイル処理が終われば TRUE を返す。
ファイルの処理が終わらない場合と失敗の場合は FALSE となる。
ファイルの処理中と失敗の区別は、
GetLastError が ERROR_IO_PENDING を返せば正常に処理中である。
その後は GetOverlappedResult で終了を判別する。

BOOL GetOverlappedResult(
  HANDLE hFile,                       // ファイル、パイプ、通信デバイスのハンドル
  LPOVERLAPPED lpOverlapped,          // オーバーラップ構造体
  LPDWORD lpNumberOfBytesTransferred, // 転送されたバイト数
  BOOL bWait                          // 待機オプション
);

lpOverlapped はオーバーラップ構造体のポインタ。
lpNumberOfBytesTransferred は転送バイトを受け取る変数のポインタ。
bWait は FALSE で終了を調べてすぐ処理を返し、
TRUE はファイル処理が終わるまでこのまま待ちます。
処理が終わると TRUE を返します。
非同期処理の失敗は GetOverlappedResult 後の
GetLastError にて ERROR_IO_IMCOMPLETE を返す。

終了時にはファイルハンドルを閉じ、イベントハンドルを閉じる。

inserted by FC2 system