使用 ReadDirecotryChangesW 函数实现文件监控

背景

在我没有了解 ReadDirecotryChangesW 这个目录监控函数之前,一直认为要想实现计算机上的文件监控,能够监控计算机上每个文件的改动,是一件极其困难的事情,曾经自己也细想过,但都没有什么好的思绪。不过,事实上,文件监控的确是一件比较复杂的事情。好在Windows为我们提供了一个功能强大,但是使用较为方便的函数接口,这边是我们这篇文章要讲解的 ReadDirecotryChangesW 函数。

函数介绍

// 检索描述指定目录中更改的信息,但不会报告对指定目录本身的更改。
// 如果函数成功,则返回值不为零。 对于同步调用,这意味着操作成功。 对于异步调用,这表示操作成功排队。
BOOL WINAPI ReadDirectoryChangesW(
  _In_        HANDLE                          hDirectory,			// 要监视的目录的句柄。必须使用FILE_LIST_DIRECTORY访问权限打开此目录。
  _Out_       LPVOID                          lpBuffer,				// 指向要读取结果的DWORD对齐的格式化缓冲区的指针
  _In_        DWORD                           nBufferLength,		// lpBuffer参数指向的缓冲区的大小
  _In_        BOOL                            bWatchSubtree,		// 是否监视以指定目录为根的目录树
  _In_        DWORD                           dwNotifyFilter,		// 函数检查以确定等待操作是否已满足过滤条件
  _Out_opt_   LPDWORD                         lpBytesReturned,		// 接收传输到lpBuffer参数的字节数
  _Inout_opt_ LPOVERLAPPED                    lpOverlapped,			// 指向OVERLAPPED结构的指针,提供在异步操作期间要使用的数据
  _In_opt_    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine	// 指向完成例程的指针
);

实现过程

(1)打开目录,获取文件句柄

首先,我们需要根据目录路径,调用 CreateFile 函数来打开目录,获取文件句柄,因为下面的调用的 ReadDirecotryChangesW 函数需要用到这个文件句柄。根据上面函数介绍,文件句柄必须要有 FILE_LIST_DIRECTORY 权限,所以要创建 FILE_LIST_DIRECTORY 权限的文件句柄。而且,要获取目录的句柄,需要以 FILE_FLAG_BACKUP_SEMANTICS 为标志调用 CreateFile 函数。

(2)设置目录监控

然后,我们可以调用 ReadDirecotryChangesW 函数设置目录监控。其中,第 1 个参数表示监控目录句柄;第 2 个参数表示输出缓冲区;第 3 个参数表示输出缓冲区大小;第 4 个参数表示是否监控指定目录下的文件及其子目录下的文件,TRUE,则监控,FALSE则表示只监控指定目录下的文件;第 5 个参数表示操作过滤,本文只监控文件名更改、属性更改以及最后一次写入更改操作;第 6 个参数表示返回缓冲区的字节数;第 7 、第 8 个参数为NULL。

(3)判断文件操作类型并将宽字节文件名字符串转成多字节字符串表示

只要有满足设置条件的文件操作,ReadDirectoryChangesW 立马返回信息,并将返回的信息返回到输出缓冲区中,返回数据是按 FILE_NOTIFY_INFORMATION 结构返回的,所以,我们直接按照 FILE_NOTIFY_INFORMATION 结构解析数据。在 FILE_NOTIFY_INFORMATION 结构中,4字节整型变量 Action 表示操作类型,宽字节字符串变量 FileName 表示更改文件的文件名。获取了一个文件操作之后,还要继续循环设置。如此,才能获取下一个文件操作。

编码实现

// 宽字节字符串转多字节字符串
void W2C(wchar_t *pwszSrc, int iSrcLen, char *pszDest, int iDestLen)
{
    ::RtlZeroMemory(pszDest, iDestLen);
    // 宽字节字符串转多字节字符串
    ::WideCharToMultiByte(CP_ACP,
        0,
        pwszSrc,
        (iSrcLen / 2),
        pszDest,
        iDestLen,
        NULL,
        NULL);
}

// 目录监控多线程
UINT MonitorFileThreadProc(LPVOID lpVoid)
{
    char *pszDirectory = (char *)lpVoid;
    // 打开目录, 获取文件句柄
    HANDLE hDirectory = ::CreateFile(pszDirectory, FILE_LIST_DIRECTORY,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if (INVALID_HANDLE_VALUE == hDirectory)
    {
        ShowError("CreateFile");
        return 1;
    }
    char szTemp[MAX_PATH] = { 0 };
    BOOL bRet = FALSE;
    DWORD dwRet = 0;
    DWORD dwBufferSize = 2048;
    // 申请一个足够大的缓冲区 
    BYTE *pBuf = new BYTE[dwBufferSize];
    if (NULL == pBuf)
    {
        ShowError("new");
        return 2;
    }
    FILE_NOTIFY_INFORMATION *pFileNotifyInfo = (FILE_NOTIFY_INFORMATION *)pBuf;
    // 开始循环设置监控
    do
    {
        ::RtlZeroMemory(pFileNotifyInfo, dwBufferSize);
        // 设置监控目录
        bRet = ::ReadDirectoryChangesW(hDirectory,
            pFileNotifyInfo,
            dwBufferSize,
            TRUE,
            FILE_NOTIFY_CHANGE_FILE_NAME  |            
            FILE_NOTIFY_CHANGE_ATTRIBUTES |            
            FILE_NOTIFY_CHANGE_LAST_WRITE,            
            &dwRet,
            NULL,
            NULL);
        if (FALSE == bRet)
        {
            ShowError("ReadDirectoryChangesW");
            break;
        }
        // 将宽字符转换成窄字符
        W2C((wchar_t *)(&pFileNotifyInfo->FileName), pFileNotifyInfo->FileNameLength, szTemp, MAX_PATH);
        // 判断操作类型并显示
        switch (pFileNotifyInfo->Action)
        {
        case FILE_ACTION_ADDED:
        {
            // 新增文件
            printf("[File Added Action]%s\n", szTemp);
            break;
        }
        default:
        {
            break;
        }
        }
    } while (bRet);
    // 关闭句柄, 释放内存
    ::CloseHandle(hDirectory);
    delete[] pBuf;
    pBuf = NULL;
    return 0;
}

测试

现在,根据上面的实现原理,我们将开发好的程序直接运行。本文的例子,是监控”C:\Users\DemonGan\Desktop\temp\“目录的文件增加情况。所以,我们复制一个文件到在此目录下,程序成功实时显示目录中的变化信息:在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值