Why would method swizzling be bypassed by the screen saver? - macos

I'm developing a screensaver for Mac OS X and I need to do some method swizzling so I made a small experiment:
#implementation CAHTTPCookieStorage
+ (void) highjack {
NSLog(#"Attempting to highjack cookies.");
Class originalClass = [NSHTTPCookieStorage class];
Method originalMeth = class_getClassMethod(originalClass, #selector(sharedHTTPCookieStorage));
Method replacementMeth = class_getClassMethod([self class], #selector(patchedSharedHTTPCookieStorage));
method_exchangeImplementations(originalMeth, replacementMeth);
}
+ (NSHTTPCookieStorage*) patchedSharedHTTPCookieStorage {
NSLog(#"Cookies have been highjacked!!!!");
return [CAHTTPCookieStorage patchedSharedHTTPCookieStorage];
}
#end
I'm calling CAHTTPCookieStorage.highjack() from my app from AppDelegate.init() and from my screensaver from the ScreenSaverViewSubclass.init(...). While running my app or the screensaver in preview mode (inside the system preferences), it works fine, but when I run it as a proper screensaver I can see the message "Attempting to highjack cookies." but never "Cookies have been highjacked!!!!".
Any ideas what might be going wrong? Maybe an issue with threads? is method swizzling per thread?

Further investigation on this proved that this was true only in 10.10 in dual monitor mode but not in 10.9 with two monitors or 10.10 with a single monitor. I'm not sure what caused it, but using the +load method for doing the swizzling solved it:
+ (void) load {
NSLog(#"Attempting to highjack cookies.");
Class originalClass = [NSHTTPCookieStorage class];
Method originalMeth = class_getClassMethod(originalClass, #selector(sharedHTTPCookieStorage));
Method replacementMeth = class_getClassMethod([self class], #selector(patchedSharedHTTPCookieStorage));
method_exchangeImplementations(originalMeth, replacementMeth);
}

Related

Use FSEvents in sandboxed app

I'm trying to use FSEvents in my sandboxed app to monitor some directories. I implemented the following class:
#implementation SNTracker
- (id)initWithPaths:(NSArray *)paths {
self=[super init];
if (self) {
trackedPaths=paths;
CFTimeInterval latency=1.0;
FSEventStreamContext context={0,(__bridge void *)self,NULL,NULL,NULL};
FSEventStreamRef eeventStream=FSEventStreamCreate(kCFAllocatorDefault,&callback,&context,(__bridge CFArrayRef)trackedPaths,kFSEventStreamEventIdSinceNow,latency,kFSEventStreamCreateFlagUseCFTypes|kFSEventStreamCreateFlagWatchRoot|kFSEventStreamCreateFlagFileEvents);
FSEventStreamScheduleWithRunLoop(eeventStream,[[NSRunLoop mainRunLoop] getCFRunLoop],kCFRunLoopDefaultMode);
FSEventStreamStart(eeventStream);
}
return self;
}
static void callback(ConstFSEventStreamRef streamRef,void *clientCallBackInfo,size_t numEvents,void *eventPaths,const FSEventStreamEventFlags eventFlags[],const FSEventStreamEventId eventIds[]) {
NSLog(#"asd");
}
The problem is that "asd" never gets printed (i.e. the callback function is never called). When I disable "Enable App Sandboxing" in the Summary of my main target in Xcode, the callback gets called. Am I doing something wrong? The only entitlement I've given to the sandboxed app is read-write access to user selected files.
And the usere has selected the path you are trying to monitor via FSEvent? Since if he hasn't, you won't be allow to access it and thus also not monitor it. A path can only be monitored as long as you are allowed to access it.

How to use IKScannerDeviceView in Cocoa

How can I use IKScannerDeviceView to scan a document inside of my app?
I tried adding an IKScannerDeviceView into my view through IB and setting its delegate to my app delegate (which implements the IKScannerDeviceViewDelegate), but when I run the app I get a view with the buttons Show Details and Scan, and only Show Details is enabled and when I click it nothing happens.
I have a scanner plugged in and I can scan through Image Capture, but not through my app.
Does anybody have a good tutorial on how to use it?
I was finally able to figure out how to use IKScannerDeviceView.
Your class must implement the following delegates:
IKScannerDeviceViewDelegate, ICScannerDeviceDelegate, ICDeviceBrowserDelegate
and you need to have an IKScannerDeviceView in your window, with its delegate set to the class implementing IKScannerDeviceViewDelegate
To start using it, you must create an ICDeviceBrowser like so:
ICDeviceBrowser *mDeviceBrowser = [[ICDeviceBrowser alloc] init];
mDeviceBrowser.delegate = self;
mDeviceBrowser.browsedDeviceTypeMask = ICDeviceLocationTypeMaskLocal|ICDeviceLocationTypeMaskRemote|ICDeviceTypeMaskScanner;
[mDeviceBrowser start];
Then implement the delegate methods in a manner similar to this:
- (void)scannerDeviceDidBecomeAvailable:(ICScannerDevice*)scanner;
{
[scanner requestOpenSession];
}
- (void)deviceBrowser:(ICDeviceBrowser*)browser didAddDevice:(ICDevice*)addedDevice moreComing:(BOOL)moreComing
{
if ( (addedDevice.type & ICDeviceTypeMaskScanner) == ICDeviceTypeScanner )
{
[scannerView setScannerDevice:(ICScannerDevice*)addedDevice];
}
}
-(void)didRemoveDevice:(ICDevice*)removedDevice
{
[removedDevice requestCloseSession];
}
Then if all goes right, your IKScannerDeviceView will be able to interact with your scanner!

URL scheme - Qt and mac

I'm trying to implement a custom URL scheme for my application. I've added the necessary lines for my Info.plist. After calling the specified url (eg.: myapp://) the application launches.
If I want to handle the URL, I've found these steps:
#interface EventHandler : NSObject {
}
#end
#implementation EventHandler
- (id)init {
self = [super init];
if (self) {
NSLog(#"eventHandler::init");
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self
selector:#selector(applicationDidFinishLaunching:)
// name:NSApplicationWillFinishLaunchingNotification
name:NSApplicationDidFinishLaunchingNotification
object:nil];
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:self andSelector:#selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
NSLog(#"%#", url);
}
#end
The above code is working if the application is running, but if the URL gets called and the application was terminated, the event is not caught. I think this is because this: NSApplicationDidFinishLaunchingNotification.
Changing it to NSApplicationWillFinishLaunchingNotification causes that non events caught. Maybe Qt handles it before me, but I can't find a workaround for the problem.
I was also trying to get my Qt-based application handle a custom URL scheme on the Mac and went down the same path as the original poster. It turns out that Qt4 already supports URL events on the Mac, and there's no need to write Objective-C code to receive them. This is in fact the reason that you didn't receive any URL events when you set the event handler in response to NSApplicationWillFinishLaunchingNotification: Qt registers its own handler afterward.
When a URL with your custom scheme is triggered, your Qt application will receive a FileOpenEvent. Note that it is the QApplication instance which receives the event. You can catch it by making your application subclass QApplication or by installing an event filter on the standard QApplication. I opted for this second approach.
Here's the eventFilter method of my custom event filter class, FileOpenEventFilter. It just emits the signal urlOpened when the event contains a non-empty URL. It also saves the last opened URL in case my main window isn't completely initialized when the event arrives (which happens in my app when it's not already running when the custom URL is clicked.)
bool FileOpenEventFilter::eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::FileOpen)
{
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
if (!fileEvent->url().isEmpty())
{
m_lastUrl = fileEvent->url().toString();
emit urlOpened(m_lastUrl);
}
else if (!fileEvent->file().isEmpty())
{
emit fileOpened(fileEvent->file());
}
return false;
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
I register my handler in my application delegate's applicationWillFinishLaunching: method, and I don't miss any events. You're probably initializing your EventHandler object too late to get that notification. If you want to keep it as a separate class, that's ok, but you should create your object and register it with NSAppleEventManager within the applicationWillFinishLaunching: method of your application delegate.

Run method on main thread from another thread

My model class has to get some data from the internet. So I decided to run it on another thread so the ui doesn't freeze.
So when an object wants some data it first asks the model using a method of this type:
- (void)giveMeSomeData:(id)object withLabel:(id)label {
objectAsking= object;
theLabel= label;
NSThread* thread= [[NSThread alloc] initWithTarget:self selector:#selector(getTheDataFromInternet) object:nil];
[thread start];
}
- (void)getTheDataFromInternet {
//getting data...
theData= [dataFromInternet retain]; //this is the data the object asked for
[self returnObjectToAsker];
}
- (void)returnObjectToAsker {
[objectAsking receiveData:theData withLabel:theLabel];
}
As I'm still a newbie, can you tell me if it's a good pattern?
Thanks!
Your setup is pretty much correct. You never want to initiate any sort of network connection on the main thread.
As it currently stands, -returnObjectToAsker will be executed on the background thread.
You'd probably be interested in -[NSObject performSelectorOnMainThread:withObject:waitUntilDone:].
Or if you wanted to something with Grand Central Dispatch (iOS 4+, Mac OS X 10.6+), you could do:
#import <dispatch/dispatch.h>
- (void)giveMeSomeData:(id)object withLabel:(id)label {
dispatch_async(dispatch_get_global_queue(0,0), ^{
//this runs on a background thread
//get data from the internet
dataFromTheInternet = ...;
dispatch_async(dispatch_get_main_queue(), ^{
[object receiveData:dataFromTheInternet withLabel:label];
//this runs on the main thread. Use theData
});
});
}
Since the blocks capture their environment, you wouldn't even have to save off object and label into ivars. :)

Simple NSSpeechRecognizer code, not working!

I noticed NSSpeechRecognizer in ADC library and I found it to be very interesting, so to play with it I prepared a simple application which will just listen the command and if recognized it displays it in log.
The code used is:
- (id)init {
if (self = [super init]) {
// Insert code here to initialize your application
NSArray *cmds = [NSArray arrayWithObjects:#"A",#"B", #"C",#"alpha",#"beta",#"vodka",#"wine",nil];
recog = [[NSSpeechRecognizer alloc] init]; // recog is an ivar
[recog setCommands:cmds];
[recog setDelegate:self];
}
return self;
}
- (IBAction)listen:(id)sender
{ NSLog(#"listen:");
if ([sender state] == NSOnState) { // listen
[recog startListening];
} else {
[recog stopListening];
}
}
- (void)speechRecognizer:(NSSpeechRecognizer *)sender didRecognizeCommand:(id)aCmd {
NSLog(#"speechRecognizer: %#",(NSString *)aCmd);
}
I tried it many times for the commands registered but I was unable to get none of the messages in log, in delegate :(
There was always some noise in the background.. could this be the reason for it or I have done something wrong in the code??
Can anyone suggest me some solution for it??
Thanks,
Miraaj
Code looks fine so far.
The NSSpeechRecognizer is a bit tricky sometimes and refuses to listen to the right words. Did you try different words?
Did you try setting startListening as default?
I wrote a little tutorial some time ago. Its in german language but maybe it will help you anyway or you use some translation tool.
http://cocoa-coding.de/spracherkennung/nsspeechrecognizer1.html

Resources