77 votes

Comment faire pour que mon programme surveille la modification des fichiers en C++ ?

Il y a beaucoup de programmes, Visual Studio par exemple, qui peuvent détecter quand un programme extérieur modifie un fichier et recharger le fichier si l'utilisateur le souhaite. Existe-t-il un moyen relativement facile de faire ce genre de chose en C++ (qui ne doit pas nécessairement être indépendant de la plate-forme) ?

119voto

Nick Haddad Points 4326

Il existe plusieurs façons de procéder, selon la plate-forme. Je choisirais parmi les choix suivants :

Plateforme croisée

Le Qt de Trolltech possède un objet appelé QFileSystemWatcher qui vous permet de surveiller les fichiers et les répertoires. Je suis sûr qu'il existe d'autres frameworks multiplateformes qui offrent ce genre de possibilités, mais celui-ci fonctionne assez bien selon mon expérience.

Windows (Win32)

Il existe une api Win32 appelée FindFirstChangeNotification qui fait l'affaire. Il y a un article sympa qui présente une petite classe wrapper pour l'api appelée Comment recevoir une notification en cas de changement dans un répertoire spécifié ? qui vous permettra de commencer.

Windows (.NET Framework)

Si vous n'avez pas de problème à utiliser C++/CLI avec le .NET Framework alors Système.IO.FileSystemWatcher est votre classe de prédilection. Microsoft propose un article intéressant sur comment surveiller les modifications du système de fichiers en utilisant cette classe.

OS X

El FSEvents L'API est nouvelle pour OS X 10.5 et très complète.

Linux

Utilisez inotify comme l'a mentionné Alex dans sa réponse.

3 votes

Note : inotify est spécifique à Linux, si vous voulez des fonctionnalités portables sous UNIX, vous devrez probablement chercher quelque chose comme libfam.

0 votes

Je pense que vous confondez C++ avec C++/CLI. Nom similaire, langage différent. À part cela, c'est une réponse complète et utile.

1 votes

FileSystemWatcher a des problèmes (qui ne seront pas corrigés) dans Windows 8 connect.microsoft.com/VisualStudio/feedback/details/772182/

21voto

Alex Martelli Points 330805

Si vous n'avez pas besoin d'être indépendant de la plate-forme, une approche sous Linux qui peut représenter une charge machine moindre que le "polling" (vérification périodique) est la suivante inotify voir http://en.wikipedia.org/wiki/Inotify et les nombreux liens qui en découlent, par exemple. Pour Windows, voir http://msdn.microsoft.com/en-us/library/aa365261(VS.85).aspx .

1 votes

Bonne réponse ! Il s'agit vraiment d'une tâche au niveau du système d'exploitation qu'il serait difficile de rendre multiplateforme.

13voto

Martin Gerhardy Points 51

SimpleFileWatcher pourrait être ce que vous recherchez. Mais il s'agit bien sûr d'une dépendance externe - peut-être n'est-ce pas une option pour vous.

5voto

Ataginsky Points 51

Un exemple fonctionnel pour 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 );
}

5voto

Charlie Martin Points 62306

Bien sûr, comme le fait VC++. Vous obtenez la dernière heure de modification lorsque vous ouvrez le fichier, et vous la vérifiez périodiquement tant que le fichier est ouvert. Si last_mod_time > saved_mod_time, cela s'est produit.

10 votes

Les sondages sont un moyen très inefficace de le faire. Comme Alex l'a noté, Windows dispose de notifications (bien que je ne sache pas si VS les utilise).

17 votes

@Matthew "Les sondages sont un moyen très inefficace de faire cela." C'est absurde. Un appel stat(2) toutes les 5 minutes a un impact epsilon. Lorsque vous utilisez le mot "inefficace", quantifiez le temps ou le coût que cela représente, et comparez-le au temps que vous passez à chercher des solutions "efficaces". Si, comme dans ce cas, la différence est de l'ordre de 1e6, vous faites probablement une optimisation perverse.

9 votes

Pour vérifier un seul fichier (comme le mentionne la question originale), polling est assez rapide. si vous voulez agir sur n'importe quel changement sur un répertoire à profondeur non limitée, cela peut rapidement devenir incontrôlable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X