ファイルをコピーする(バックアップする)ソフトを作ろうとしています。バックアップ対象のフォルダに深い階層を持ったディレクトリを指定した場合に、深い階層のファイルの更新を検知するのに何か良い方法はないでしょうか?
フォルダの階層の深さは関係ないと思うけど。
フォルダの変更を監視するAPIを使えばいいんじゃない?
ところで、前の質問は解決したの?
前の質問は解決にしたつもりでした。
そのAPIが知りたいのですが。
Mr.XRAYです.
>深い階層のファイルの変更を検知するには
というこですが,浅い階層で検知するのができていれば,同じ方法が使えませんか?
残念ですが,私はファイルの変更(何の変更か)を検出する方法を知りません.
あるフォルダ内の変更を監視するAPIというのはありますが,
これは,フォルダ内のどのファイルに変更があったかはわかりません.
(この場合の変更とは,ファイルの削除,追加,名前の変更のことです)
「変更」の内容というか意味によっても何か方法があるかも知れません.
私だったら,更新日時ぐらいしか思いつきません.
更新日時であれば,階層には関係ないですよね.
これでいいだろ?
if SearchRec.Attr and faArchive = faArchive then
begin
end;
コピーしたら A 属性解除忘れずに
ご回答ありがとうございます。変更というのはおっしゃる通り、ファイル・ディレクトリの削除・追加・名前の変更のことです。また、更新されたかどうかは、やはり更新日時で判断しようとしています。
言葉が足りなかったようで補足しておきます。
「深い階層」と言っているのは、例えば、あるディレクトリをバックアップ対象として設定した場合、その直下のサブディレクトリについては、その中にファイルが作成されたり、さらにサブディレクトリが作られるとタイムスタンプが更新されます。この場合はFindFirst〜FindNextで知ることができます。さらに、作成したサブディレクトリ内にフォルダやファイルが作成された場合を考えると、そのサブディレクトリもタイムスタンプが更新されます。しかし、そのサブディレクトリ内にさらにディレクトリを作った場合には、当初対象にした2階層上のディレクトリのタイムスタンプは更新されません。(解りにくい表現で済みません。)
つまりは、あるディレクトリの中のサブディレクトリの中のディレクトリ(のさらにその下のディレクトリも含めて)深いところで変更があった場合、おおもとのディレクトリのタイムスタンプを見ただけでは解らないということになって、これを検知する方法がないか、と思ったわけです。
やはりサブディレクトリがあった場合は、その中を調べて、またサブディレクトリがあったらまたその中を調べる、という以外に方法はない、ということになりますでしょうか。
真琴:「ハルコさん、階層が深いSubフォルダのファイル変更もこれで監視できるの?」
春子:「そぅよ、EditにフォルダPATHを入れて、Startボタン押して確かめてみたら?」
真琴:「あっ、そのSubフォルダのファイル書き換えや新規作成、コピーなんかでも、そのファイル名が表示されるね」
春子:「でしょ? もちろん深いSubフォルダ名の変更、作成の検知もOK」
真琴:「なるほど、スレッドを使って監視してるのね? でも、ハルコさん…」
春子:「ん? なに?」
真琴:「けっこう面倒よね?この方法も^^;、API一発で分かるって方法とかない?^^;;」
春子:「世の中そんな甘くないって」
type
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength: DWORD;
FileName: array[0..0] of WideChar;
end;
const
FILE_LIST_DIRECTORY = $0001;
ActionDsp: array[FILE_ACTION_ADDED..FILE_ACTION_RENAMED_NEW_NAME] of string =
('ADDED %s','DELETED %s','MODIFIED %s','RENAME_Old %s','RENAME_New %s');
var
NotifyBuf: array[0..$1000] of Byte;
NotifyFilter: DWORD;
hDirectory: THandle;
hCompletionPort: THandle;
Overlapped: TOverlapped;
rwBytes: DWORD;
type
TWatchThread = class(TThread)
private
procedure ShowInformation;
public
procedure Execute; override;
end;
var
WatchThread := TWatchThread;
procedure TWatchThread.ShowInformation;
var
pFNI: PFileNotifyInformation;
Offset: Integer;
begin
pFNI := @NotifyBuf[0];
repeat
Offset := pFNI^.NextEntryOffset;
Form1.ListBox1.Items.Add(Format(ActionDsp[pFNI^.Action], [WideCharLenToString(@(pFNI^.FileName), pFNI^.FileNameLength)]));
inc(PChar(pFNI), Offset);
until Offset = 0;
end;
procedure TWatchThread.Execute;
var
CompletionKey: DWORD;
lpOverlapped: POverlapped;
begin
while (not Terminated)and(not Application.Terminated) do begin
lpOverlapped := @Overlapped;
GetQueuedCompletionStatus(hCompletionPort, rwBytes, CompletionKey, lpOverlapped, INFINITE);
if CompletionKey <> 0 then begin
Synchronize(ShowInformation);
ZeroMemory(@NotifyBuf, SizeOf(NotifyBuf));
ReadDirectoryChanges(hDirectory, @NotifyBuf, SizeOf(NotifyBuf), True, NotifyFilter, @rwBytes, @Overlapped, nil);
end
end;
end;
procedure TForm1.StartButtonClick(Sender: TObject);
begin
if Assigned(WatchThread)and(not WatchThread.Suspended) then exit;
ListBox1.Clear;
hDirectory := CreateFile(PChar(EditPath.Text),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0);
if hDirectory = INVALID_HANDLE_VALUE then begin
hDirectory := 0;
ShowMessage(SysErrorMessage(GetLastError));
exit;
end;
NotifyFilter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME
or FILE_NOTIFY_CHANGE_LAST_WRITE or FILE_NOTIFY_CHANGE_CREATION;
hCompletionPort := CreateIoCompletionPort(hDirectory, 0, DWORD(Pointer(Self)), 0);
ZeroMemory(@NotifyBuf, SizeOf(NotifyBuf));
if not ReadDirectoryChanges(hDirectory, @NotifyBuf, SizeOf(NotifyBuf), True, NotifyFilter, @rwBytes, @Overlapped, nil) then
begin
CloseHandle(hDirectory);
CloseHandle(hCompletionPort);
hDirectory := 0;
hCompletionPort := 0;
ShowMessage(SysErrorMessage(GetLastError));
exit;
end;
EditPath.Enabled := False;
WatchThread := TWatchThread.Create(True);
WatchThread.FreeOnTerminate := False;
WatchThread.Resume;
end;
procedure TForm1.StopButtonClick(Sender: TObject);
begin
if hCompletionPort = 0 then exit;
PostQueuedCompletionStatus(hCompletionPort, 0, 0, nil);
if Assigned(WatchThread) then begin
WatchThread.Terminate;
WatchThread.WaitFor;
WatchThread.Free;
WatchThread := nil;
end;
CloseHandle(hDirectory);
hDirectory := 0;
CloseHandle(hCompletionPort);
hCompletionPort := 0;
EditPath.Enabled := True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
StopButton.Click;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ListBox1.Font.Name := 'MS ゴシック';
hCompletionPort := 0;
hDirectory := 0;
ZeroMemory(@Overlapped, SizeOf(Overlapped));
end;
春子:「ところで、マコト、どう? 久しぶりの中学生活はもう慣れたかな?」
真琴:「それがまだ…、忘れてたコトも多いから友達と話をあわせるのがもう大変」
春子:「ま、4年ぶりだからね」
真琴:「でも、ハルコさん、どうしてアタシの話をあっさり信じてくれたの?」
春子:「そっかぁ、マコトは憶えてないんだね、4年前に戻ったワケを」
真琴:「えっ、ワケって、ハルコさんはそれを知ってるの?」
春子:「モチロン…、ジツはネ、マコトはまた、時の迷子になっちゃったのよ」
真琴:「時の迷子? それってどういうコト? よく分からないけど、ナンで?」
春子:「ナンでって…理由はアタシの方が聞きたいよ、もう、めちゃアセッタんだから」
真琴:「だって、そんな記憶ゼンゼンないもん、それでどうなったの?」
春子:「1回目の時はまだアタシの能力があって助けられたけど、今回はもうダメかと」
真琴:「でも、またハルコさんが助けてくれたの? えっ、それじゃ、もしかして…」
春子:「ホントに何も憶えてない? どうしても元に戻る出口が見つからず、やっと見つけたのが4年前の出口だったってコトを」
真琴:「憶えてない…」
春子:「もう死ぬ覚悟で、最後の力を振り絞って助けたんだから、感謝してよネ」
真琴:「それがホントならモチロン大感謝だけど…、もしかしてまた冗談話とか^^;」
春子:「こうして冗談話が聞けるのも助かったからでしょ? …あっ、予約のお客様が到着したのかな?」
真琴:「え〜? やっぱり冗談? それともホント? ねぇどっちなのぉ?」
ありがとうございます。これはすごい! こんなに教えていただいてしまって大感謝です。これからどういう風に応用していこうか考えてみます。
リアルタイム監視ということで、制作中のアプリケーションに組み込むには一工夫いりそうですが、とりあえず使える方法があるということでスレッドはクローズいたします。
ご返答くださった方々、ありがとうございました。
ツイート | ![]() |