如何让我的程序监视 C++ 中的文件修改?

Posted

技术标签:

【中文标题】如何让我的程序监视 C++ 中的文件修改?【英文标题】:How do I make my program watch for file modification in C++? 【发布时间】:2010-10-30 04:29:42 【问题描述】:

有很多程序,例如 Visual Studio,可以检测外部程序何时修改文件,然后在用户需要时重新加载文件。有没有一种相对简单的方法可以在 C++ 中做这种事情(不一定必须独立于平台)?

【问题讨论】:

【参考方案1】:

根据平台的不同,有几种方法可以做到这一点。我会从以下选项中进行选择:

跨平台

奇趣科技的 Qt 有一个名为 QFileSystemWatcher 的对象,它允许您监视文件和目录。我确信还有其他跨平台框架也可以为您提供这种功能,但根据我的经验,这个框架运行得相当好。

Windows (Win32)

有一个名为FindFirstChangeNotification 的 Win32 api 可以完成这项工作。有一篇不错的文章,其中一个名为 How to get a notification if change occurs in a specified directory 的 api 小包装类将帮助您入门。

Windows(.NET 框架)

如果您可以在 .NET Framework 中使用 C++/CLI,那么 System.IO.FileSystemWatcher 是您的首选课程。微软有一篇很好的文章 how to monitor file system changes 使用这个类。

操作系统

FSEvents API 是 OS X 10.5 的新 API,功能非常全面。

Linux

使用 inotify,正如 Alex 在他的回答中提到的那样。

【讨论】:

注意:inotify 是特定于 Linux 的,如果您想要一些 UNIX 可移植功能,您可能正在寻找类似 libfam 的东西 我认为您将 C++ 与 C++/CLI 混淆了。相似的名字,不同的语言。除此之外,这是一个彻底而有用的答案。 FileSystemWatcher 在 Windows 8 connect.microsoft.com/VisualStudio/feedback/details/772182/… 中存在问题(无法修复) QFileSystemWatcher 对它可以观看的文件数量有限制。doc.qt.io/qt-4.8/qfilesystemwatcher.html FSEvents 文档说“文件系统事件 API 也不是为找出特定文件何时更改而设计的。为此,kqueues 机制更合适。”【参考方案2】:

如果您不需要独立于平台,那么在 Linux 上可能比“轮询”(定期检查)更少的机器负载的方法是 inotify,请参阅 http://en.wikipedia.org/wiki/Inotify 以及其中的许多链接例如。对于 Windows,请参阅http://msdn.microsoft.com/en-us/library/aa365261(VS.85).aspx。

【讨论】:

好答案!这确实是一个操作系统级别的任务,很难跨平台。【参考方案3】:

SimpleFileWatcher 可能是您正在寻找的。但当然,它是一种外部依赖——也许这不是你的选择。

【讨论】:

超级简单轻量级的解决方案。谢谢。 @MartinGerhardy Github 链接已损坏 很棒的图书馆!如果您将文件直接包含到您的项目中,它不再是依赖项,而只是帮助文件......这就是我所做的,它很震撼! 注意:这个库有一个错误。如果您删除包含文件的子文件夹,当您删除该文件夹时,将不会触发已删除的文件 evetn(在 Windows 中)。也许为每个子文件夹添加一个监听器(您可以从文件添加事件中检测到一个新文件夹)可以解决它。 我最近将我的 io 处理切换到 libuv - 它还实现了文件/目录观察器支持并且是跨平台的。【参考方案4】:

当然,就像 VC++ 一样。当您打开文件时,您会获得上次修改时间,并在打开文件时定期检查它。如果 last_mod_time > saved_mod_time,它发生了。

【讨论】:

轮询是一种非常低效的方法。正如 Alex 所说,Windows 确实有可用的通知(当然我不知道 VS 是否使用它们)。 @Matthew “轮询是一种非常低效的方法。”废话。每 5 分钟调用一次 stat(2) 会产生 epsilon 影响。当您使用“低效”一词时,量化它的时间或成本,并将其与您花在寻找“高效”解决方案上的时间进行比较。如果在这种情况下,差异在 1e6 的数量级上,那么您可能进行了不正当的优化。 用于检查单个文件(如原始问题所述),轮询足够快。如果您想对无限深度目录的任何更改采取行动,它很快就会失控。 每个 /file/ 进行一次统计检查。如果您想监视目录树中的数百个文件(对于从事复杂项目的开发人员来说完全不合理)怎么办?至于寻找解决方案的时间,我花了大约 10 秒才找到“目录更改通知”/FindFirstChangeNotification API。所以我不认为这是不成熟或不正当的。我也不认为在说明显而易见的情况时我需要给出确切的成本。 对此的一种可能的变体是仅在应用程序获得焦点时进行轮询。在文件仅由用户修改的情况下,这可以正常工作。我不确定同时进行大量变更注册的成本是多少......并且分析它实际上是不可能的,因为这些成本是连续的。不过,我怀疑它的成本很高。即便如此,民意调查也不是很糟糕。【参考方案5】:

WinCE 的工作示例

void FileInfoHelper::WatchFileChanges( TCHAR *ptcFileBaseDir, TCHAR *ptcFileName )
static int iCount = 0;
DWORD dwWaitStatus; 
HANDLE dwChangeHandles; 

if( ! ptcFileBaseDir || ! ptcFileName ) return;

wstring wszFileNameToWatch = ptcFileName;

dwChangeHandles = FindFirstChangeNotification(
    ptcFileBaseDir,
    FALSE,
    FILE_NOTIFY_CHANGE_FILE_NAME |
    FILE_NOTIFY_CHANGE_DIR_NAME |
    FILE_NOTIFY_CHANGE_ATTRIBUTES |
    FILE_NOTIFY_CHANGE_SIZE |
    FILE_NOTIFY_CHANGE_LAST_WRITE |
    FILE_NOTIFY_CHANGE_LAST_ACCESS |
    FILE_NOTIFY_CHANGE_CREATION |
    FILE_NOTIFY_CHANGE_SECURITY |
    FILE_NOTIFY_CHANGE_CEGETINFO
    );

if (dwChangeHandles == INVALID_HANDLE_VALUE) 

    printf("\n ERROR: FindFirstChangeNotification function failed [%d].\n", GetLastError());
    return;


while (TRUE) 
 
    // Wait for notification.
    printf("\n\n[%d] Waiting for notification...\n", iCount);
    iCount++;

    dwWaitStatus = WaitForSingleObject(dwChangeHandles, INFINITE); 
    switch (dwWaitStatus) 
     
        case WAIT_OBJECT_0: 

            printf( "Change detected\n" );

            DWORD iBytesReturned, iBytesAvaible;
            if( CeGetFileNotificationInfo( dwChangeHandles, 0, NULL, 0, &iBytesReturned, &iBytesAvaible) != 0 ) 
            
                std::vector< BYTE > vecBuffer( iBytesAvaible );

                if( CeGetFileNotificationInfo( dwChangeHandles, 0, &vecBuffer.front(), vecBuffer.size(), &iBytesReturned, &iBytesAvaible) != 0 ) 
                    BYTE* p_bCurrent = &vecBuffer.front();
                    PFILE_NOTIFY_INFORMATION info = NULL;

                    do 
                        info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>( p_bCurrent );
                        p_bCurrent += info->NextEntryOffset;

                        if( wszFileNameToWatch.compare( info->FileName ) == 0 )
                        
                            wcout << "\n\t[" << info->FileName << "]: 0x" << ::hex << info->Action;

                            switch(info->Action) 
                                case FILE_ACTION_ADDED:
                                    break;
                                case FILE_ACTION_MODIFIED:
                                    break;
                                case FILE_ACTION_REMOVED:
                                    break;
                                case FILE_ACTION_RENAMED_NEW_NAME:
                                    break;
                                case FILE_ACTION_RENAMED_OLD_NAME:
                                    break;
                            
                        
                    while (info->NextEntryOffset != 0);
                
            

            if ( FindNextChangeNotification( dwChangeHandles ) == FALSE )
            
                printf("\n ERROR: FindNextChangeNotification function failed [%d].\n", GetLastError());
                return;
            

            break; 

        case WAIT_TIMEOUT:
            printf("\nNo changes in the timeout period.\n");
            break;

        default: 
            printf("\n ERROR: Unhandled dwWaitStatus [%d].\n", GetLastError());
            return;
            break;
    


FindCloseChangeNotification( dwChangeHandles );

【讨论】:

【参考方案6】:

为 libuv 添加一个答案(尽管它是用 C 编写的),它通过系统特定的 API 支持 Windows 和 Linux:

Linux 上的 inotify,Darwin 上的 FSEvents,BSD 上的 kqueue, Windows 上的 ReadDirectoryChangesW,Solaris 上的事件端口,不受支持 在 Cygwin 上

您可以查看文档here,注意文档说通知相关的API不是很一致。

【讨论】:

libuv 可以监视同一个文件系统中的文件移动吗? 文件移动似乎不是一个正常的文件系统事件,文档没有显示任何关于move事件的信息。

以上是关于如何让我的程序监视 C++ 中的文件修改?的主要内容,如果未能解决你的问题,请参考以下文章

如何让我的 c++ 程序继续运行?

压缩文件后(它们从 Visual Studio 中的文件路径引用打开),如何让我的应用程序从单独的 Winform 打开? (C#)

我的 C++ 程序的工作目录是啥? [复制]

如何使用 Android 设备监视器的文件资源管理器查找我的应用程序的 /data/data

如何让我的应用程序中的播放器播放 UNNotificationSound.default()?

如何在“ng build --prod”之后让我的 Angular 应用程序使用编辑过的配置文件