🌟
フォルダの監視
ReadDirectoryChangesW関数でフォルダ監視をする
他社システムとの連動にCSVファイルでの共有というのは現在でも意外と多い。
ファイル名が固定される場合はSetTimerで更新日付を監視する方が簡単だが、WindowsAPIにReadDirectoryChangesW関数という監視機能があったため、試しに利用してみることにした。
サンプル
メインスレッド側が終了フラグを立て、スレッド側がそれを取り込む箇所が(もっと簡単にできるだろうが)複雑。これはクラシックコンパイラで使用することを意識したため、排他処理で実装している。今考えればSync関数で取り込んでしまえばよかった。
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <System.Threading.hpp>
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
bool Cancel; // スレッド終了フラグ
TMultiReadExclusiveWriteSynchronizer *ReadWriteLock; // 排他制御オブジェクト
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
ReadWriteLock = new TMultiReadExclusiveWriteSynchronizer();
ReadWriteLock->BeginWrite();
Cancel = false;
ReadWriteLock->EndWrite();
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
_di_ITask task1 = TTask::Create([&](){
// ディレクトリへのハンドルを作成
HANDLE hDir = CreateFile(
L"D:\\doc\\",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if ( hDir == INVALID_HANDLE_VALUE) {
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("CreateFile failed.");});
return;
}
// ディレクトリの変更検知を行うオブジェクトを作成
//監視に使うフィルタ条件
DWORD dwNotifyFilter =
FILE_NOTIFY_CHANGE_FILE_NAME | //ファイル名変更
FILE_NOTIFY_CHANGE_DIR_NAME | //ディレクトリ名変更
FILE_NOTIFY_CHANGE_ATTRIBUTES | //属性変更
FILE_NOTIFY_CHANGE_LAST_WRITE | //最終書き込み日時変更
FILE_NOTIFY_CHANGE_LAST_ACCESS | //最終アクセス日時変更
FILE_NOTIFY_CHANGE_CREATION; //作成日時変更
wchar_t lpBuffer[1024];
wchar_t lpAsync[1024];
DWORD RetBytes;
DWORD i = 0;
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
OVERLAPPED ovlp = {0};
ResetEvent(hEvent);
ovlp.hEvent = hEvent;
bool bResult = ReadDirectoryChangesW(
hDir,
lpBuffer,
1024,
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL,
&ovlp,
NULL);
if (!bResult) {
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("ReadDirectoryChangesW failed.");});
continue;
}
while ( [&](){ReadWriteLock->BeginRead(); bool ret = !Cancel; ReadWriteLock->EndRead(); return ret;}() ) {
// 変更通知まち
DWORD waitResult = WaitForSingleObject(hEvent, 500); // 0.5秒待ち
if (waitResult != WAIT_TIMEOUT) {
// 変更通知があった場合 (イベントがシグナル状態になった場合)
break;
}
}
ReadWriteLock->BeginRead();
if ( Cancel == true ) {
ReadWriteLock->EndRead();
CloseHandle(hDir);
delete ReadWriteLock;
return;
}
ReadWriteLock->EndRead();
DWORD retsize = 0;
if (!GetOverlappedResult(hDir, &ovlp, &retsize, FALSE)) {
DWORD Error = GetLastError();
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("Error:");});
}
if (retsize == 0) {
// 返却サイズ、0ならばバッファオーバーを示す
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("overflow:");});
continue;
}
FILE_NOTIFY_INFORMATION *pInfo = (FILE_NOTIFY_INFORMATION *)&lpBuffer[i];
switch(pInfo->Action) {
case FILE_ACTION_ADDED:
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("file added:");});
break;
case FILE_ACTION_REMOVED:
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("file deleted:");});
break;
case FILE_ACTION_MODIFIED:
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("time stamp or attribute changed:");});
break;
case FILE_ACTION_RENAMED_OLD_NAME:
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("file name changed - old name:");});
break;
case FILE_ACTION_RENAMED_NEW_NAME:
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("file name changed - new name:");});
break;
default:
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add("unknown event:");});
continue;
}
WCHAR FileName[256];
lstrcpyn(FileName, pInfo->FileName, pInfo->FileNameLength / sizeof(WCHAR)+1);
FileName[pInfo->FileNameLength / sizeof(WCHAR)] = '\0';
TThread::Synchronize(NULL,[=](){Memo1->Lines->Add(FileName);});
if(pInfo->NextEntryOffset == 0) continue;
i += pInfo->NextEntryOffset;
}
CloseHandle(hEvent);
CloseHandle(hDir);
});
task1->Start();
}
//---------------------------------------------------------------------------
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
ReadWriteLock->BeginWrite();
Cancel = true;
ReadWriteLock->EndWrite();
// delete ReadWriteLock; // ここでdeleteしてはいけない!!スレッドが終了する前にReadWriteLockが解放され、スレッド側がハングアップする。
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Button2Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
Discussion