Подтвердить что ты не робот

Как заставить мою программу следить за модификацией файла на С++?

Есть много программ, например Visual Studio, которые могут обнаружить, когда внешняя программа изменяет файл, а затем перезагружает файл, если пользователь хочет выбирать. Есть ли относительно простой способ делать подобные вещи в С++ (необязательно, должен быть независимым от платформы)?

4b9b3361

Ответ 1

Есть несколько способов сделать это в зависимости от платформы. Я бы выбрал один из следующих вариантов:

Кроссплатформенный

Trolltech Qt имеет объект с именем QFileSystemWatcher, который позволяет вам отслеживать файлы и каталоги. Я уверен, что есть и другие кроссплатформенные фреймворки, которые также предоставляют вам такую возможность, но, по моему опыту, эта работает довольно хорошо.

Windows (Win32)

Существует Win32 API под названием FindFirstChangeNotification, который делает эту работу. Есть хорошая статья, которая представляет собой небольшой класс-обертку для API под названием . Как получить уведомление, если в указанном каталоге произойдет изменение, которое поможет вам начать работу.

Windows (.NET Framework)

Если вы в порядке, используя C++/CLI с .NET Framework, тогда System.IO.FileSystemWatcher - ваш класс по выбору. У Microsoft есть хорошая статья на Как отслеживать изменения файловой системы с помощью этого класса.

OS X

FSEvents API является новым для OS X 10.5 и очень полнофункциональным.

Linux

Используйте inotify, как Алекс упомянул в своем ответе.

Ответ 3

SimpleFileWatcher может быть то, что вы ищете. Но, конечно, это внешняя зависимость - возможно, это не вариант для вас.

Ответ 4

Конечно, как и VС++. Вы получаете последнее измененное время, когда вы открываете файл, и периодически проверяете его, пока файл открыт. Если last_mod_time > saved_mod_time, это произошло.

Ответ 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), он поддерживает как Windows, так и Linux с системными API:

inotify на Linux, FSEvents на Darwin, kqueue на BSD, ReadDirectoryChangesW в Windows, порты событий в Solaris, не поддерживаются на Cygwin

Вы можете проверить документ здесь, помните, что в документе говорится, что API, связанные с уведомлениями, не очень согласованы.