はじめから出るのスレッド:
私が新たに追加されたファイルの特定のディレクトリを監視する小さなアプリケーションを書いていますが。非同期ReadDirectoryChangesW通話ブロック
監視コードを別のスレッドに入れたいので、メインスレッドを他のもののために空けておき、必要なときに監視スレッドをキャンセルすることができます。
関連情報:
- 私は以降のWindows XPをサポートしようとしているスレッドの作成/同期
- のための生のWin32 APIを使用しています監視
- を行うにはReadDirectoryChangesWを使用しています。
問題:
は、私は一つのことを除いて、適切にすべてのものをコーディングすることができました:
私は、したがって、適切にこのポストをスレッドの監視を終了することはできません。
私はメインスレッドでイベントオブジェクトを通知し、スレッドが終了してからクリーンアップを行うのを待ちます。
ReadDirectoryChangesW
の問題は、そのコードをコメントアウトした後に問題なく動作するためです。
イベントハンドルが通知されると、ReadDirectoryChangesW
はスレッドをブロックし、イベントを「キャッチ」して終了します。 ReadDirectoryChangesW
というディレクトリに新しいファイルを追加すると、スレッドはそのイベントを「キャッチ」して終了します。
私はこれまでに述べたことを具体的に示すために、さらに小さな数字をMVCEとしました。
MVCE:この問題を解決するには
#include <iostream>
#include <Windows.h>
#include <map>
struct SThreadParams
{
HANDLE hEvent;
HANDLE hDir;
int processDirectoryChanges(const char *buffer)
{
if (NULL == buffer) return -1;
DWORD offset = 0;
char fileName[MAX_PATH] = "";
FILE_NOTIFY_INFORMATION *fni = NULL;
do
{
fni = (FILE_NOTIFY_INFORMATION*)(&buffer[offset]);
// since we do not use UNICODE,
// we must convert fni->FileName from UNICODE to multibyte
int ret = ::WideCharToMultiByte(CP_ACP, 0, fni->FileName,
fni->FileNameLength/sizeof(WCHAR),
fileName, sizeof(fileName), NULL, NULL);
switch (fni->Action)
{
case FILE_ACTION_ADDED:
{
std::cout << "FILE_ACTION_ADDED " << fileName << std::endl;
}
break;
case FILE_ACTION_REMOVED:
{
std::cout << "FILE_ACTION_REMOVED " << fileName << std::endl;
}
break;
case FILE_ACTION_MODIFIED:
{
std::cout << "FILE_ACTION_MODIFIED " << fileName << std::endl;
}
break;
case FILE_ACTION_RENAMED_OLD_NAME:
{
std::cout << "FILE_ACTION_RENAMED_OLD_NAME " << fileName << std::endl;
}
break;
case FILE_ACTION_RENAMED_NEW_NAME:
{
std::cout << "FILE_ACTION_RENAMED_NEW_NAME " << fileName << std::endl;
}
break;
default:
break;
}
// clear string so we can reuse it
::memset(fileName, '\0', sizeof(fileName));
// advance to next entry
offset += fni->NextEntryOffset;
} while (fni->NextEntryOffset != 0);
return 0;
}
};
DWORD WINAPI thread(LPVOID arg)
{
SThreadParams p = *((SThreadParams *)arg);
OVERLAPPED ovl = { 0 };
DWORD bytesTransferred = 0, error = 0;
char buffer[1024];
if (NULL == (ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL)))
{
std::cout << "CreateEvent error = " << ::GetLastError() << std::endl;
return ::GetLastError();
};
do {
if (::ReadDirectoryChangesW(p.hDir, buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL))
{
if (::GetOverlappedResult(p.hDir, &ovl, &bytesTransferred, TRUE))
{
for (int i = 0; i < 5; ++i) std::cout << '=';
std::cout << std::endl;
if (-1 == p.processDirectoryChanges(buffer))
std::cout << "processDirectoryChanges error = " << std::endl;
}
else
{
bytesTransferred = 0;
std::cout << "GetOverlappedResult error = " << ::GetLastError() << std::endl;
}
if (0 == ::ResetEvent(ovl.hEvent))
{
std::cout << "ResetEvent error = " << ::GetLastError() << std::endl;
::CloseHandle(ovl.hEvent);
return ::GetLastError();
}
}
else
{
// we shall just output the error, and try again...
std::cout << "ReadDirectoryChangesW error = " << ::GetLastError() << std::endl;
}
error = ::WaitForSingleObject(p.hEvent, 2000);
} while (WAIT_TIMEOUT == error);
::CloseHandle(ovl.hEvent);
return 0;
}
int main()
{
SThreadParams s;
s.hDir = ::CreateFile(SOME_DIRECTORY,
FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (INVALID_HANDLE_VALUE == s.hDir)
{
std::cout << "CreateFile error = " << ::GetLastError() << std::endl;
return 1;
}
s.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == s.hEvent)
{
std::cout << "CreateEvent error = " << ::GetLastError() << std::endl;
::CloseHandle(s.hDir);
return 1;
}
HANDLE hThread = ::CreateThread(NULL, 0, thread, (LPVOID)&s, 0, NULL);
if (NULL == hThread)
{
std::cout << "CreateThread error = " << ::GetLastError() << std::endl;
::CloseHandle(s.hDir);
::CloseHandle(s.hEvent);
return 1;
}
std::cout << "press any key to close program..." << std::endl;
std::cin.get();
if (0 == ::CancelIoEx(s.hDir, NULL))
{
std::cout << "CancelIoEx error = " << ::GetLastError() << std::endl;
::CloseHandle(s.hDir);
::CloseHandle(s.hEvent);
return 1;
}
if (0 == ::SetEvent(s.hEvent))
{
std::cout << "SetEvent error = " << ::GetLastError() << std::endl;
::CloseHandle(s.hDir);
::CloseHandle(s.hEvent);
return 1;
}
// wait for thread to exit
DWORD error = ::WaitForSingleObject(hThread, INFINITE);
std::cout << "Thread exited with error code = " << error << std::endl;
::CloseHandle(s.hEvent);
::CloseHandle(s.hDir);
::CloseHandle(hThread);
return 0;
}
私の努力:私はスレッドに渡された構造に、スレッドの外にOVERLAPPED
構造を移動した
。次に、
OVERLAPPED.hEvent
を強制的に "ブロック解除"するように設定しましたReadDirectoryChangesW
。これはうまくいくようですが、文書化されていないので、安全ではない/間違いやすいと思うので、私を恐れています。私は完了ルーチンを使用しようとしましたが、私はすべてこれで新しくなってから成功しませんでした。私は通知を受け取ることができましたが、バッファの内容(
ReadDirectoryChangesW
で満たされたもの)は最初のパスの後に正しく読み込まれませんでした。私はまだ自分自身でこの作業をしようとしていますが、助けを使うことができます。私はI/O完了ポートを使用することができましたが、私は1つのディレクトリだけを監視するので、これはちょっとした過ちです。私が間違っている場合は、私のケースにI/O完了ポートを使用する方法を教えてください、私はそれらを試してみたいと思います。
QUESTION:
あなたはそれが(ReadDirectoryChangesW
ブロックせずに)適切に終了しますので、糸の手順でコードを変更する方法の私を指示することができ、MVCE上記の
。
私は完了ルーチンを使用しなければならないと感じています。その場合、擬似コードや書面による指示を謙虚に尋ねるのは、これを初めて使用するためです。
私が進歩を遂げたと感じる度に、それに応じてこの投稿を更新します。
さらなる情報や説明が必要な場合は、コメントを残して返信します。
ありがとう、
よろしく。 - あなたは最悪のバリアントを選択
最悪
- 使用ApcRoutine
- 使用IoCompletionPort
- 使用イベント:
奇妙なエンドレスループイベントは何ですか?貨物カルトプログラミングのような匂い。 –
@ JonathanPotter:これらのエラーを処理するプログラムフローを正しく構造化する方法がわかりません。質問に記載されている実際の問題を手伝ってもらえますか? – AlwaysLearningNewStuff
@ JonathanPotter:私はあなたの提案に従ってコードを書き直しました... – AlwaysLearningNewStuff