例如,Visual Studio等许多程序可以检测到外部程序修改文件,并在用户选择时重新加载文件。在C++中是否有一种相对简单的方法来执行此类操作(不一定需要跨平台)?
例如,Visual Studio等许多程序可以检测到外部程序修改文件,并在用户选择时重新加载文件。在C++中是否有一种相对简单的方法来执行此类操作(不一定需要跨平台)?
根据平台的不同,可以采用多种方法来实现此功能。以下是我会从以下选项中进行选择:
Trolltech's Qt有一个名为 QFileSystemWatcher 的对象,可允许您监视文件和目录。我相信还有其他跨平台框架也能提供这种功能,但在我使用经验中,这个框架效果还不错。
有一个名为FindFirstChangeNotification的Win32 API可以完成此任务。有一篇很好的文章介绍了该API的一个小封装类:如何获得指定目录发生更改的通知,可以帮助您入门。
如果您可以使用.NET框架的C++ / CLI,则System.IO.FileSystemWatcher就是您的首选类。微软有一篇很好的文章介绍如何使用这个类监视文件系统更改。
FSEvents API是适用于OS X 10.5及以上版本的,具有非常完整的功能。
如Alex在他的答案中提到的,可以使用inotify来实现此功能。
如果您不需要跨平台,Linux上一种可能比“轮询”(定期检查)更少机器负载的方法是使用inotify
,例如在http://en.wikipedia.org/wiki/Inotify及其相关链接中了解更多信息。对于Windows,请参见http://msdn.microsoft.com/en-us/library/aa365261(VS.85).aspx。
SimpleFileWatcher可能是您正在寻找的东西。但当然,它是一个外部依赖项 - 也许对您来说这不是一个选项。
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 );
}
为libuv添加一个答案(尽管它是用C编写的),它支持使用特定于系统的API在Windows和Linux上运行:
在Linux上使用inotify,在Darwin上使用FSEvents,在BSD上使用kqueue, 在Windows上使用ReadDirectoryChangesW,在Solaris上使用事件端口,在Cygwin上不支持
您可以在此处查看文档,但请注意,该文档表示与通知相关的API不太一致。
libuv
可以监视同一文件系统中的文件移动吗? - Some Name
FSEvents
在 macOS 上的链接已经失效。 - TChapman500