中查找文件是否被修改

中查找文件是否被修改

本文介绍了如何使用 C++ 在 Windows 中查找文件是否被修改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to find whether a file is modified or not using FirstChangeNotification in Windows using C++. Can I use FileSystemWatcher class to do the same? Can anyone provide me with a solution for both ways if possible? I have searched and found snippets that I'm not able to understand since i am a beginner on these topics.

     #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 它打印声明两次.

The above code is in C and i have changed it to C++ and executed it.. It works well for file creation and deletion.. but when i use FILE_NOTIFY_CHANGE_LAST_WRITE instead of FILE_NOTIFY_FILE_NAME it printsthe statement twice.

推荐答案

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

most effective way do this - use ReadDirectoryChangesW with asynchronous file handle. we create class, which encapsulate folder handle and all required info (say for example window handle, to where post notification on change). file handle we bind to system iocp via BindIoCompletionCallback. as result every time when some modifications occur - our callback will be automatically called in system thread. here we can direct process changes or say post notify/data to gui thread. etc. after this, if no errors, callback again call ReadDirectoryChangesW and so on. when we finally want stop monitoring - we can simply close file handle. as result current active i/o request will be completed with error ERROR_NOTIFY_CLEANUP or if i/o request not active, when we close handle, we detect this and direct call callback with error code. when callback got error we stop monitoring and release object. with this we can create any count of monitor objects and continue execute another tasks. say gui thread - continue run message loop. for example:

#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);
    }
}

这篇关于如何使用 C++ 在 Windows 中查找文件是否被修改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 05:55