如何让我的程序监视 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++ 中的文件修改?的主要内容,如果未能解决你的问题,请参考以下文章
压缩文件后(它们从 Visual Studio 中的文件路径引用打开),如何让我的应用程序从单独的 Winform 打开? (C#)
如何使用 Android 设备监视器的文件资源管理器查找我的应用程序的 /data/data