Можно ли использовать FSEvents для получения уведомлений о перемещении папки? - PullRequest
8 голосов
/ 27 апреля 2011

Я использую API FSEvents для получения уведомлений об изменениях в локальном каталоге, который я отслеживаю.

Можно ли получить уведомление о том, что просматриваемый каталог был перемещен в другое место на диске, используя FSEvents или что-то еще?

Обновление:

Вот код, который у меня есть, я сейчас пытаюсь использовать флаг kFSEventStreamCreateFlagWatchRoot с FSEventStreamCreate, чтобы получить уведомление об изменении корня, поэтомудалеко без успеха.

- (void)registerForFileSystemNotifications {

    NSString *watchedDirectoryPath = [[NSUserDefaults standardUserDefaults] valueForKey:kMyWatchedDirectoryPathKey];
    self.watchedDirectoryFileDescriptor = open([watchedDirectoryPath cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);

    NSArray *paths = [NSArray arrayWithObject:watchedDirectoryPath];
    void *appController = (void *)self;
    FSEventStreamContext context = {0, appController, NULL, NULL, NULL};
    FSEventStreamRef streamRef = FSEventStreamCreate(NULL, 
                                                     &fsevents_callback, 
                                                     &context, 
                                                     (CFArrayRef) paths, 
                                                     kFSEventStreamEventIdSinceNow, 
                                                     (CFTimeInterval)2.0, 
                                                     kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagWatchRoot);

    FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    FSEventStreamStart(streamRef);

}

void fsevents_callback(ConstFSEventStreamRef streamRef, 
                       void *userData, 
                       size_t numumberOfEvents, 
                       void *eventPaths, 
                       const FSEventStreamEventFlags eventFlags[], 
                       const FSEventStreamEventId eventIds[]) {

    MyAppController *appController = (MyAppController *)userData;   
    char *newPath = calloc(4096, sizeof(char));
    int pathIntPointer = (int)newPath;

    int length = fcntl(appController.watchedDirectoryFileDescriptor, F_GETPATH, pathIntPointer);

    NSString *newPathString = [[NSString alloc] initWithBytes:newPath length:(NSUInteger)length encoding:NSUTF8StringEncoding];
    NSLog(@"newPathString: %@", newPathString); // empty
}

1 Ответ

6 голосов
/ 27 апреля 2011

Да. Передайте kFSEventStreamCreateFlagWatchRoot в качестве последнего аргумента FSEventStreamCreate, и вы получите уведомление, если каталог будет перемещен или переименован. Из документов :

Запрос уведомлений об изменениях на пути к пути, который вы смотрите. Например, с этим флагом, если вы смотрите «/ foo / bar» и он переименовывается в «/foo/bar.old», вы получите событие RootChanged. То же самое верно, если каталог "/ foo" был переименован. Событие, которое вы получаете, является специальным событием: путь для события является указанным вами исходным путем, установлен флаг kFSEventStreamEventFlagRootChanged, а идентификатор события равен нулю. События RootChanged полезны для указания того, что вам следует повторно сканировать определенную иерархию, потому что она полностью изменилась (в отличие от изменений внутри нее). Если вы хотите отслеживать текущее местоположение каталога, лучше всего открыть каталог перед созданием потока, чтобы у вас был файловый дескриптор для него, и вы можете выполнить F_GETPATH ​​fcntl (), чтобы найти текущий путь.

Редактировать: добавление примера fcntl

Этот пример cocoadev предполагает, что автор немного неопытен с указателями. PathIntPointer не только не нужен, но и является причиной вашей проблемы. Проверка ошибок кода возврата из fnctl могла бы его перехватить. Вот пересмотренная версия вашего обратного вызова:

void fsevents_callback(ConstFSEventStreamRef streamRef, 
                   void *userData, 
                   size_t numumberOfEvents, 
                   void *eventPaths, 
                   const FSEventStreamEventFlags eventFlags[], 
                   const FSEventStreamEventId eventIds[]) {

    MyAppController *appController = (MyAppController *)userData;   
    char newPath[ MAXPATHLEN ];
    int rc;

    rc = fcntl( appController.watchedDirectoryFileDescriptor, F_GETPATH, newPath );
    if ( rc == -1 ) {
        perror( "fnctl F_GETPATH" );
        return;
    }

    NSString *newPathString = [[NSString alloc] initWithUTF8String: newPath ];
    NSLog(@"newPathString: %@", newPathString);
    [ newPathString release ];
}
...