ディレクトリ操作


カレントディレクトリ

さて、今後より高度なウィンドウアプリケーションを作成していくためにも
この辺で Windows のシステムについて詳しく掘り下げていこうと思う

ANSI C がサポートする標準関数セットには基本的なファイル操作やネットワーク機能があった
しかし、UNIX や MS-DOS ならば ANSI の標準関数群は役に立つものの
それだけでは Win32 のグラフィックスシステムの機能を最大にまで引き出すことはできない

やはり、システムに依存する高度な機能はそのシステムに問い合わせるべきです
Windows はディレクトリやファイルに関する高度な関数群を提供しています

Windows のファイルシステムはカレントディレクトリに対して行われます
例えば、以前紹介したリストを使ったファイル列挙の対象はカレントディレクトリです
カレントディレクトリを知るには GetCurrentDirectory() 関数を使用します

DWORD GetCurrentDirectory(DWORD nBufferLength , LPTSTR lpBuffer);

nBufferLength には lpBuffer のサイズを指定します
lpBuffer にはカレントディレクトリの名前を受け取るバッファを指定します
バッファはディレクトリ名と終端文字 NULL を受ける十分なサイズを確保する必要があります
ディレクトリ名の最大サイズはシステムが MAX_PATH という定数で提供しています

関数が成功すると NULL 文字を除いたバッファに格納した文字数
バッファのサイズが足りなかった場合は NULL 文字を含む必要なバッファのサイズ
失敗した場合は 0 のいずれかの値が返ります

また、操作するディレクトリを変更するためにカレントディレクトリを変更することもできます
カレントディレクトリの変更には SetCurrentDirectory() 関数を使います

BOOL SetCurrentDirectory(LPCTSTR lpPathName);

lpPathName はパス名が格納された文字列へのポインタをしていします
絶対パスでも相対パスでもかまいません
ここで指定したパスが新しいカレントディレクトリとしてセットされます
関数が成功すると 0 以外、失敗すると 0 が返ります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND list;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		list = CreateWindow(
			TEXT("LISTBOX") , NULL ,
			WS_CHILD | WS_VISIBLE | LBS_STANDARD ,
			0 , 0 , 180 , 180 , hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		SendMessage(
			list , LB_DIR ,
			DDL_READWRITE , (LPARAM)TEXT("*.*")
		);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;
	TCHAR crDir[MAX_PATH + 1];

	GetCurrentDirectory(MAX_PATH + 1 , crDir);
	SetCurrentDirectory(lpCmdLine);

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , crDir ,
			WS_OVERLAPPED | WS_SYSMENU | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			200 , 200 ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、コマンドライン引数として受け取ったディレクトリのファイルを列挙します
タイトルバーは、デフォルトのカレントディレクトリを表しています

リストに LB_DIR メッセージを送信すると、指定した条件のファイルを列挙しました
このプログラムでは起動時に SetCurrentDirectory() でカレントディレクトリを
コマンドライン引数 lpCmdLine にセットしてからリストを生成しています


重要なディレクトリ

Windows コンピュータでは、とくにその動作に大きく関わる重要なデータを格納している
システムの中枢をになうディレクトリがいくつか存在します
一般的にこの名前は固定であり、変更することは推奨されませんが
ディレクトリの位置は自由に変更できるため、必ずしもデフォルトの状態とは限らないでしょう

そこで、こういったディレクトリにアクセスするにはその位置を取得する必要があります
フォントや重要な DLL が格納されているシステムディレクトリの取得には
GetSystemDirectory() 関数を使います

UINT GetSystemDirectory(LPTSTR lpBuffer , UINT uSize);

lpBuffer には、ディレクトリのパス名を格納するバッファを指定します
uSize はバッファのサイズを指定します
当然、バッファはパス名が入るだけのサイズを確保しておく必要があります

成功すれば、バッファに格納した NULL 文字も含むサイズが
バッファのサイズが足りなければバッファには何も書きこまず必要なサイズを返します
失敗すれば 0 が返ります

ただし、アプリケーションはシステムディレクトリ内にファイルを生成してはいけません
基本的にシステムディレクトリへのアクセスはネットワーク管理者のみが可能となっています

Windows がインストールされているディレクトリは
GetWindowsDirectory() 関数で得ることができます
このディレクトリには基本的な付属アプリケーションなどが格納されています

UINT GetWindowsDirectory(LPTSTR lpBuffer , UINT uSize);

引数や戻り値の意味は GetSystemDirectory() 関数と同じです
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	TCHAR tmpDir[2][MAX_PATH + 1];
	TEXTMETRIC tm;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		GetWindowsDirectory(tmpDir[0] , MAX_PATH + 1);
		GetSystemDirectory(tmpDir[1] , MAX_PATH + 1);
		
		hdc = BeginPaint(hwnd , &ps);
		GetTextMetrics(hdc , &tm);
		TextOut(hdc , 0 , 0 , tmpDir[0] , lstrlen(tmpDir[0]));
		TextOut(hdc , 0 , tm.tmHeight , tmpDir[1] , lstrlen(tmpDir[1]));

		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、Windows ディレクトリとシステムディレクトリを取得し
それをクライアント領域に表示させるとい動く簡単なものです
上の結果は筆者の Windows98 SE デフォルトの環境の結果です
多くの場合はこの結果と同じものが得られるでしょう


作成と削除

カレントディレクトリの変更は Windows 関数以外にも C 言語の chdir() というものもありました
同様に ANSI C に慣れているプログラマはディレクトリの生成といえば mkdir() を思い出すでしょう

しかし、Win32 においてはやはり Win32 API を使用するべきでしょう
Win32 API では、NTFS (NT File System) など Windows NT でのセキュリティにも対応しています
ディレクトリの生成には CreateDirectory() 関数を使用します
BOOL CreateDirectory(
	LPCTSTR lpPathName,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes 
);
lpPathName にはディレクトリのパスを格納した文字列へのポインタを指定します
Windows NT であれば、ワイド文字の CreateDirectory() を呼び出して
"\\?\" または "\\?\UNC\" を付けることによって
MAX_PATH の制限を受けない文字数を指定できるようになります
たとえば "\\?\C:\WINDOWS" は "C:\WINDOS" と解釈されることになります

lpSecurityAttributed にはセキュリティ属性を格納してある
SECURITY_ATTRIBUTES 構造体へのポインタを指定します
この構造体は NTFS で使用するものであり、9x シリーズの FAT システムでは使いません
Windows 95 系であれば、この引数を NULL に指定しても問題ありません
(もちろん構造体のポインタを渡してもかまいませんが、無視されます)

Windows NT でこの引数を NULL にするとデフォルトのセキュリティー記述子が使われます
この構造体や NT のファイルシステムについては、この場ではふれません
関数が成功すると 0 以外、失敗すると 0 が返ります
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	if (!CreateDirectory(lpCmdLine , NULL))
		MessageBox(
			NULL , TEXT("ディレクトリが作成できません") ,
			TEXT("エラー") , MB_OK
		);
	return 0;
}
コマンドライン引数をパスとしてディレクトリを生成するプログラムです
簡単のために、ウィンドウ生成は省略しました
絶対パスでも相対パスでも作成できることを確認してください
ただし、ディレクトリとして不正な名前であれば関数は失敗します

また CreateDirectoryEx() 関数で生成することもできます
この関数は指定したテンプレートディレクトリの属性を継承します
BOOL CreateDirectoryEx(
	LPCTSTR lpTemplateDirectory ,
	LPCTSTR lpNewDirectory ,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
lpTemplateDirectory にはテンプレートディレクトリを指定します
ここで指定したディレクトリの属性を引き継ぐということ以外は CreateDirectory() 関数と同じです

ディレクトリの削除には RemoveDirectory() 関数を使用します
削除する権限を持っていれば削除することができます

BOOL RemoveDirectory(LPCTSTR lpPathName);

lpPathName には削除するディレクトリのパス名が入った文字列へのポインタを指定します
削除するディレクトリは空でなければなりません
関数が成功すると 0 以外、失敗すると 0 が返ります
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
		PSTR lpCmdLine , int nCmdShow) {
	if (!RemoveDirectory(lpCmdLine))
		MessageBox(
			NULL , TEXT("ディレクトリが削除できません") ,
			TEXT("エラー") , MB_OK
		);
	return 0;
}
ディレクトリ操作をする時は、エスケープ文字 \ を文字列内によく使用しますが
\ を文字列で表現するには \\ を使用するという基本的な文字操作を忘れないで下さい


GetCurrentDirectory()

DWORD GetCurrentDirectory(DWORD nBufferLength , LPTSTR lpBuffer);

現在のカレントディレクトリを取得します

nBUfferLength - lpBuffer のサイズを指定します
lpBuffer - カレントディレクトリ名を取得するバッファを指定します

戻り値 - 格納した文字列、バッファが不十分なら必要なサイズ、失敗したら 0

SetCurrentDirectory()

BOOL SetCurrentDirectory(LPCTSTR lpPathName); 新しくカレントディレクトリを設定します

lpPathName - ディレクトリの名前が格納されている文字列へのポインタを指定します

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

GetSystemDirectory()

UINT GetSystemDirectory(LPTSTR lpBuffer , UINT uSize);

システムディレクトリの位置をバッファに取得します

lpBuffer - ディレクトリのパス名を格納するバッファを指定します
uSize - MAX_PATH 以上で lpBuffer のサイズを指定します

戻り値 - 格納したサイズ、足りなければ必要なサイズ、失敗すれば 0

GetWindowsDirectory()

UINT GetWindowsDirectory(LPTSTR lpBuffer , UINT uSize);

Windows が格納されているディレクトリをバッファに取得します

lpBuffer - ディレクトリのパス名を格納するバッファを指定します
uSize - MAX_PATH 以上で lpBuffer のサイズを指定します

戻り値 - 格納したサイズ、足りなければ必要なサイズ、失敗すれば 0

CreateDirectory()

BOOL CreateDirectory(
	LPCTSTR lpPathName,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes 
);
新しいディレクトリを作成します

lpPathName - 作成するディレクトリのパス名を格納する文字列へのポインタを指定します
lpSecurityAttributes - SECURITY_ATTRIBUTES 構造体へのポインタを指定します

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

CreateDirectoryEx()

BOOL CreateDirectoryEx(
	LPCTSTR lpTemplateDirectory ,
	LPCTSTR lpNewDirectory ,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
新しいディレクトリを作成します
ディレクトリはテンプレートディレクトリの属性を引き継ぎます

lpTemplateDirectory - テンプレートディレクトリのパスが入った文字列へのポインタを指定します
lpPathName - 作成するディレクトリのパス名を格納する文字列へのポインタを指定します
lpSecurityAttributes - SECURITY_ATTRIBUTES 構造体へのポインタを指定します

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

RemoveDirectory()

BOOL RemoveDirectory(LPCTSTR lpPathName);

空のディレクトリを削除します

lpPathName - 削除するディレクトリのパスが入った文字列へのポインタを指定します

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



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