ファイル操作


ファイルハンドル

Windows は、ANSI C 標準関数よりもより柔軟に扱えるファイル操作用の API を用意しています
これを操作することで、Windows のディスクファイルをプログラムから制御することができます

まず、多くのファイル操作関数はファイルハンドルを必要とします
ファイルハンドルは CreateFile() 関数が生成します
この関数は指定したオブジェクトを作成したり開いたりする高度な関数の一つです
HANDLE CreateFile(
	LPCTSTR lpFileName, 
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
);
lpFileName には作成、またはオープンするファイルの名前(パス)を指定します
dwDesiredAccess は読み取りか書き込みかのアクセスタイプを以下の定数から指定します

定数解説
0 デバイスの属性を問い合わせます
GENERIC_READ 読み取りアクセスです
データの読み取りとファイルポインタの移動ができます
読み書きアクセスをするには、GENERIC_WRITE と組み合わせて指定します
GENERIC_WRITE 書き込みアクセスです
データの書き込みとファイルポインタの移動ができます
読み書きアクセスをするには、GENERIC_READ と組み合わせて指定します

dwShareMode はこのファイルの共有方法を指定します
マルチタスクの Windows はファイルを開いている間も、他のアクセスが考えられます
その時に共有方法を、次の定数のいずれか、または組み合わせで指定します

定数解説
0 このオブジェクトは共有しない
ハンドルがクローズされるまでこのオブジェクトのオープン操作は失敗します
FILE_SHARE_DELETE Windows NT のみ
後続のオープン操作で削除アクセスが要求された場合
そのオープンを許可します
FILE_SHARE_READ 後続のオープン操作で読み取りアクセスが要求された場合
そのオープンを許可します
FILE_SHARE_WRITE 後続のオープン操作で書き込みアクセスが要求された場合
そのオープンを許可します


lpSecurityAttributes は SECURITY_ATTRIBUTES 構造体へのポインタを指定します
この構造体は前回説明したように、ファイルシステムが NTFS の時に有効です

dwCreationDisposition は、ファイルの存在の真偽によってどのように動作するかを
次の定数のいずれかで指定します

定数解説
CREATE_NEW 新しいファイルを作成します
指定ファイルがすでに存在している場合、関数は失敗します
CREATE_ALWAYS 新しいファイルを作成します
指定ファイルがすでに存在している場合、そのファイルは上書きされます
OPEN_EXISTING ファイルをオープンします
指定ファイルが存在していない場合、関数は失敗します
デバイス (コンソールも含む) 上で CreateFile 関数を使う場合は
このフラグを指定しなければなりません
OPEN_ALWAYS ファイルをオープンします
指定ファイルが存在していない場合、関数は新しいファイルを作成します。
TRUNCATE_EXISTING ファイルをオープンし、ファイルのサイズを 0 バイトにします
指定ファイルが存在していない場合、関数は失敗します
dwDesiredAccess パラメータで
少なくとも GENERIC_WRITE を指定しなければなりません

dwFlagsAndAttributes はファイルの属性やフラグを定数で指定します
通常のファイル処理は FILE_ATTRIBUTE_NORMAL (属性なし)だけを指定します
この他、各種属性を指定する定数は下のリファレンスを参照してください

hTemplateFile はテンプレートファイルを指定します
テンプレートファイルは、作成しているファイルに対してファイル属性となどを提供します
Windows 95 では NULL を指定します。ハンドルを指定すると関数は失敗します

関数が成功すると、指定したファイルのハンドルが
関数が失敗すると、INVALID_HANDLE_VALUE が返ります

CreateFile() 関数は一見複雑ですが、これ以外のファイル操作はさほど複雑ではありません
ここで取得したファイルハンドルから各種の情報の取得や読み書きができるようになります
さらに、CreateFile() 関数はコンソールや通信リソースなどを開くこともできます

CreateFile() 関数で開いたオブジェクトハンドルは CloseHandle() で閉じます
この関数は指定されたオブジェクトを無効化し、システムが管理するハンドルカウントを1つ減らします
オブジェクトの最後のハンドルがクローズされると、そのオブジェクトは OS から削除されます

BOOL CloseHandle(HANDLE hObject);

hObject にはクローズするオブジェクトのハンドルを指定します
関数が成功すると 0 以外、失敗すると 0 が返ります
ファイルハンドルが必要なくなれば、この関数でクローズしてください
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	HANDLE hFile;

	hFile = CreateFile(
		lpCmdLine , GENERIC_READ , 0 , NULL ,
		CREATE_NEW , FILE_ATTRIBUTE_NORMAL , NULL
	);
	if (hFile == INVALID_HANDLE_VALUE) {
		MessageBox(
			NULL , TEXT("ファイルを作成できませんでした") ,
			TEXT("エラー") , MB_OK
		);
		return 1;
	}

	CloseHandle(hFile);

	return 0;
}
このプログラムは、コマンドライン引数で得た文字列のファイルを生成します
CREATE_NEW を指定しているので、ファイルがすでに存在している場合は失敗します
成功すれば、カレントディレクトリにファイルが生成されていることを確認できるでしょう


ファイルの情報

上記した方法でファイルハンドルを取得すれば、そのファイルの情報を得られます
既存のファイルから、ファイルの作成日やサイズなどの詳細な情報を取得できるのです

ファイルサイズを取得するには GetFileSize() 関数を使います
ファイルを読み込む時などで、メモリを割り当てる時に必要になるでしょう

DWORD GetFileSize(HANDLE hFile , LPDWORD lpFileSizeHigh);

hFile にはサイズを取得するファイルのハンドルを指定します
ここで指定するハンドルは 、GENERIC_READ
または GENERIC_WRITE アクセスを持っている必要があります

lpFileSizeHigh は、ファイルサイズを得る DWORD 型へのポインタを指定します
ここには、ファイルサイズの上位32ビットが格納されます
そして、戻り値はファイルサイズの下位32ビットが返ります
上位ワードが不用であれば lpFileSizeHight は NULL を指定してもかまいません
関数が失敗すれば 0xFFFFFFFF が返ります
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	HANDLE hFile;
	TCHAR strFileSize[1024];

	hFile = CreateFile(
		lpCmdLine , GENERIC_READ , 0 , NULL ,
		OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL
	);
	if (hFile == INVALID_HANDLE_VALUE) {
		MessageBox(
			NULL , TEXT("ファイルを開けませんでした") ,
			TEXT("エラー") , MB_OK
		);
		return 1;
	}

	wsprintf(strFileSize , "%s = %dByte" , lpCmdLine , GetFileSize(hFile , NULL));
	MessageBox(NULL , strFileSize , TEXT("ファイル情報") , MB_OK);

	CloseHandle(hFile);

	return 0;
}
コマンドラインから取得したファイル名のファイルを開き
そのファイルのサイズ(ただし、4GB以下のファイル)を取得するプログラムです

ファイルの種類を取得するには GetFileType() 関数を使用します

DWORD GetFileType(HANDLE hFile);

hFile にはファイルタイプを取得するファイルハンドルを指定します
この関数は次のいずれかの値を返します

定数解説
FILE_TYPE_UNKNOWN 種類は不明です
FILE_TYPE_DISK ディスクファイルです
FILE_TYPE_CHAR LPT デバイスやコンソールといった文字ファイルです
FILE_TYPE_PIPE 名前付きまたは名前なしパイプです

通常のファイル操作ではディスクファイル以外の値は返しませんが
標準入出力なども扱うプログラムで、ハンドルのタイプを取得するのに使ったりします


削除、コピー、移動

Win32 API はファイルの削除やコピー、移動といった基本動作もサポートします
ファイルの削除は DeleteFile() 関数を使います

BOOL DeleteFile(LPCTSTR lpFileName);

lpFileName には、削除するファイル名を指定します
関数が成功すると 0 以外、失敗すると 0 を返します
存在しないファイルを削除しようとすると、この関数は失敗します
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	if (!DeleteFile(lpCmdLine))
		MessageBox(
			NULL , TEXT("削除できません") ,
			NULL , MB_OK
		);

	return 0;
}
コマンドライン引数で指定したファイルを削除するプログラムです
ファイルが存在しなければエラーになります
重要なファイルは消さないようにファイルの削除には十分注意をはらってください

また、既存のファイルをコピーすることも可能です
ファイルのコピーには CopyFile() 関数を使用します
BOOL CopyFile(
	LPCTSTR lpExistingFileName,
	LPCTSTR lpNewFileName, 
	BOOL bFailIfExists
);
lpExistingFileName にはコピーする既存のファイル名を
lpNewFileName には新しいファイル名を指定します
bFailItExists は、lpNewFileName のファイルがすでに存在していた場合の動作で
TRUE を指定すると関数は失敗、FALSE ならば既存のファイルを上書きします
関数が成功すると 0 以外、失敗すると 0 を返します

ただし、ファイルのセキュリティ属性はコピーされません(既存のファイル属性はコピーされます)
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	int param2;

	for (param2 = 0 ; *(lpCmdLine + param2) ; param2++)
		if (*(lpCmdLine + param2) == ' ') break;

	*(lpCmdLine + param2) = 0;
	param2++;

	if (!CopyFile(lpCmdLine , lpCmdLine + param2 , TRUE))
		MessageBox(
			NULL , TEXT("ファイルをコピーできません") ,
			NULL , MB_OK
		);

	return 0;
}
このプログラムは、コマンドライン引数を二つ受け取ります
第一コマンドライン引数のファイルを第二コマンドライン引数のファイル名でコピーします
この時、引数の分割処理は一文字の空白 ' ' で引数を分けています
空白部に NULL 文字を挿入し、その直後にオフセットアドレス param2 をセットしています

ファイルやディレクトリの移動は MoveFile() 関数で行います
この関数は、ディレクトリの移動も可能としています

BOOL MoveFile(LPCTSTR lpExistingFileName , LPCTSTR lpNewFileName);

lpExistingFileName には移動させるファイル、またはディレクトリ名を
lpNewFileName には移動先の新しいファイル名、またはディレクトリ名を指定します
関数が成功すれば 0 以外、失敗すれば 0 が返ります

ファイルの場合は移動先のドライブ名を指定することも可能ですが
ディレクトリの場合はドライブは指定できません
なぜならば、ファイルは物理的にデータを転送するのですが
ディレクトリの場合、この関数は名前を変更して論理的に移動するだけで
ファイルとは異なり物理的に移動するわけではないためです


CreateFile()

HANDLE CreateFile(
	LPCTSTR lpFileName, 
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
);
ファイルを作成、または開きます

lpFileName - 作成、またはオープンするファイル名を指定します
dwDesiredAccess - 読み取りか書き込みかのアクセスタイプを指定します
dwShareMode - 共有方法を指定します
lpSecurityAttributes - SECURITY_ATTRIBUTES 構造体へのポインタを指定します
dwCreationDisposition - ファイルの存在の真偽によっての動作を指定します
dwFlagsAndAttributes - ファイルの属性やフラグを指定します
hTemplateFile - テンプレートファイルを指定します

戻り値 - ファイルのハンドル。失敗すると INVALID_HANDLE_VALUE

dwDesiredAccess は以下の定数を組み合わせて指定します

定数解説
0 デバイスの属性を問い合わせます
GENERIC_READ 読み取りアクセスです
データの読み取りとファイルポインタの移動ができます
読み書きアクセスをするには、GENERIC_WRITE と組み合わせて指定します
GENERIC_WRITE 書き込みアクセスです
データの書き込みとファイルポインタの移動ができます
読み書きアクセスをするには、GENERIC_READ と組み合わせて指定します

dwShareMode は次の定数のいずれか、または組み合わせで指定します

定数解説
0 このオブジェクトは共有しない
ハンドルがクローズされるまでこのオブジェクトのオープン操作は失敗します
FILE_SHARE_DELETE Windows NT のみ
後続のオープン操作で削除アクセスが要求された場合
そのオープンを許可します
FILE_SHARE_READ 後続のオープン操作で読み取りアクセスが要求された場合
そのオープンを許可します
FILE_SHARE_WRITE 後続のオープン操作で書き込みアクセスが要求された場合
そのオープンを許可します

dwCreationDisposition は以下の定数のいずれかを指定します

定数解説
CREATE_NEW 新しいファイルを作成します
指定ファイルがすでに存在している場合、関数は失敗します
CREATE_ALWAYS 新しいファイルを作成します
指定ファイルがすでに存在している場合、そのファイルは上書きされます
OPEN_EXISTING ファイルをオープンします
指定ファイルが存在していない場合、関数は失敗します
デバイス (コンソールも含む) 上で CreateFile 関数を使う場合は
このフラグを指定しなければなりません
OPEN_ALWAYS ファイルをオープンします
指定ファイルが存在していない場合、関数は新しいファイルを作成します。
TRUNCATE_EXISTING ファイルをオープンし、ファイルのサイズを 0 バイトにします
指定ファイルが存在していない場合、関数は失敗します
dwDesiredAccess パラメータで
少なくとも GENERIC_WRITE を指定しなければなりません

dwFlagsAndAttributes は以下の定数を組み合わせて指定します
ただし、FILE_ATTRIBUTE_NORMAL 属性は単独で指定します

定数解説
属性
FILE_ATTRIBUTE_ARCHIVE アーカイブファイルです
この属性は、ファイルのバックアップや
削除のためのマークとして使われます
FILE_ATTRIBUTE_ COMPRESSED 圧縮ファイルまたは圧縮ディレクトリです
圧縮ディレクトリ内に新しく作られるファイル
またはディレクトリは
デフォルトで圧縮状態になります
FILE_ATTRIBUTE_HIDDEN 隠しファイルです
FILE_ATTRIBUTE_NORMAL とくに属性はありません
単独で指定します
FILE_ATTRIBUTE_OFFLINE ファイルのデータは、すぐには利用できません
ファイルのデータが別のオフラインの
記憶装置に移されていることを示します
FILE_ATTRIBUTE_READONLY 読み取り専用です
FILE_ATTRIBUTE_SYSTEM オペレーティングシステムのファイルの一部です
または、オペレーティングシステム専用のファイルです
FILE_ATTRIBUTE_TEMPORARY テンポラリファイルです
フラグ
FILE_FLAG_WRITE_THROUGH キャッシュに書き込まれたデータを
そのまま直接ディスクに書き込むようにします
FILE_FLAG_OVERLAPPED 時間のかかる処理に対して
ERROR_IO_PENDING を返すようにします
処理が終了すると、イベントはシグナル状態に設定されます
このフラグを指定したときは、ReadFile 関数や
WriteFile 関数で OVERLAPPED 構造体を指定しなければなりません
FILE_FLAG_NO_BUFFERING バッファやキャッシュを使用せずに
ファイルをオープンするようにします
場合によっては、パフォーマンスが向上します
このフラグを指定したときは、次の条件を満たさなければなりません

1
ファイルアクセスの開始オフセットを
ボリュームのセクタサイズの整数倍にしなければなりません

2
ファイルのアクセスのバイト数を
ボリュームのセクタサイズの整数倍にしなければなりません
セクタサイズが 512 バイトの場合
512 バイト、1024 バイト……の読み書きはできますが
335 バイト、981 バイトなどの読み書きはできません

3
読み書き操作用のバッファのアドレスを
ボリュームのセクタサイズの整数倍に整列しなければなりません

3 番目の条件を満たすには、VirtualAlloc() 関数を利用すると便利です
VirtualAlloc() は、確保するメモリを (セクタサイズではありませんが)
ページサイズの整数倍に整列します
ボリュームのセクタサイズを求めるには
GetDiskFreeSpace() 関数を使います
FILE_FLAG_RANDOM_ACCESS ファイルをランダムアクセスすることをシステムに示します
システムは、この指定をファイルのキャッシングを
最適化するヒントとして使用します
FILE_FLAG_SEQUENTIAL_SCAN ファイルをシーケンシャルにアクセスすることをシステムに示します
システムは、この指定をファイルのキャッシングを
最適化するヒントとして使用します
このフラグを指定しても
ランダムアクセスができなくなるわけではありません
FILE_FLAG_DELETE_ON_CLOSE ファイルのハンドルがクローズされたら
そのファイルを削除するようにします
FILE_FLAG_BACKUP_SEMANTICS Windows NT
バックアップまたは復元操作のために
ファイルをオープンまたは作成します
FILE_FLAG_POSIX_SEMANTICS POSIX の規則に従ってファイルにアクセスします
このフラグを使って作成したファイルには
MS-DOS/Windows アプリケーションからはアクセスできません

名前付きパイプのクライアント側をオープンする場合は
Security Quality of Service (SQOS)情報を指定できます
呼び出し側アプリケーションが SECURITY_SQOS_PRESENT フラグを指定している場合
dwFlagsAndAttributes パラメータには、次の定数の組み合わせを指定できます

定数解説
SECURITY_ANONYMOUS 匿名 (Anonymous) 偽装レベルでクライアントを偽装します
SECURITY_IDENTIFICATION 確認 (Identification) 偽装レベルでクライアントを偽装します
SECURITY_IMPERSONATION 偽装 (Impersonation) 偽装レベルでクライアントを偽装します
SECURITY_DELEGATION 代理 (Delegation) 偽装レベルでクライアントを偽装します
SECURITY_CONTEXT_TRACKING セキュリティトラッキングモードが動的であることを指定します
このフラグを指定しない場合
セキュリティトラッキングモードは静的です
SECURITY_EFFECTIVE_ONLY クライアントのセキュリティコンテキストのうち
有効になっている部分だけをサーバーで利用できるようにします
このフラグを指定しない場合は、すべての部分が利用できます

CloseHandle()

BOOL CloseHandle(HANDLE hObject);

開いているオブジェクトのハンドルを閉じます

hObject - オブジェクトのハンドルを指定します

戻り値 - 成功すると 0 以外、失敗すると 0

GetFileSize()

DWORD GetFileSize(HANDLE hFile , LPDWORD lpFileSizeHigh);

指定したファイルのサイズを取得します

hFile - サイズを取得するファイルのハンドルを指定します
lpFileSizeHigh - サイズの上位32ビットを格納する DWORD へのポインタを指定します

戻り値 - ファイルサイズの下位32ビット、失敗すれば 0xFFFFFFFF

GetFileType()

DWORD GetFileType(HANDLE hFile);

ファイルタイプを取得します

hFile - ファイルタイプを取得するファイルのハンドルを指定します

戻り値 - ファイルのタイプを表す定数

戻り値は、以下の定数のいずれかになります

定数解説
FILE_TYPE_UNKNOWN 種類は不明です
FILE_TYPE_DISK ディスクファイルです
FILE_TYPE_CHAR LPT デバイスやコンソールといった文字ファイルです
FILE_TYPE_PIPE 名前付きまたは名前なしパイプです

DeleteFile()

BOOL DeleteFile(LPCTSTR lpFileName);

指定ファイルを削除します

lpFileName - 削除するファイル名を指定します

戻り値 - 成功すると 0 以外、失敗すると 0

CopyFile()

BOOL CopyFile(
	LPCTSTR lpExistingFileName,
	LPCTSTR lpNewFileName, 
	BOOL bFailIfExists
);
既存ファイルをコピーします

lpExistingFileName - コピーする既存のファイル名を指定します
lpNewFileName - コピー先の新しいファイル名を指定します
bFailIfExists - コピー先が存在する時、TRUE なら失敗、FALSE なら上書き

戻り値 - 成功すると 0 以外、失敗すると 0

MoveFile()

BOOL MoveFile(LPCTSTR lpExistingFileName , LPCTSTR lpNewFileName);

ファイルを移動、またはディレクトリ名を変更します

lpExistingFileName - 移動させるファイル、またはディレクトリ名を指定します
lpNewFileName - 移動先のファイル、またはディレクトリ名を指定します

戻り値 - 成功すると 0 以外、失敗すると 0



前のページへ戻る次のページへ