I'm trying to do file monitoring on a directory, took the code from the apple docs. Its crashing on the fseventstreamcreate function call with this error:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000008
I'm fairly new to objective C (although I know this is C code, its part of an obj-c app) and really have no idea why this is crashing, prob a noob mistake here. I appreciate any help with this.
void monitorCallback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
int i;
char **paths = (char**)eventPaths;
// printf("Callback called\n");
for (i=0; i<numEvents; i++)
{
/* flags are unsigned long, IDs are uint64_t */
printf("Change %llu in %s, flags %u\n", eventIds[i], paths[i], eventFlags[i]);
}
}
- (void) monitorFolder
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * monitorFolder = [[NSUserDefaults standardUserDefaults] objectForKey:#"monitorFolder"];
//Create stream to monitor for changes.
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&monitorFolder, 1, NULL);
FSEventStreamRef stream;
CFAbsoluteTime latency = 3.0; /* Latency in seconds */
/* Create the stream, passing in a callback */
stream = FSEventStreamCreate(
NULL,
(FSEventStreamCallback)&monitorCallback,
NULL,
pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagNone );
...
}
Related
I get strange code errors when I rename the following command line program from main.m to main.mm. Works just fine as main.m. Anyone know why?
https://stackoverflow.com/a/36469891/105539
SOURCE
#import <Foundation/Foundation.h>
void detectNewFile (
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
int i;
char **paths = eventPaths;
printf("GOT AN EVENT!!!!\n");
for (i=0; i<numEvents; i++) {
printf("Change %llu in %s, flags %u\n", eventIds[i], paths[i], (unsigned int)eventFlags[i]);
}
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
short nPathCount = 2;
CFStringRef mypath[nPathCount];
mypath[0] = CFSTR("/Users/mike/Documents");
mypath[1] = CFSTR("/Users/mike/Downloads");
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, nPathCount, NULL);
void *callbackInfo = NULL;
CFAbsoluteTime latency = 1.0; // seconds
FSEventStreamRef hStream = FSEventStreamCreate(NULL,
&detectNewFile,
callbackInfo,
pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagFileEvents
);
FSEventStreamScheduleWithRunLoop(hStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(hStream);
printf("Waiting on new file creations...\n");
CFRunLoopRun(); // runs in an endless loop, only letting the callback function run
} // end autorelease pool
return 0;
}
ERRORS
FOR:
char **paths = eventPaths;
Cannot initialize a variable of type 'char **' with an lvalue of type 'void *'
FOR:
FSEventStreamRef hStream = FSEventStreamCreate(NULL,
&detectNewFile,
callbackInfo,
pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagFileEvents
);
No matching function for call to 'FSEventStreamCreate'
Thanks to #johnelemans, I found the problems. In C, it's legal to have automatic casting from void * to char **, but not in C++, which is what the .mm file would switch this into. The fix is to use casting:
char **paths = (char **)eventPaths;
Then, on the FSEventStreamCreate, it didn't like the void * instead of this:
FSEventStreamContext *callbackInfo = NULL;
...and didn't like the CFAbsoluteTime instead of:
CFTimeInterval latency = 1.0; // seconds
Then, you need to add CoreServices.framework library to the build steps.
I made those changes and it compiles now.
I'm trying to code a little command line tool using Xcode (under MacOS 10.10) that watches a specific folder and informs me about changes to files in that folder.
I'm following the guide given in https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html
Here is my current code:
#import <Foundation/Foundation.h>
#include <CoreServices/CoreServices.h>
void mycallback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
int i;
char **paths = eventPaths;
printf("Callback called\n");
for (i=0; i<numEvents; i++) {
int count;
/* flags are unsigned long, IDs are uint64_t */
printf("Change %llu in %s, flags %lu\n", eventIds[i], paths[i], eventFlags[i]);
}
}
int main(int argc, const char * argv[]) {
// #autoreleasepool {
// insert code here...
NSLog(#"Starting to watch ");
/* Define variables and create a CFArray object containing
CFString objects containing paths to watch.
*/
CFStringRef mypath = CFSTR("/Users/testuser/");
CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)&mypath, 1, NULL);
void *callbackInfo = NULL; // could put stream-specific data here.
FSEventStreamRef stream;
CFAbsoluteTime latency = 3.0; /* Latency in seconds */
/* Create the stream, passing in a callback */
stream = FSEventStreamCreate(NULL,
&mycallback,
callbackInfo,
pathsToWatch,
kFSEventStreamEventIdSinceNow, /* Or a previous event ID */
latency,
kFSEventStreamCreateFlagNone /* Flags explained in reference */
);
/* Create the stream before calling this. */
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
CFRunLoopRun();
return 0;
}
The code compiles and can be started. However, no events are being fired. I'm pretty new to Xcode and also never ever used a callback function before. So I guess that this is a pretty dumb mistake that I made.
I'd appreciate any hints that may help.
Thanks in advance
Norbert
Update: The code was updated with a working solution from the answer.
According to the documentation, after starting event stream to send events, you should call CFRunLoopRun.
Try to change your while() loop to:
CFRunLoopRun();
Update. My output:
$ ./fsevent
2015-05-17 13:51:29.718 fsevent[898:23601] Starting to watch
Callback called
Change 1165579 in /Users/baf/src/tests/, flags 66560
Callback called
Change 1165594 in /Users/baf/src/tests/, flags 66048
It appears you have FSEventStreamRef stream; commented out. In order to start watching events you'll want to uncomment that line. It also looks like your callback print statement is also commented out, although that appears to be just for debugging perhaps.
So im using the EventStream to watch a folder for change. Now it all works fine and I can see a log call back when I alter files in the folder, but I cant seem to call my folderWatch, it gives the error "use of undeclared identifier 'self'". I can use this function everywhere else, just not in the fsEventsCallback. Any help would be appreciated!
void fsEventsCallback(ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]){
[self folderWatch];
NSLog(#"2");
}
The reason is that fsEventsCallback is a C function and not an Objective-C instance method, so fsEventsCallback does not know anything about self.
You can use the info field in the FSEventStreamContext to pass self to the callback function. The following example assumes that your class is called Watcher.
(If you don't use ARC, you can omit all the __bridge casts.)
- (void)folderWatch
{
}
void fsEventsCallback(ConstFSEventStreamRef streamRef,
void *info,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
Watcher *watcher = (__bridge Watcher *)info;
[watcher folderWatch];
}
- (void)startWatching
{
FSEventStreamContext context;
context.info = (__bridge void *)(self);
context.version = 0;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
NSArray *pathsToWatch = #[#"/path/to/watch"];
self.eventStream = FSEventStreamCreate(NULL,
&fsEventsCallback,
&context,
(__bridge CFArrayRef)(pathsToWatch),
kFSEventStreamEventIdSinceNow,
1.0,
kFSEventStreamCreateFlagFileEvents
);
}
I'm working on a desktop application that watch folders using the fileevent api, so basically this is my code :
#import "PNAppDelegate.h"
void callback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
[(__bridge PNAppDelegate *)clientCallBackInfo reloadStatus];
};
#implementation PNAppDelegate
#synthesize window = _window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSArray *pathsToWatch = [NSArray arrayWithObject: #"/Users/romainpouclet/Projects/foo"];
void *appPointer = (__bridge void *)self;
FSEventStreamContext context = {0, appPointer, NULL, NULL, NULL};
FSEventStreamRef stream;
CFAbsoluteTime latency = 3.0;
stream = FSEventStreamCreate(NULL,
&callback,
&context,
(__bridge CFArrayRef) pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagNone);
NSLog(#"Schedule with run loop");
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
[self reloadStatus];
}
-(void)reloadStatus
{
}
#end
No problem, it works pretty well for a POC as simple as this one, BUT it feels kinda ugly (and it probably is, I'm not really used to mix Objective-C and C). So here are my questions :
where should I declare my callback? It feels weird having it at the top of my file, just because it worked there.
is it possible to have some kind of #selector-based approach instead of callbacks? (I find them reassuring :D)
Thanks for your time !
Why not put the callback declaration in either PNAppDelegate.h, or its own header file (if you don't want to spread it around your app). That way you can just include the header file and put the function definition anywhere you want. Doing so is standard C functionality.
// Header file callback.h
void callback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]);
// PNAppDelegate.m
#import "PNAppDelegate.h"
#import "callback.h"
#implementation PNAppDelegate
...
#end
void callback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
[(__bridge PNAppDelegate *)clientCallBackInfo reloadStatus];
};
You are correct, that code IS ugly. However, bridging C and Obj-C is no small task, so you really only have a few options:
Create an Objective-C wrapper around the C-based API. This would be my recommended approach, especially if the API is not too complex. It gives you the advantage of using either delegates or blocks, instead of functions.
Use blocks for callbacks, by getting their internal function pointer:
// internal structure of a block
struct blockPtr {
void *__isa;
int __flags;
int __reserved;
void *__FuncPtr;
void *__descriptor;
};
int main()
{
#autoreleasepool {
__block int b = 0;
void (^blockReference)(void *) = ^(void *arg) {
NSLog(#"<%s>: %i", arg, b++);
};
void *blockFunc = ((__bridge struct blockPtr *) blockReference)->__FuncPtr;
void (*castedFunction)(void *, void *) = blockFunc;
// the first argument to any block funciton is the block
// reference itself, similar to how the first argument to
// any objc function is 'self', however, in most cases you
// don't need the block reference (unless reading __block variables), it's just difficult to
// get that first argument from inside the block
castedFunction((__bridge void *) blockReference, "one");
castedFunction((__bridge void *) blockReference, "two");
}
}
I really don't think this is practical in most situations, but if you can find a way to make it work, more power to you.
Stick with how you are currently doing it. It sucks, but that is how C works.
Block variable in objective-c is a reference and I learned from somewhere infer that following code may be transformed by compiler to another form.
Original code:
typedef int (^block_type)();
block_type create_k(int i)
{
block_type block = ^(){
return i;
};
//[block copy];
return block;
}
Generated Code:
typedef void (*generic_invoke_funcptr)(void *, ...);
struct __block_literal {
void *isa;
int flags;
int reserved;
generic_invoke_funcptr invoke;
struct __block_descriptor_tmp *descriptor;
const int captured_i;
};
static const struct __block_descriptor_tmp {
unsigned long reserved;
unsigned long literal_size;
/* no copy/dispose helpers needed */
} __block_descriptor_tmp = {
0UL, sizeof(struct __block_literal)
};
// ^int (void) { return i; }
int __create_k_block_invoke_(struct __block_literal *bp) {
return bp->captured_i;
}
typedef int (*iv_funcptr)(struct __block_literal *);
typedef int (^block_type)();
block_type create_k(int i)
{
//block_type block = ^(){
// return i;
//};
struct __block_literal __b = {
.isa = &_NSConcreteStackBlock,
.flags = BLOCK_HAS_DESCRIPTOR,
.reserved = 0,
.invoke = (generic_invoke_funcptr)__f_block_invoke_,
.descriptor = &__block_descriptor_tmp,
.captured_i = i
};
struct __block_literal *block = &__b;
return block;
}
So |_b| in stack and block is only a reference to |_b|. If |create_k| return |block|, the receiver only get a invalid address.
But
int main(int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
printf("%d\n", create_k(40)());
[pool drain];
return 0;
}
By exec it, print |40| and |block| is a valid block. What's the matter?
My guess would be that the memory for that stack frame hasn't been zeroed yet. Try calling another function between create_k() and printf() to get some other random data in there.