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

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

114315 次浏览

Sure, just like VC++ does. You get the last modified time when you open the file, and you periodically check it while you have the file open. If last_mod_time > saved_mod_time, it happened.

If you don't need to be platform-independent, an approach on Linux that may be less of a machine load than "polling" (checking periodically) is inotify, see http://en.wikipedia.org/wiki/Inotify and the many links from it for example. For Windows, see http://msdn.microsoft.com/en-us/library/aa365261(VS.85).aspx .

There are several ways to do this depending on the platform. I would choose from the following choices:

Cross Platform

Trolltech's Qt has an object called QFileSystemWatcher which allows you to monitor files and directories. I'm sure there are other cross platform frameworks that give you this sort of capability too, but this one works fairly well in my experience.

Windows (Win32)

There is a Win32 api called FindFirstChangeNotification which does the job. There is a nice article which a small wrapper class for the api called How to get a notification if change occurs in a specified directory which will get you started.

Windows (.NET Framework)

If you are ok using C++/CLI with the .NET Framework then System.IO.FileSystemWatcher is your class of choice. Microsoft has a nice article on how to monitor file system changes using this class.

OS X

The FSEvents API is new for OS X 10.5 and very full-featured.

Linux

Use inotify as Alex mentioned in his answer.

A working exemple for 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 );
}

SimpleFileWatcher might be what you are looking for. But of course it is an external dependency - maybe that is no option for you.

Add an answer for libuv (though it's written in C), it has support for both Windows and Linux with system-specific APIs:

inotify on Linux, FSEvents on Darwin, kqueue on BSDs, ReadDirectoryChangesW on Windows, event ports on Solaris, unsupported on Cygwin

You may check the document here, beware that the document says that the notification related APIs are not very consistent.