简体   繁体   English

如何在主线程的 WaitForMultipleObjects 调用中间安全地终止工作线程?

[英]How can I safely terminate a worker thread in the middle of a WaitForMultipleObjects call from the main thread?

Hi I am using C++ with the Qt framework for a Windows program.嗨,我正在使用 C++ 和 Qt 框架来执行 Windows 程序。 I use Qt threads but this situation could relevant for other thread APIs as well.我使用 Qt 线程,但这种情况也可能与其他线程 API 有关。 I am dedicating a worker thread to monitor for directory changes using ReadDirectoryChangesW and WaitForMultipleObjects from the Win API.我使用 Win API 中的ReadDirectoryChangesWWaitForMultipleObjects指定一个工作线程来监视目录更改。 I want to be able to cancel the worker thread gracefully from the main thread.我希望能够从主线程优雅地取消工作线程。 I have read about CancellIOEx that takes a handle and OVERLAPPED parameter but these data types are both pointers.我已经阅读了有关带有句柄和 OVERLAPPED 参数的CancelIOEx的信息,但是这些数据类型都是指针。 Is there some safe way to pass these pointers from the worker thread to the main thread safely?是否有一些安全的方法可以将这些指针从工作线程安全地传递到主线程? Is there a better way of doing things?有没有更好的做事方式?

Here's some code from here but using WaitForSingleObject instead of WaitForMultipleObjects, the function will be called from the worker thread.这是这里的一些代码,但使用 WaitForSingleObject 而不是 WaitForMultipleObjects,将从工作线程调用 function。 Am I allowed to post this code from the link?我可以从链接中发布此代码吗? Also see here for valuable information about ReadDirectoryChangesW outside of the Windows Dev Center.另请参阅此处以获取有关 Windows 开发中心之外的 ReadDirectoryChangesW 的宝贵信息。

Thanks!谢谢!

void WatchDirectory(LPCWSTR path)
{
   char buf[2048];
   DWORD nRet;
   BOOL result=TRUE;
   char filename[MAX_PATH];
   DirInfo[0].hDir = CreateFile (path, GENERIC_READ|FILE_LIST_DIRECTORY, 
                                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                                 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED,
                                 NULL);

   if(DirInfo[0].hDir == INVALID_HANDLE_VALUE)
   {
       return; //cannot open folder
   }

   lstrcpy( DirInfo[0].lpszDirName, path);
   OVERLAPPED PollingOverlap;

   FILE_NOTIFY_INFORMATION* pNotify;
   int offset;
   PollingOverlap.OffsetHigh = 0;
   PollingOverlap.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
   while(result)
   {
       result = ReadDirectoryChangesW(
                  DirInfo[0].hDir,// handle to the directory to be watched
                  &buf,// pointer to the buffer to receive the read results
                  sizeof(buf),// length of lpBuffer
                  TRUE,// flag for monitoring directory or directory tree
                  FILE_NOTIFY_CHANGE_FILE_NAME |
                  FILE_NOTIFY_CHANGE_DIR_NAME |
                  FILE_NOTIFY_CHANGE_SIZE,
                //FILE_NOTIFY_CHANGE_LAST_WRITE |
                //FILE_NOTIFY_CHANGE_LAST_ACCESS |
                //FILE_NOTIFY_CHANGE_CREATION,
                &nRet,// number of bytes returned
                &PollingOverlap,// pointer to structure needed for overlapped I/O
                NULL);

       WaitForSingleObject(PollingOverlap.hEvent,INFINITE);
       offset = 0;
       int rename = 0;
       char oldName[260];
       char newName[260];
       do
       {
           pNotify = (FILE_NOTIFY_INFORMATION*)((char*)buf + offset);
           strcpy(filename, "");
           int filenamelen = WideCharToMultiByte(CP_ACP, 0, pNotify->FileName, pNotify->FileNameLength/2, filename, sizeof(filename), NULL, NULL);
           filename[pNotify->FileNameLength/2] = '';
           switch(pNotify->Action)
           {
               case FILE_ACTION_ADDED:
                   printf("\nThe file is added to the directory: [%s] \n", filename);
                   break;
               case FILE_ACTION_REMOVED:
                   printf("\nThe file is removed from the directory: [%s] \n", filename);
                   break;
               case FILE_ACTION_MODIFIED:
                   printf("\nThe file is modified. This can be a change in the time stamp or attributes: [%s]\n", filename);
                   break;
               case FILE_ACTION_RENAMED_OLD_NAME:
                   printf("\nThe file was renamed and this is the old name: [%s]\n", filename);
                   break;
               case FILE_ACTION_RENAMED_NEW_NAME:
                   printf("\nThe file was renamed and this is the new name: [%s]\n", filename);
                   break;
               default:
                   printf("\nDefault error.\n");
                   break;
            }

           offset += pNotify->NextEntryOffset;

        }while(pNotify->NextEntryOffset); //(offset != 0);
      }

    CloseHandle( DirInfo[0].hDir );

}

The main thread could create the OVERLAPPED struct and give it to the thread to use, rather than the other way around.主线程可以创建OVERLAPPED结构并将其提供给线程使用,而不是相反。 However, trying to cancel the I/O from the main thread would be a race condition either way.但是,无论哪种方式,尝试从主线程取消 I/O 都将是一种竞争条件。 Since your worker thread has to make a new call to ReadDirectoryChangesEx() after every directory event, it could be between calls to ReadDirectoryChangesEx() when the main thread wants the worker thread to terminate, thus calling CancelIoEx() when there is no I/O in progress would be a no-op.由于您的工作线程必须在每个目录事件之后对ReadDirectoryChangesEx()进行新调用,因此当主线程希望工作线程终止时,它可能在对ReadDirectoryChangesEx()的调用之间,因此在没有 I/ 时调用CancelIoEx() O in progress 将是一个空操作。

Instead, create another event object for the main thread and worker thread to share, in addition to the event object that you are creating for the I/O.相反,除了要为 I/O 创建的事件 object 之外,还要创建另一个事件 object供主线程和工作线程共享。 Have the worker thread wait on both events at the same time with WaitForMultipleObjects() , and then the main thread can signal the shared event when it wants the worker thread to terminate.使用WaitForMultipleObjects()让工作线程同时等待两个事件,然后主线程可以在希望工作线程终止时发出共享事件的信号。

WaitForMultipleObjects() will tell the worker thread which event was signaled. WaitForMultipleObjects()将告诉工作线程哪个事件发出信号。 If the shared event is signaled, the worker thread can cancel its I/O in progress via CancelIo/Ex() before exiting.如果共享事件发出信号,工作线程可以在退出之前通过CancelIo/Ex()取消其正在进行的 I/O。

// shared with both threads...
HANDLE hTermEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// in main thread...
HANDLE hWorkerThread = CreateThread(...);
...
SetEvent(hTermEvent);
WaitForSingleObject(hWorkerThread, INFINITE);
// called by worker thread...
void WatchDirectory(LPCWSTR path)
{
   DWORD buf[512];
   DWORD nRet, dwRet;
   char filename[MAX_PATH];
   DirInfo[0].hDir = CreateFile(path, GENERIC_READ | FILE_LIST_DIRECTORY, 
                                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                                 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                                 NULL);

   if (DirInfo[0].hDir == INVALID_HANDLE_VALUE)
   {
       return; //cannot open folder
   }

   lstrcpy(DirInfo[0].lpszDirName, path);

   OVERLAPPED PollingOverlap = {};
   PollingOverlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (!PollingOverlap.hEvent)
   {
       return; //cannot create I/O event to wait on
   }

   FILE_NOTIFY_INFORMATION* pNotify;
   int offset;

   do
   {
       if (!ReadDirectoryChangesW(
              DirInfo[0].hDir,// handle to the directory to be watched
              &buf,// pointer to the buffer to receive the read results
              sizeof(buf),// length of lpBuffer
              TRUE,// flag for monitoring directory or directory tree
              FILE_NOTIFY_CHANGE_FILE_NAME |
              FILE_NOTIFY_CHANGE_DIR_NAME |
              FILE_NOTIFY_CHANGE_SIZE,
              //FILE_NOTIFY_CHANGE_LAST_WRITE |
              //FILE_NOTIFY_CHANGE_LAST_ACCESS |
              //FILE_NOTIFY_CHANGE_CREATION,
              &nRet,// number of bytes returned
              &PollingOverlap,// pointer to structure needed for overlapped I/O
              NULL))
       {
           break; // can't wait for an event
       }

       HANDLE events[] = {hTermEvent, PollingOverlap.hEvent};

       dwRet = WaitForMultipleObjects(2, events, FALSE, INFINITE);
       if (dwRet != (WAIT_OBJECT_0 + 1))
       {
           CancelIo(DirInfo[0].hDir);
           GetOverlappedResult(DirInfo[0].hDir, &PollingOverlap, &nRet, TRUE);
           break; // terminate requested, or wait failed
       }

       if (!GetOverlappedResult(DirInfo[0].hDir, &PollingOverlap, &nRet, TRUE))
       {
           break; // read failed
       }

       if (nRet == 0)
       {
           continue; // overflow, current event data discarded
       }

       offset = 0;
       int rename = 0;
       char oldName[MAX_PATH];
       char newName[MAX_PATH];
       do
       {
           pNotify = (FILE_NOTIFY_INFORMATION*) (buf + offset);
           int filenamelen = WideCharToMultiByte(CP_ACP, 0, pNotify->FileName, pNotify->FileNameLength/2, filename, sizeof(filename), NULL, NULL);
           switch (pNotify->Action)
           {
               case FILE_ACTION_ADDED:
                   printf("\nThe file is added to the directory: [%.*s] \n", filenamelen, filename);
                   break;
               case FILE_ACTION_REMOVED:
                   printf("\nThe file is removed from the directory: [%.*s] \n", filenamelen, filename);
                   break;
               case FILE_ACTION_MODIFIED:
                   printf("\nThe file is modified. This can be a change in the time stamp or attributes: [%.*s]\n", filenamelen, filename);
                   break;
               case FILE_ACTION_RENAMED_OLD_NAME:
                   printf("\nThe file was renamed and this is the old name: [%.*s]\n", filenamelen, filename);
                   break;
               case FILE_ACTION_RENAMED_NEW_NAME:
                   printf("\nThe file was renamed and this is the new name: [%.*s]\n", filenamelen, filename);
                   break;
               default:
                   printf("\nDefault error.\n");
                   break;
           }

           offset += pNotify->NextEntryOffset;
       }
       while (pNotify->NextEntryOffset);
   }
   while (true);

   CloseHandle(PollingOverlap.hEvent);
   CloseHandle(DirInfo[0].hDir);
}

terminate thread never safe.终止线程永远不安全。 how minimum this lead to resource leaks.这导致资源泄漏的程度如何。 and main - you never can know at which point thread was when you terminate it.和 main - 您永远无法知道线程在何时终止。 it can for example be inside heap alloc/free critical section.例如,它可以在堆分配/释放临界区内。 and terminate it at this point lead to deadlock on next heap operation (because it critical section will never released).并在此时终止它会导致下一次堆操作死锁(因为它的临界区永远不会释放)。

however exist many correct solutions, how stop I/O.但是存在许多正确的解决方案,如何停止 I/O。 possible of course use 2 special event how already described in comments, but in my opinion this is not the best solution当然可以使用评论中已经描述的2个特殊事件,但我认为这不是最好的解决方案

1) we can use CancelIoEx on file handle. 1)我们可以在文件句柄上使用CancelIoEx of course simply call CancelIoEx not enough - because at this time can be no I/O active in dedicated thread.当然仅仅调用CancelIoEx是不够的——因为此时在专用线程中可能没有 I/O 活动。 need yet use special flag ( _bQuit ) for task canceled, but even this not enough.还需要使用特殊标志( _bQuit )来取消任务,但这还不够。 need check/set this flag inside critical section or rundown-protection with ReadDirectoryChangesW/CancelIoEx需要在关键部分检查/设置此标志或使用ReadDirectoryChangesW/CancelIoEx进行故障保护

in dedicated thread在专用线程中

AcquireSRWLockExclusive(this);

if (!_bQuit) // (1)
{
    ReadDirectoryChangesW(*); // (4)
}

ReleaseSRWLockExclusive(this);

and for stop并停止

AcquireSRWLockExclusive(this);

_bQuit = true; // (2)
CancelIoEx(*); (3)

ReleaseSRWLockExclusive(this);

without critical section or rundown-protection will be possible execution in next order:没有关键部分或损坏保护将可能按下一个顺序执行:

if (!_bQuit) // (1)
_bQuit = true; // (2)
CancelIoEx(*); (3)
ReadDirectoryChangesW(*); // (4)

can be situation when worked thread first check flag _bQuit and it still false.可能是工作线程首先检查标志 _bQuit 并且它仍然为假的情况。 then main thread set flag and call CancelIoEx which will be have no effect, because no I/O on file.然后主线程设置标志并调用CancelIoEx这将无效,因为文件上没有 I/O。 and only then worked thread call ReadDirectoryChangesW whicj will be not canceled.只有这样工作的线程调用ReadDirectoryChangesW才会被取消。 but by use critical section (in wide sense) we make this impossible.但是通过使用临界区(广义上),我们使这成为不可能。 so possible only 2 orders: or所以可能只有 2 个订单:或

if (!_bQuit) ReadDirectoryChangesW(*); // (1)
_bQuit = true; CancelIoEx(*); // (2)

in this case ReadDirectoryChangesW will be canceled by CancelIoEx在这种情况下, ReadDirectoryChangesW将被CancelIoEx取消

or或者

_bQuit = true; CancelIoEx(*); // (1)
if (!_bQuit) ReadDirectoryChangesW(*); // (2)

in this case worked thread view _bQuit flag set and not call ReadDirectoryChangesW more.在这种情况下,工作线程视图_bQuit标志设置,而不是调用ReadDirectoryChangesW更多。

in complete code this can look like:在完整的代码中,这看起来像:

inline ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

struct WatchFolder : SRWLOCK
{
    HANDLE _hThread, _hFile;
    BOOLEAN _bQuit;

    WatchFolder() : _hThread(0), _hFile(0), _bQuit(false)
    {
        InitializeSRWLock(this);
    }

    ~WatchFolder()
    {
        if (_hThread) {
            WaitForSingleObject(_hThread, INFINITE);
            CloseHandle(_hThread);
        }
        if (_hFile) CloseHandle(_hFile);
    }

    static ULONG CALLBACK _WatchDirectory(PVOID This)
    {
        reinterpret_cast<WatchFolder*>(This)->WatchDirectory();
        return 0;
    }

    void WatchDirectory()
    {
        OVERLAPPED ov {};

        if (ov.hEvent = CreateEvent(0, 0, 0, 0))
        {
            union {
                FILE_NOTIFY_INFORMATION fni;
                char buf[0x800];// must be aligned as FILE_NOTIFY_INFORMATION
            };

            for(;;) 
            {
                AcquireSRWLockExclusive(this);

                ULONG dwError = _bQuit ? ERROR_OPERATION_ABORTED : BOOL_TO_ERROR(
                    ReadDirectoryChangesW(_hFile, buf, sizeof(buf), TRUE, FILE_NOTIFY_VALID_MASK, 0, &ov, 0));

                ReleaseSRWLockExclusive(this);

                ULONG NumberOfBytesTransferred = 0;

                if (dwError == NOERROR)
                {
                    dwError = BOOL_TO_ERROR(GetOverlappedResult(_hFile, &ov, &NumberOfBytesTransferred, TRUE));
                }

                if (dwError || !NumberOfBytesTransferred)
                {
                    if (dwError != ERROR_OPERATION_ABORTED)
                    {
                        __nop();
                    }
                    break;
                }

                FILE_NOTIFY_INFORMATION* pNotify = &fni;

                ULONG NextEntryOffset = 0;
                do 
                {
                    (PBYTE&)pNotify += NextEntryOffset;

                    DbgPrint("%x %.*S\n", pNotify->Action, pNotify->FileNameLength / sizeof(WCHAR), pNotify->FileName);

                } while (NextEntryOffset = pNotify->NextEntryOffset);
            }

            CloseHandle(ov.hEvent);
        }
    }

    ULONG Start(PCWSTR szFile)
    {
        HANDLE hFile = CreateFileW(szFile, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS,
            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL);

        ULONG dwError;

        if (hFile != INVALID_HANDLE_VALUE)
        {
            if (_hThread = CreateThread(0, 0, _WatchDirectory, this, 0, 0))
            {
                _hFile = hFile;

                return NOERROR;
            }
            dwError = GetLastError();
            CloseHandle(hFile);
        }
        else
        {
            dwError = GetLastError();
        }

        return dwError;
    }

    void Stop()
    {
        AcquireSRWLockExclusive(this);

        _bQuit = true, CancelIoEx(_hFile, 0);

        ReleaseSRWLockExclusive(this);
    }
};

void test()
{
    WatchFolder wf;
    if (wf.Start(L"somepath") == NOERROR)
    {
        MessageBoxW(0,0,0,0);
        wf.Stop();
    }
}

2) another way do this simply call CloseHandle(_hFile) instead CancelIoEx(_hFile, 0); 2) 另一种方法是简单地调用CloseHandle(_hFile)而不是CancelIoEx(_hFile, 0); . . when handle(last, but assume you have only single handle) is closed - system end complete ReadDirectoryChangesW with status STATUS_NOTIFY_CLEANUP .当句柄(最后一个,但假设您只有一个句柄)关闭时 - 系统结束完成ReadDirectoryChangesW状态STATUS_NOTIFY_CLEANUP code will be very similar to case CancelIoEx except now error on termination will be ERROR_NOTIFY_CLEANUP instead ERROR_OPERATION_ABORTED .代码将与CancelIoEx非常相似,只是现在终止时的错误将是ERROR_NOTIFY_CLEANUP而不是ERROR_OPERATION_ABORTED but if use GetOverlappedResult[Ex] exist problem - this api have error in implementation - it lost all positive status values.但如果使用GetOverlappedResult[Ex]存在问题 - 此 api 在实施中有错误 - 它丢失了所有正状态值。 it simply lost STATUS_NOTIFY_CLEANUP (but we of course can view it in Internal field of OVERLAPPED . code can be next:它只是丢失了STATUS_NOTIFY_CLEANUP (但我们当然可以在OVERLAPPEDInternal字段中查看它。代码可以是下一个:

            AcquireSRWLockExclusive(this);

            ULONG dwError = _bQuit ? ERROR_NOTIFY_CLEANUP : BOOL_TO_ERROR(
                ReadDirectoryChangesW(_hFile, buf, sizeof(buf), TRUE, FILE_NOTIFY_VALID_MASK, 0, &ov, 0));

            ReleaseSRWLockExclusive(this);

            ULONG NumberOfBytesTransferred = 0;

            if (dwError == NOERROR)
            {
                dwError = BOOL_TO_ERROR(GetOverlappedResult(_hFile, &ov, &NumberOfBytesTransferred, TRUE));
                // fix for error in GetOverlappedResult
                if (dwError == NOERROR && ov.Internal) dwError = RtlNtStatusToDosError((NTSTATUS)ov.Internal);
            }

            if (dwError || !NumberOfBytesTransferred)
            {
                if (dwError != ERROR_NOTIFY_CLEANUP)
                {
                    __nop();
                }
                break;
            }

and for stop并停止

    AcquireSRWLockExclusive(this);

    _bQuit = true, CloseHandle(_hFile), _hFile = 0;

    ReleaseSRWLockExclusive(this);

3) else one option use alertable wait inside GetOverlappedResultEx and insert apc (or alert to worked thread). 3) 否则一个选项在GetOverlappedResultEx内使用警报等待并插入 apc(或向工作线程发出警报)。 in this case we not need use critical section/or rundown-protection - because no matter will be apc (or alert) inserted before or after call ReadDirectoryChangesW - it anyway break wait.在这种情况下,我们不需要使用关键部分/或损坏保护 - 因为无论是在调用ReadDirectoryChangesW之前还是之后插入 apc(或警报) - 它无论如何都会中断等待。

            ULONG dwError = _bQuit ? STATUS_USER_APC : BOOL_TO_ERROR(
                ReadDirectoryChangesW(_hFile, buf, sizeof(buf), TRUE, FILE_NOTIFY_VALID_MASK, 0, &ov, 0));

            ULONG NumberOfBytesTransferred = 0;

            if (dwError == NOERROR)
            {
                dwError = BOOL_TO_ERROR(GetOverlappedResultEx(_hFile, &ov, &NumberOfBytesTransferred, INFINITE, TRUE));
            }

            if (dwError || !NumberOfBytesTransferred)
            {
                if (dwError == STATUS_USER_APC)
                {
                    CancelIo(_hFile);
                    GetOverlappedResult(_hFile, &ov, &NumberOfBytesTransferred, TRUE);
                }
                break;
            }

and for stop we need我们需要停下来

static VOID NTAPI dummyAPC(_In_ ULONG_PTR )
{

}

_bQuit = true;
QueueUserAPC(dummyAPC, _hThread, 0);

of course instead call dummyAPC (which not need) better use alert, but GetOverlappedResultEx (more exactly WaitForSingleObjectEx ) ignore STATUS_ALERT and again begin wait after it interrupted with STATUS_ALERT .当然,改为调用dummyAPC (不需要)更好地使用警报,但 GetOverlappedResultEx (更确切地说是WaitForSingleObjectEx )忽略STATUS_ALERT并在它被STATUS_ALERT中断后再次开始等待。 so need use custom code here所以需要在这里使用自定义代码

ULONG
WINAPI
GetOverlappedResult2( _In_ LPOVERLAPPED lpOverlapped,
                      _Out_ PULONG_PTR lpNumberOfBytesTransferred)
{
    while (lpOverlapped->Internal == STATUS_PENDING)
    {
        if (NTSTATUS status = ZwWaitForSingleObject(lpOverlapped->hEvent, TRUE, 0))
        {
            return RtlNtStatusToDosError(status);
        }
    }

    KeMemoryBarrier();

    *lpNumberOfBytesTransferred = lpOverlapped->InternalHigh;

    return RtlNtStatusToDosError((NTSTATUS)lpOverlapped->Internal);
}

and with it can use next code:并可以使用下一个代码:

            ULONG dwError = _bQuit ? ERROR_ALERTED : BOOL_TO_ERROR(
                ReadDirectoryChangesW(_hFile, buf, sizeof(buf), TRUE, FILE_NOTIFY_VALID_MASK, 0, &ov, 0));

            ULONG_PTR NumberOfBytesTransferred = 0;

            if (dwError == NOERROR)
            {
                dwError = GetOverlappedResult2(&ov, &NumberOfBytesTransferred);
            }

            if (dwError || !NumberOfBytesTransferred)
            {
                if (dwError == ERROR_ALERTED)
                {
                    CancelIo(_hFile);
                    GetOverlappedResult(_hFile, &ov, (ULONG*)&NumberOfBytesTransferred, TRUE);
                }
                break;
            }

and for stop并停止

_bQuit = true;
NtAlertThread(_hThread);

4) however the best way for my option - not use dedicated thread all all, but use complete asynchronous I/O. 4)但是我选择的最佳方式 - 不完全使用专用线程,而是使用完整的异步 I/O。 example of code代码示例

struct WatchFolderCB : SRWLOCK, OVERLAPPED
{
    HANDLE _hFile;
    LONG _dwRefCount;
    union {
        FILE_NOTIFY_INFORMATION fni;
        char buf[0x800];// must be aligned as FILE_NOTIFY_INFORMATION
    };
    BOOLEAN _bQuit;

    void AddRef()
    {
        InterlockedIncrementNoFence(&_dwRefCount);
    }

    void Release()
    {
        if (!InterlockedDecrement(&_dwRefCount))
        {
            delete this;
        }
    }

    WatchFolderCB() : _hFile(0), _bQuit(false), _dwRefCount(1)
    {
        InitializeSRWLock(this);
        RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
    }

    ~WatchFolderCB()
    {
        if (_hFile) CloseHandle(_hFile);
    }

    static VOID WINAPI _IoCompletionCallback(
        _In_    DWORD dwErrorCode,
        _In_    DWORD dwNumberOfBytesTransfered,
        _Inout_ LPOVERLAPPED lpOverlapped
        )
    {
        static_cast<WatchFolderCB*>(lpOverlapped)->IoCompletionCallback(
            RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
    }

    VOID IoCompletionCallback(DWORD dwErrorCode, DWORD NumberOfBytesTransferred)
    {
        if (dwErrorCode || !NumberOfBytesTransferred)
        {
            if (dwErrorCode != ERROR_NOTIFY_CLEANUP)
            {
                __nop();
            }
        }
        else
        {
            FILE_NOTIFY_INFORMATION* pNotify = &fni;

            ULONG NextEntryOffset = 0;
            do 
            {
                (PBYTE&)pNotify += NextEntryOffset;

                DbgPrint("%x %.*S\n", pNotify->Action, pNotify->FileNameLength / sizeof(WCHAR), pNotify->FileName);

            } while (NextEntryOffset = pNotify->NextEntryOffset);

            ReadChanges();
        }

        Release();
    }

    void ReadChanges()
    {
        AddRef();

        AcquireSRWLockExclusive(this);

        ULONG dwError = _bQuit ? ERROR_NOTIFY_CLEANUP : BOOL_TO_ERROR(
            ReadDirectoryChangesW(_hFile, buf, sizeof(buf), TRUE, FILE_NOTIFY_VALID_MASK, 0, this, 0));

        ReleaseSRWLockExclusive(this);

        if (dwError)
        {
            IoCompletionCallback(dwError, 0);
        }
    }

    ULONG Start(PCWSTR szFile)
    {
        HANDLE hFile = CreateFileW(szFile, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS,
            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL);

        ULONG dwError;

        if (hFile != INVALID_HANDLE_VALUE)
        {
            if (BindIoCompletionCallback(hFile, _IoCompletionCallback, 0))
            {
                _hFile = hFile;
                ReadChanges();
                return NOERROR;
            }
            dwError = GetLastError();
            CloseHandle(hFile);
        }
        else
        {
            dwError = GetLastError();
        }

        return dwError;
    }

    void Stop()
    {
        AcquireSRWLockExclusive(this);

        _bQuit = true, CloseHandle(_hFile), _hFile = 0;

        ReleaseSRWLockExclusive(this);
    }
};


void test1()
{
    if (WatchFolderCB* p = new WatchFolderCB)
    {
        if (p->Start(L"*") == NOERROR)
        {
            MessageBoxW(0,0,0,0);
            p->Stop();
        }
        p->Release();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM