83

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

4

6 回答 6

122

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

跨平台

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

视窗 (Win32)

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

Windows(.NET 框架)

如果您可以在 .NET Framework 中使用 C++/CLI,那么 System.IO.FileSystemWatcher是您的选择。Microsoft 有一篇关于 如何使用此类监视文件系统更改的好文章。

操作系统

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

Linux

正如亚历克斯在他的回答中提到的那样使用inotify 。

于 2009-05-31T03:10:24.390 回答
22

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

于 2009-05-31T02:16:48.570 回答
13

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

于 2015-04-07T11:05:38.760 回答
5

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

于 2009-05-31T02:13:57.780 回答
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 );
}
于 2015-03-04T10:15:23.793 回答
1

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

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

你可以在这里查看文档,注意文档中说的通知相关的 API 不是很一致。

于 2019-09-11T09:41:49.950 回答