如何使用C ++查找Windows中是否修改了文件?

时间:2017-11-25 05:37:31

标签: c++ windows winapi

我试图在使用C ++的Windows中使用FirstChangeNotification来查找是否修改了文件。我可以使用FileSystemWatcher类来做同样的事吗?如果可能,任何人都可以为我提供两种解决方案吗?我搜索并找到了我无法理解的片段,因为我是这些主题的初学者。

     #define _WIN32_WINNT 0x0501
     #include <windows.h>
     #include <stdio.h>

     int main(int argc, char argv[])
     {
         DWORD dwWaitStatus;
         HANDLE dwChangeHandles[2];
         LPCWSTR DirName = L"F:\\myproject";
         LPCWSTR DirName1 = L"F:\\";

         dwChangeHandles[0] = FindFirstChangeNotification(
         DirName,FALSE,FILE_NOTIFY_CHANGE_FILE_NAME);

         if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)
             ExitProcess(GetLastError());
         else
             printf("FindFirstChangeNotification() for file change is 
             OK.\n");


         dwChangeHandles[1] = FindFirstChangeNotification(
         DirName1,TRUE,FILE_NOTIFY_CHANGE_DIR_NAME);

         if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)
         {
               printf("Something wrong!\n");
               ExitProcess(GetLastError());
         }
         else
               printf("FindFirstChangeNotification() for directory change is 
                OK.\n");

         if (dwChangeHandles[0] != INVALID_HANDLE_VALUE && 
                               dwChangeHandles[1] != INVALID_HANDLE_VALUE)
         {
              printf("\nI'm monitoring any file deletion/creation in %S 
                     and\n", DirName);
              printf("I'm monitoring any directory deletion/creation in 
                     %S.\n", DirName1);
     }

     while (TRUE)
     {
         dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, FALSE, 
          INFINITE);
         switch (dwWaitStatus)
         {
             case 0: 
             if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE)
             {
                 printf("FindNextChangeNotification() not OK\n");
                 ExitProcess(GetLastError());
              }
              else
              printf("File created/deleted in %S.\n", DirName);
              break;

              case 1:

              if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)
                   ExitProcess(GetLastError());
              else
                   printf("Directory was deleted/created in %S.\n", 
                          DirName1);
              break;

        default:
                 printf("FindNextChangeNotification(): Invalid return 
                           value.\n");
                  ExitProcess(GetLastError());
           }
       }

       if(FindCloseChangeNotification(dwChangeHandles[0]) != 0)
             printf("FindCloseChangeNotification() is OK\n");
       if(FindCloseChangeNotification(dwChangeHandles[1]) != 0)
             printf("FindCloseChangeNotification() is OK\n");
        return 0;
    }

以上代码在C中,我已将其更改为C ++并执行它..它适用于文件创建和删除..但是当我使用FILE_NOTIFY_CHANGE_LAST_WRITE而不是FILE_NOTIFY_FILE_NAME时,它会打印出来 声明两次。

1 个答案:

答案 0 :(得分:4)

最有效的方法 - 将ReadDirectoryChangesW异步文件句柄一起使用。我们创建了一个类,它封装了文件夹句柄和所有必需的信息(例如窗口句柄,以及更改后发布通知的位置)。文件句柄我们通过BindIoCompletionCallback绑定到系统iocp。每当发生一些修改时结果 - 我们的回调将在系统线程中自动调用。在这里我们可以直接进行流程更改或者将通知/数据发布到gui线程。等等,此后,如果没有错误,再次回拨呼叫ReadDirectoryChangesW等等。当我们最终想要停止监控时 - 我们可以简单地关闭文件句柄。结果当前活动的i / o请求将以错误ERROR_NOTIFY_CLEANUP完成,或者如果i / o请求未激活,当我们关闭句柄时,我们检测到这一点并使用错误代码直接调用回调。当回调出错时,我们停止监视并释放对象。有了这个,我们可以创建任何计数的监视器对象,并继续执行其他任务。说gui线程 - 继续运行消息循环。例如:

#include <windows.h>

#ifdef __cplusplus
extern "C" {
#endif

NTSYSAPI
ULONG
__cdecl
DbgPrint (
          _In_z_ _Printf_format_string_ PCSTR Format,
          ...
          );

NTSYSAPI
ULONG
NTAPI
RtlNtStatusToDosError (
                       _In_ NTSTATUS Status
                       );

#ifdef __cplusplus
}
#endif

class CMonitor
{
    friend struct NDC_IRP;

    HANDLE _hFile;
    DWORD _dwNotifyFilter;
    LONG _nRef;
    LONG _HandleLock;

    BOOL LockHandle();

    void UnlockHandle();

    ~CMonitor()
    {
        DbgPrint("%s<%p>\n", __FUNCTION__, this);
        Close();
    }

    BOOL OnComplete(NDC_IRP* Irp, DWORD dwErrorCode, ULONG dwNumberOfBytesTransfered, PFILE_NOTIFY_INFORMATION pfni );

public:
    CMonitor(DWORD dwNotifyFilter) : _nRef(1), _HandleLock(0), _hFile(0)
    { 
        _dwNotifyFilter = dwNotifyFilter;
        DbgPrint("%s<%p>\n", __FUNCTION__, this);
    }

    void AddRef()
    {
        InterlockedIncrement(&_nRef);
    }

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

    void Assign(HANDLE hFile)
    {
        _hFile = hFile, _HandleLock = 0x80000000;
    }

    void Close();

    ULONG Open(PCWSTR FileName);

    ULONG DoRead();

    ULONG DoRead(NDC_IRP* irp);

    static void DumpDirectoryChanges(PVOID pv);
};

struct NDC_IRP : OVERLAPPED
{
    CMonitor* _pObj;

    union {
        FILE_NOTIFY_INFORMATION _fni;
        BYTE _buf[0x1000];// aligned as FILE_NOTIFY_INFORMATION
    };

    NDC_IRP(CMonitor* pObj) : _pObj(pObj)
    {
        pObj->AddRef();

        RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
    }

    ~NDC_IRP()
    {
        _pObj->Release();
    }

    VOID IOCompletionRoutine(DWORD dwErrorCode, ULONG dwNumberOfBytesTransfered)
    {
        if (_pObj->OnComplete(this, dwErrorCode, dwNumberOfBytesTransfered, &_fni))
        {
            delete this;
        }
    }

    static VOID CALLBACK _IOCompletionRoutine(ULONG status, ULONG dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
    {
        static_cast<NDC_IRP*>(lpOverlapped)->IOCompletionRoutine(RtlNtStatusToDosError(status), dwNumberOfBytesTransfered);
    }

    static ULONG Bind(HANDLE hFile)
    {
        return BindIoCompletionCallback(hFile, _IOCompletionRoutine, 0) ? NOERROR : GetLastError();
    }

    DWORD CheckErrorCode(DWORD dwErrorCode)
    {
        switch (dwErrorCode)
        {
        case NOERROR:
        case ERROR_IO_PENDING:
            break;
        default:
            IOCompletionRoutine(dwErrorCode, 0);
        }

        return ERROR_IO_PENDING;
    }
};

//////////////////////////////////////////////////////////////////////////
// CMonitor

void CMonitor::Close()
{
    if (LockHandle())
    {
        _interlockedbittestandreset(&_HandleLock, 31);
        UnlockHandle();
    }
}

BOOL CMonitor::LockHandle()
{
    LONG Value = _HandleLock, NewValue;

    for ( ; Value < 0; Value = NewValue)
    {
        NewValue = _InterlockedCompareExchange(&_HandleLock, Value + 1, Value);

        if (NewValue == Value) return TRUE;
    }

    return FALSE;
}

void CMonitor::UnlockHandle()
{
    if (!_InterlockedDecrement(&_HandleLock))
    {
        CloseHandle(_hFile);
        _hFile = 0;
    }
}

ULONG CMonitor::DoRead()
{
    if (NDC_IRP* irp = new NDC_IRP(this))
    {
        return DoRead(irp);
    }

    return ERROR_NO_SYSTEM_RESOURCES;
}

ULONG CMonitor::DoRead(NDC_IRP* irp)
{
    ULONG err = ERROR_INVALID_HANDLE;

    if (LockHandle())
    {
        err = ReadDirectoryChangesW(_hFile, 
            irp->_buf, sizeof(irp->_buf), TRUE, _dwNotifyFilter, 0, irp, 0)
            ? ERROR_IO_PENDING : GetLastError();

        UnlockHandle();
    }

    irp->CheckErrorCode(err);

    return NOERROR;
}

ULONG CMonitor::Open(PCWSTR FileName)
{
    HANDLE hFile = CreateFile(FileName, FILE_LIST_DIRECTORY, 
        FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0,
        OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, 0);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    if (ULONG err = NDC_IRP::Bind(hFile))
    {
        return err;
    }

    Assign(hFile);

    return NOERROR;
}

BOOL CMonitor::OnComplete(NDC_IRP* irp, DWORD dwErrorCode, ULONG dwNumberOfBytesTransfered, PFILE_NOTIFY_INFORMATION pfni )
{
    switch (dwErrorCode)
    {
    case ERROR_NOTIFY_CLEANUP:
        DbgPrint("%p> ---- NOTIFY_CLEANUP -----\n", this);
        break;

    case ERROR_NOTIFY_ENUM_DIR:
        DbgPrint("%p> ---- ERROR_NOTIFY_ENUM_DIR -----\n", this);
    case NOERROR:
        if (dwNumberOfBytesTransfered) 
        {
            DumpDirectoryChanges(pfni);
        }
        DoRead(irp);
        return FALSE;// reuse irp

    default:
        DbgPrint("%p> error=%x\n", this, dwErrorCode);
    }

    return TRUE;// free irp
}

void CMonitor::DumpDirectoryChanges(PVOID pv)
{
    union {
        PVOID buf;
        PBYTE pb;
        PFILE_NOTIFY_INFORMATION pfni;
    };

    buf = pv;

    for (;;)
    {
        DbgPrint("%x <%.*S>\n", pfni->Action, pfni->FileNameLength >> 1, pfni->FileName);

        ULONG NextEntryOffset = pfni->NextEntryOffset;

        if (!NextEntryOffset)
        {
            break;
        }

        pb += NextEntryOffset;
    }
}

#define FILE_NOTIFY_VALID_MASK          0x00000fff

void CloseMonitor(CMonitor*p)
{
    p->Close();
    p->Release();
}

ULONG CreateMonitor(CMonitor** ppvObj, PCWSTR FileName, DWORD dwNotifyFilter)
{
    if (CMonitor* p = new CMonitor(dwNotifyFilter))
    {
        ULONG err;

        if (!(err = p->Open(FileName)) && !(err = p->DoRead()))
        {
            *ppvObj = p;

            return NOERROR;
        }

        CloseMonitor(p);

        return err;
    }

    return ERROR_NO_SYSTEM_RESOURCES;
}

void demo()
{
    CMonitor *p, *q;

    if (!CreateMonitor(&p, L"c:\\", FILE_NOTIFY_VALID_MASK))
    {
        if (!CreateMonitor(&q, L"d:\\", FILE_NOTIFY_VALID_MASK))
        {
            MessageBoxW(0, 0, L"monitoring..", MB_ICONINFORMATION);
            CloseMonitor(q);
        }
        CloseMonitor(p);
    }
}