ディレクトリ変更通知の取得
アプリケーションでは、変更通知を使用してディレクトリとそのサブディレクトリの内容を監視できます。 変更通知の待機は、ディレクトリとそのサブディレクトリ (必要な場合) に対して保留中の読み取り操作を行うのと似ています。 監視対象のディレクトリ内で何かが変更されると、読み取り操作が完了します。 たとえば、アプリケーションでは、監視対象のディレクトリ内のファイル名が変更されるたびに、これらの関数を使用してディレクトリ一覧を更新できます。
アプリケーションでは、FindFirstChangeNotification 関数を使用して、変更通知をトリガーする一連の条件を指定できます。 条件には、ファイル名、ディレクトリ名、属性、ファイル サイズ、最終書き込み時刻、セキュリティの変更が含まれます。 この関数は、wait 関数を使用して待機できるハンドルも返します。 待機条件が満たされた場合、FindNextChangeNotification を使用して、後続の変更を待機する通知ハンドルを提供できます。 ただし、これらの関数は、待機条件を満たした実際の変更を示すものではありません。
通知ハンドルを閉じるには FindCloseChangeNotification を使用します。
通知の一部として特定の変更に関する情報を取得するには、ReadDirectoryChangesW 関数を使用します。 この関数を使用して完了ルーチンを提供することもできます。
Note
FindFirstChangeNotification 関数と ReadDirectoryChangesW 関数は相互に排他的です。 どちらか一方のみを使用してください。
ボリュームの変更を追跡するには、「変更ジャーナル」を参照してください。
次の例では、ディレクトリ ツリーのディレクトリ名の変更を監視しています。 ディレクトリのファイル名の変更についても監視しています。 この例では、FindFirstChangeNotification 関数を使用して 2 つの通知ハンドルを作成し、WaitForMultipleObjects 関数を使用してハンドルを待機しています。 ツリーでディレクトリが作成または削除されるたびに、この例ではディレクトリ ツリー全体が更新されます。 ディレクトリでファイルが作成または削除されるたびに、この例ではディレクトリが更新されます。
Note
この単純な例では、終了とクリーンアップに ExitProcess 関数を使用しますが、より複雑なアプリケーションでは、必要に応じて FindCloseChangeNotification などの適切なリソース管理を常に使用する必要があります。
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
void RefreshDirectory(LPTSTR);
void RefreshTree(LPTSTR);
void WatchDirectory(LPTSTR);
void _tmain(int argc, TCHAR *argv[])
{
if(argc != 2)
{
_tprintf(TEXT("Usage: %s <dir>\n"), argv[0]);
return;
}
WatchDirectory(argv[1]);
}
void WatchDirectory(LPTSTR lpDir)
{
DWORD dwWaitStatus;
HANDLE dwChangeHandles[2];
TCHAR lpDrive[4];
TCHAR lpFile[_MAX_FNAME];
TCHAR lpExt[_MAX_EXT];
_tsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);
lpDrive[2] = (TCHAR)'\\';
lpDrive[3] = (TCHAR)'\0';
// Watch the directory for file creation and deletion.
dwChangeHandles[0] = FindFirstChangeNotification(
lpDir, // directory to watch
FALSE, // do not watch subtree
FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes
if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)
{
printf("\n ERROR: FindFirstChangeNotification function failed.\n");
ExitProcess(GetLastError());
}
// Watch the subtree for directory creation and deletion.
dwChangeHandles[1] = FindFirstChangeNotification(
lpDrive, // directory to watch
TRUE, // watch the subtree
FILE_NOTIFY_CHANGE_DIR_NAME); // watch dir name changes
if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)
{
printf("\n ERROR: FindFirstChangeNotification function failed.\n");
ExitProcess(GetLastError());
}
// Make a final validation check on our handles.
if ((dwChangeHandles[0] == NULL) || (dwChangeHandles[1] == NULL))
{
printf("\n ERROR: Unexpected NULL from FindFirstChangeNotification.\n");
ExitProcess(GetLastError());
}
// Change notification is set. Now wait on both notification
// handles and refresh accordingly.
while (TRUE)
{
// Wait for notification.
printf("\nWaiting for notification...\n");
dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles,
FALSE, INFINITE);
switch (dwWaitStatus)
{
case WAIT_OBJECT_0:
// A file was created, renamed, or deleted in the directory.
// Refresh this directory and restart the notification.
RefreshDirectory(lpDir);
if ( FindNextChangeNotification(dwChangeHandles[0]) == FALSE )
{
printf("\n ERROR: FindNextChangeNotification function failed.\n");
ExitProcess(GetLastError());
}
break;
case WAIT_OBJECT_0 + 1:
// A directory was created, renamed, or deleted.
// Refresh the tree and restart the notification.
RefreshTree(lpDrive);
if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE )
{
printf("\n ERROR: FindNextChangeNotification function failed.\n");
ExitProcess(GetLastError());
}
break;
case WAIT_TIMEOUT:
// A timeout occurred, this would happen if some value other
// than INFINITE is used in the Wait call and no changes occur.
// In a single-threaded environment you might not want an
// INFINITE wait.
printf("\nNo changes in the timeout period.\n");
break;
default:
printf("\n ERROR: Unhandled dwWaitStatus.\n");
ExitProcess(GetLastError());
break;
}
}
}
void RefreshDirectory(LPTSTR lpDir)
{
// This is where you might place code to refresh your
// directory listing, but not the subtree because it
// would not be necessary.
_tprintf(TEXT("Directory (%s) changed.\n"), lpDir);
}
void RefreshTree(LPTSTR lpDrive)
{
// This is where you might place code to refresh your
// directory listing, including the subtree.
_tprintf(TEXT("Directory tree (%s) changed.\n"), lpDrive);
}