Windows NTで動作するディスクユーティリティを作成する方法

戻る

WindowsNTでデバイスを操作するのは,デバイスドライバを作成する必要があり 大変難しいとされています.しかし,標準でサポートされている基本的なデバイスに 関しては,通常のアプリケーションで操作できるものも多くあります.
ハードディスクやフロッピーディスクも標準でサポートされているデバイスの1つ ですが,普通のアプリケーションはファイルのオープン・クローズで簡単に操作する ことができます.ディスクに記録されている情報のうち,ファイルシステムの管理は OSが行っていますが,それらの情報を直接操作する必要があるときにはもう少し低水準 のインターフェースが必要になります.
ここでは,WindowsNTで低水準のI/O操作を行う方法について考えてみます.

5-1 Windows95の32ビットアプリケーションで低水準のI/O操作を行うには

Windows95/98 の基盤部分はMS-DOS(16ビット)を利用して動作しています.
このため,水準のI/Oを行うにはDOSのシステムコールを,仮想ドライバを介して 呼び出す必要があります.このため,Windows95/98で,IOCTLを実行するには, VWIN32.VXD を呼び出して,Win32 API DeviceIoControl で,INT 21H や ディスクBIOS を呼び出すことができます.(* 拙著 Win32 サブルーチンズ 「Windows95で動作する ディスクユーティリティの作成方法がわかりたい」を参照.)

5-2 WindowsNTの32ビットアプリケーションで低水準I/O操作を行うには

WindowsNTでは,INT13H などのBIOSがなくてもディスクへのアクセスを行うことが できるようになっています.そもそもBIOSは,BASICがI/Oを行うためのシステムコール のような存在でした.しかし,WindowsNTは,ローダー以外のBIOSを持たないMIPSや PowerPC のマシンでも動作させる必要があるため,一般的なPCに依存した機能が なくても動作します.
(* BIOSを利用しないのは,処理速度が低下したりコードが リエントラントなっていないなどの問題があることも要因になっている.)
そこで,BIOSに代わるHAL(Hardware Abstruction Layer)と呼ばれるDLLを持ってい ます.このDLLに,ハードウェアに依存した処理をまとめてもっており,移植性を 高めることができるようになっています.

図5-1:WindowsNTの構造
      +---------------------+
      |  アプリケーション   |
      +---------------------+
             ↑↓
      +-------------------+------------------+--------------------+
      | Win32サブシステム | OS/2サブシステム | POSIX サブシステム |
      +-------------------+------------------+--------------------+
                   |            |                 |
ユーザモード       |            |                 |
-------------------|------------|-----------------|----------------
カーネルモード     ↓            ↓                 ↓
   +---------------------------------------------------------------+
   |                       システムサービス                        |
   +-----------------------------------------------+---------------+
   |            N T  E x e c t i v e               |               |
   |                                               |     I/O       |
   +-----------------------------------------------+  マネージャー |
   |                   カーネル                    |               |
   +-----------------------------------------------+---------------+
   |                          H    A    L                          |
   +---------------------------------------------------------------+

●実際にディスクアクセスするには
WindowsNTにはBIOSがあることが想定されていません.したがって,Windows95/98と 同じ方法で低水準I/Oを実行することができません.このため,それに相当する機能が 実装してあります.

●デバイスをオープンする
ディスクの低水準I/Oを利用するには,Windows95/98の場合と同様に, Win32 API CreateFile でデバイスをオープンします.しかし,"\\.\vwin32"のように 仮想ドライバ名のような特別な名前や,普通のファイルパスを指定するのではなく,
"\\.\X:" (X は A, B ...)
のような形式で指定します.例えば,Aドライブが,フロッピーディスクの場合, "\\.\A:" と指定します.これにより,Aドライブを物理デバイスとして操作することが 可能になります.デバイスがハードディスクのときは,
"\\.\PhysicalDriveX" (X は 0, 1 ...)
のような設定を行います.なお,パーティションを指定する場合は,フロッピー ディスクと同様の書式で,ドライブレターを指定します. なお,Windows95/98で,上記のような指定を行うと CreateFile の戻り値は INVALID_HANDLE_VALUE となり,Win32 API GetLastError で取得できるエラーコードは ERROR_FILE_NOT_FOUND になります.
低水準I/O用に作成したファイルハンドルは,Win32 API ReadFile, WriteFile 以外に, Win32 API DeviceIoControl で利用することができるようになります.

●デバイスを制御する
実際にデバイスを制御するには,デバイスに対してコマンドを送る必要があります. コマンドを送るには,Windows95/98 と同様に,Win32 API DeviceIoControl を 利用します.各制御コード以降に指定する引数は,コードにより異なります. これらのコードを見ると,ディスクアドミニストレーターやフォーマットコマンドが 行っているような低水準I/O制御を行うための機能がほぼそろっていることが 分かります.フロッピーディスクのように,FATでしかフォーマットされないデバイス であれば,これらの機能があれば独自のフォーマットプログラムを作ることも可能 です.

表5-1:Win32 API DeviceIoControl と制御コード表
BOOL DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode,
 LPVOID lpvInBuffer, DWORD dwInBuffer,
 LPVOID lpvOutBuffer, DWORD dwOutBuffer,
 LPDWORD lpdwBytesReturned, LPOVERLAPPED lpoOverlapped)
フォーマットダイアログボックスを表示する

引数
HANDLE hDevice ... デバイスハンドル
DWORD dwIoControlCode ... 実行する操作の制御コード
LPVOID lpvInBuffer ... 入力データのバッファのアドレス
DWORD dwInBuffer ... 入力バッファのサイズ
LPVOID lpvOutBuffer ... 出力バッファのアドレス
DWORD dwOutBuffer ... 出力バッファのサイズ
LPDWORD lpdwBytesReturned ... 出力された実際のバイト数
LPOVERLAPPED lpoOverlapped ... オーバーラップ構造体を指す

戻り値
TRUE 正常終了
FALSE 異常終了

FSCTL_ALLOW_EXTENDED_DASD_IO ファイルシステムドライバに入出力境界チェックを行わせない
FSCTL_DELETE_REPARSE_POINT ファイルあるいはディレクトリのためにReparseポイントを削除する
FSCTL_DISMOUNT_VOLUME ボリュームをディスマウントする
FSCTL_GET_COMPRESSION ファイルあるいはディレクトリの圧縮状態を得る
FSCTL_GET_HFS_INFORMATION インプットハンドルと結び付けられたファイルについての マッキントッシュファインダ情報を返す
FSCTL_GET_REPARSE_POINT ファイルあるいはディレクトリのReparseポイントデータを返す
FSCTL_LOCK_VOLUME ボリュームをロックする
FSCTL_QUERY_ALLOCATED_RANGES ファイルにディスクスペースを割り当てることのできる範囲を得る
FSCTL_QUERY_FAT_BPB FAT16あるいは FAT12ボリュームの最初の36バイトを生成する
FSCTL_READ_COMPRESSION 予約済み
FSCTL_SET_COMPRESSION ファイルあるいはディレクトリの圧縮ステートセットする
FSCTL_SET_REPARSE_POINT ファイルあるいはディレクトリにReparseポイントをセットする
FSCTL_SET_SPARSE ファイルにSparseファイルマークを付ける
FSCTL_SET_ZERO_DATA ファイルのレンジをゼロバイトに設定する
FSCTL_UNLOCK_VOLUME ボリュームのロックを解除する
FSCTL_WRITE_COMPRESSION 予約済み
IOCTL_DISK_FORMAT_TRACKS ディスクの連続したトラックをフォーマットする
IOCTL_DISK_GET_DRIVE_GEOMETRY 物理ディスクのジオメトリに関する情報を取得する
IOCTL_DISK_GET_DRIVE_LAYOUT ディスクの各パーティションに関する情報を取得する
IOCTL_DISK_GET_PARTITION_INFO ディスクのパーティション情報を取得する
IOCTL_DISK_PERFORMANCE ディスクの性能情報を取得する
IOCTL_DISK_REASSIGN_BLOCKS ディスクブロックを予備ブロックプールにマップする
IOCTL_DISK_SET_DRIVE_LAYOUT ディスクのパーティションを設定する
IOCTL_DISK_SET_PARTITION_INFO ディスクのパーティションの種類を設定する
IOCTL_DISK_VERIFY ディスクの特定範囲の論理フォーマットを実行する
IOCTL_SERIAL_LSRMST_INSERT 回線とモデムの状態データをデータストリーム中に 配置できるかどうかを示す
IOCTL_STORAGE_CHECK_VERIFY メディア交換可能デバイスのメディア交換をチェック
IOCTL_STORAGE_EJECT_MEDIA SCSIデバイスからメディアをイジェクトする
IOCTL_STORAGE_GET_MEDIA_TYPES メディアのサポートに関する情報を取得する
IOCTL_STORAGE_LOAD_MEDIA デバイスにメディアをロードする
IOCTL_STORAGE_MEDIA_REMOVAL メディアイジェクト機構を使用可能(不能)にする

5-3 フロッピーディスクの構造

FATでフォーマットされたフロッピーディスクの領域は, IPL(Initial Program Loader), FAT(File Allocation Table), ルートディレクトリ, データ域(ファイル本体とサブディレクトリ)で構成されます. IPLには,BPB(Bios Parameter Block)があり,IOCTL_DISK_GET_DRIVE_GEOMETRY で取得 できる項目はここに保存されています.
Win32 API ReadFile や Win32 API WriteFile で実際の入出力を行う場合, ディスクへのアクセスは論理セクタ番号順にアクセスが行われます. このため,ディスクの物理的なセクタやヘッド数(フロッピーディスクの場合2)を 意識する必要はありません.
フォーマットプログラムを作成する必要があるときには,FSCTL_QUERY_FAT_BPB を 利用して,実際にどのような情報を設定する必要があるかを確認するとよいでしょう. また,Win32 API GetVolumeInformation や Win32 API GetDriveType で得られる情報 では不足しているときには,この制御コードを利用して,ディスクの内容を直接参照 する必要があります.ただし,WindowsNT4.0ではFAT32のサポートはされませんが, Windows2000ではサポートされるのではないかと思われます.
図には,FAT32固有の情報もありますが,FAT32でフォーマットされたディスクの場合 のみ設定されます.この場合,ルートディレクトリエントリのサイズは サブディレクトリと同様に可変長で管理されるようになります.

図5-2:8セクタ/トラックでフォーマットされたフロッピーディスクの論理セクタ
----------+-------------------------+----------------------------
   ヘッド |             0           |             1
----------+-------------------------+----------------------------
   セクタ | 01 02 03 04 05 06 07 08 | 01 02 03 04 05 06 07 08
----------+------------------------------------------------------
          |                   論理セクタ番号
----------+------------------------------------------------------
トラック0 |  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F
        1 |  10 11 12 13 14 15 16 17  18 ........
        2 |  ...
        3 |  ...
        : :
        : :

図5-3:フロッピーディスクの構造
    +-----+------+------+-------------------+---------------------------
    | IPL | FAT1 | FAT2 | ルートディレクトリ| データ ...
    +-----+------+------+-------------------+---------------------------
    |     |
    |     +----------------------------------------------------------------
    |
    +----------------------------------------------------------------------
    |オフセット サイズ     内容
    +----------------------------------------------------------------------
    |  00H       3         IPLへのNEARポインタ
    |  03H       8         OEMバージョン
 ---+----------------------------------------------------------------------
 ↓ |  0BH       2         1セクタあたりのバイト数(bytes/sector)
 BPB|  0DH       1         1アローケーションユニットあたりセクタ数(byte/sect)
    |  0EH       2         予約セクタ数
    |  10H       1         FATの数
    |  11H       2         ルートディレクトリエントリ数
    |  13H       2         論理セクタ数
    |  15H       1         メディアディスクリプタ
    |  16H       2         FATあたりのセクタ数(sector/FAT)
    |  18H       2         トラックあたりのセクタ数(sector/track)
    |  1AH       2         ヘッド数
    |  1CH       2         隠れたセクタ数(下位WORD)
    |  1EH       2         隠れたセクタ数(上位WORD)
    +------- FAT32固有情報 (* フロッピーディスクには存在しない) --------
    |  20H       2         FAT32 トータルセクタ(下位WORD)
    |  22H       2         FAT32 トータルセクタ(上位WORD)
    |  24H       2         FAT32 FATあたりのセクタ数(セクタ/FAT 下位WORD)
    |  26H       2         FAT32 FATあたりのセクタ数(セクタ/FAT 上位WORD)
    |  28H       2         拡張フラグ
    |  2AH       2         ファイルシステムバージョン
    |  2CH       2         ルートディレクトリ開始クラスタ(下位WORD)
    |  2EH       2         ルートディレクトリ開始クラスタ(上位WORD)
    |  30H       2         ファイルシステム情報(BIGFATBOOTFSINFO)セクタ
    |  32H       2         バックアップブートセクタ
    |  34H       2         予約
    +----------------------------------------------------------------------
    |   :                  IPL, BIGFATBOOTFSINFO など
    +----------------------------------------------------------------------

5-4 実際にフロッピーディスクを制御するには

●ドライブをオープンしボリュームをロックする
デバイスを直接操作する訳ですから,必ずディスク(ボリューム)へのアクセスを ロックして,他のアプリケーションから操作できないようにする必要があります. ボリュームをロックするときに,他のアプリケーションが利用している場合は, ロックは失敗し,ERROR_ACCESS_DENIED を返します.

●ディスクの情報を得る
ディスクの情報を取得するには,IOCTL_DISK_GET_DRIVE_GEOMETRY や IOCTL_DISK_GET_MEDIA_TYPES を利用することができます.
これらの制御コードを利用することにより,ディスクのフォーマットされた状態や ディスクドライブがサポートしているメディアを問い合わせることができます. ディスクの情報を得る以外に,フロッピーディスクの物理フォーマットやメディアの イジェクト(* サポートしているデバイスの場合のみ)を行うことができます.
もちろん,セクタ単位でデータの読み出し・書き込みを行うことができます.
●ドライブをクローズする
ボリュームのロックを解除し,オープンしたドライブをクローズします.
<DISK_GEOMETRY構造体>
typedef struct _DISK_GEOMETRY {
   MEDIA_TYPE  MediaType;        メディアタイプ
   LARGE_INTEGER  Cylinders;     シリンダの数
   DWORD  TracksPerCylinder;     シリンダ当たりのトラック数
   DWORD  SectorsPerTrack;       トラック当たりのセクタ数
   DWORD  BytesPerSector;        セクタ当たりのバイト数
} DISK_GEOMETRY ;

表5-2:メディアタイプ
F5_1Pt2_5125.25", 1.2MB, 512バイト/セクタ
F3_1Pt44_5123.5", 1.44MB, 512バイト/セクタ
F3_2Pt88_5123.5", 2.88MB, 512バイト/セクタ
F3_20Pt8_5123.5", 20.8MB, 512バイト/セクタ
F3_720_5123.5", 720KB, 512バイト/セクタ
F5_360_5125.25", 360KB, 512バイト/セクタ
F5_320_5125.25", 320KB, 512バイト/セクタ
F5_320_10245.25", 320KB, 1024バイト/セクタ
F5_180_5125.25", 180KB, 512バイト/セクタ
F5_160_5125.25", 160KB, 512バイト/セクタ
RemovableMediaフロッピー以外の交換可能メディア
FixedMedia固定ハード ディスク メディア

5-5 プログラムについて

WindowsNTで動作する,ハードディスク上にフロッピーディスクのイメージファイルを 作成,復元するツールです.Windows95/98 では動作しません.
フロッピーディスクのコピーをするために作成していますので,MOや PDのような大容量のデバイスでの動作は確認していません.
ディスクイメージをフロッピーディスクに復元するときは,フロッピーディスク の物理フォーマットを必ず行います.

●使い方
>FDFILE sre dest
src ... コピー元のフロッピードライブまたはイメージファイル名
dest ... コピー先のフロッピードライブまたはイメージファイル名

●実行例
>FDFILE a: c:\temp\my_disk.2hd
A:ドライブのディスクイメージを c:\temp\my_disk.2hd にコピーする
>FDFILE c:\temp\my_disk.2hd a:
c:\temp\my_disk.2hd のディスクイメージをA:ドライブのディスクに復元する

●ソースファイル
FDFILE.EXE
FDFILE.C(ソースファイル)
MAKEFILE(メイクファイル)