问题
I am listening for Directory and disk changes in a Cocoa project using FSEvents
. I need to get events when a root folder is renamed or deleted. So, I passed kFSEventStreamCreateFlagWatchRoot
while creating the FSEventStream
. But even if I delete or rename the root folder I am not getting corresponding FSEventStreamEventFlags
. Any idea what could possibly be the issue. I am listening for changes in a USB mounted device. I used both FSEventStreamCreate
and FSEventStreamCreateRelativeToDevice
. One thing I notices is when I try with FSEventStreamCreate
I get the following error message while creating FSEventStream
:
(
CarbonCore.framework
)FSEventStreamCreate
:watch_all_parents
:
error trying to add kqueue forfd 7
(/Volumes/NO NAME
; Operation not supported)
But with FSEventStreamCreateRelativeToDevice
there are no errors but still not getting kFSEventStreamEventFlagRootChanged
in event flags. Also, while creation using FSEventStreamCreateRelativeToDevice
apple say's if I want to listen to root path changes pass emty string ""
. But I am not able to listen to root path changes by passing empty string. But when I pass "/"
it works. But even for "/"
I do not get any proper FSEventStreamEventFlags
. I am pasting the code here:
-(void) subscribeFileSystemChanges:(NSString*) path
{
PRINT_FUNCTION_BEGIN;
// if already subscribed then unsubscribe
if (stream)
{
FSEventStreamStop(stream);
FSEventStreamInvalidate(stream); /* will remove from runloop */
FSEventStreamRelease(stream);
}
FSEventStreamContext cntxt = {0};
cntxt.info = self;
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void**)&path, 1, NULL);
stream = FSEventStreamCreate(NULL, &feCallback, &cntxt,
pathsToWatch, kFSEventStreamEventIdSinceNow, 1,
kFSEventStreamCreateFlagWatchRoot );
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
}
Call back function:
static void feCallback(ConstFSEventStreamRef streamRef, void* pClientCallBackInfo,
size_t numEvents, void* pEventPaths, const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
char** ppPaths = (char**)pEventPaths; int i;
for (i = 0; i < numEvents; i++)
{
NSLog(@"Event Flags %lu Event Id %llu", eventFlags[i], eventIds[i]);
NSLog(@"Path changed: %@",
[NSString stringWithUTF8String:ppPaths[i]]);
}
}
Thanks a lot in advance.
回答1:
I had the same problem and I think I figured it out. Apparently kFSEventStreamCreateFlagWatchRoot
is just simply busted when using FSEventStreamCreateRelativeToDevice
. You have to use FSEventStreamCreate
. Since the former form is preferable if you're relying on historical event ids, you might need to create 2 streams. Also, note that it appears that you don't get kEventFlagChangedRoot
sent to you if your app isn't running, so you'll need to stat the directory when you start up.
回答2:
I think the change of the volume name is not counted as a change in the file system reported by FSEvents. Remember, the volume name itself does not really exists as a file system entry. The ones under /Volumes
is cooked up by the OS.
It's instead covered by Disk Arbitration.
A short sample code follows. First, define the callback
#import <DiskArbitration/DiskArbitration.h>
void callBack(DADiskRef disk,CFArrayRef keys,void *context )
{
CFDictionaryRef dict=DADiskCopyDescription(disk);
NSString*mountPoint=[(NSDictionary*)dict objectForKey:(NSString*)kDADiskDescriptionVolumePathKey];
NSLog(@"disk at %@:",mountPoint);
for(NSString*key in (NSArray*)keys){
NSLog(@"key %@ changed: %@",key,[(NSDictionary*)dict objectForKey:key]);
}
CFRelease(dict);
}
and then install the handler like this
DASessionRef session=DASessionCreate(NULL);
DARegisterDiskDescriptionChangedCallback(session, NULL, NULL, callBack, NULL);
DASessionScheduleWithRunLoop(session, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes);
来源:https://stackoverflow.com/questions/2154845/how-to-listen-for-file-system-changes-mac-kfseventstreamcreateflagwatchroot