App Changed Notifications in CFRunLoop Daemon - nsnotificationcenter

static void registerForDriverLoadedNotification()
{
// Snipped code that works and is not related to issue
}
static void registerForApplicationChangedNotification()
{
NSLog(#"registerForApplicationChangedNotification!");
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserverForName: nil object: nil queue: nil
usingBlock: ^(NSNotification* notification) {
NSLog(#"NOTIFICATION %# -> %#", notification.name,
notification.userInfo);
}];
}
int main (int argc, const char* argv[]) {
registerForDriverLoadedNotification();
registerForApplicationChangedNotification();
CFRunLoopRun();
return 0;
}
The above code is for a daemon process, it waits for USB devices to be plugged in, then loads a configuration. I would like to extend this functionality to also detect when applications are launched, and if an app-specific config is present load it.
However, I do not seem to receive any notifications other than NSUserDefaultsDidChangeNotification.
The code above in registerForApplicationChangedNotification originally was monitoring both NSWorkspaceDidActivateApplicationNotification and NSWorkspaceDidDeactivateApplicationNotification, but I changed it to the catch-all so I could see what other notifications were being posted.
No matter what happens only the NSUserDefaultsDidChangeNotification notification seems to be received... what is wrong with this rather simple code?

Silly mistake!
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
Should be:
NSNotificationCenter* center = [[NSWorkspace sharedWorkspace] notificationCenter]

Related

After IORegisterForSystemPower failing to call IODeregisterForSystemPower

I have an application, written in Objective-C for MacOS 10.10+ which registers for sleep/wake notifications (code sample below, but the code isn't the question). What I am wondering is, if I call IORegisterForSystemPower at App initialisation, but during debugging I kill the app before it has a chance to call IODeregisterForSystemPower, what are the implications? Does the app get de-registered automatically when it dies in any case? Is there a system dictionary I need to clear out (a plist somewhere, etc.)? Thanks in advance for any help.
io_object_t root_notifier = MACH_PORT_NULL;
IONotificationPortRef notify = NULL;
DebugLog(#"App: Logging IORegisterForSystemPower sleep/wake notifications %#", [NSDate date]);
/* Log sleep/wake messages */
powerCallbackPort = IORegisterForSystemPower ((__bridge void *)self, &notify, sleepWakeCallback, &root_notifier);
if ( powerCallbackPort == IO_OBJECT_NULL ) {
DebugLog(#"IORegisterForSystemPower failed");
return;
}
self.rootNotifierPtr = &(root_notifier); // MARK: deregister with this pointer
if ( notify && powerCallbackPort )
{
CFRunLoopAddSource(CFRunLoopGetCurrent(),IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode);
}
To be honest, I don't know the exact answer. But it may help you.
First of, if you call IORegisterForSystemPower, you need to make two calls in this order: - Call IODeregisterForSystemPower with the 'notifier' argument returned here. - Then call IONotificationPortDestroy passing the 'thePortRef' argument returned here (Please visit apple's document for more detail).
In case of port binding, if I use CFSocketSetAddress, before releasing this socket no other can use this port for binding. But in case of app terminate/closed without releasing this socket this port is available. That means after terminated the app system automatically releasing this.
Does the app get de-registered automatically when it dies in any case?
I think it will automatically de-registered by system.
I also used similar code as you in one of my project. But recently replaced with below codes:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: #selector(receiveWakeNotification:) name: NSWorkspaceDidWakeNotification object: nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: #selector(receiveSleepNotification:) name: NSWorkspaceWillSleepNotification object: nil];

macOS: Detect all application launches including background apps?

Newbie here. I'm trying to create a small listener for application launches, and I already have this:
// almon.m
#import <Cocoa/Cocoa.h>
#import <stdio.h>
#include <signal.h>
#interface almon: NSObject {}
-(id) init;
-(void) launchedApp: (NSNotification*) notification;
#end
#implementation almon
-(id) init {
NSNotificationCenter * notify
= [[NSWorkspace sharedWorkspace] notificationCenter];
[notify addObserver: self
selector: #selector(launchedApp:)
name: #"NSWorkspaceWillLaunchApplicationNotification"
object: nil
];
fprintf(stderr,"Listening...\n");
[[NSRunLoop currentRunLoop] run];
fprintf(stderr,"Stopping...\n");
return self;
}
-(void) launchedApp: (NSNotification*) notification {
NSDictionary *userInfo = [notification userInfo]; // read full application launch info
NSString* AppPID = [userInfo objectForKey:#"NSApplicationProcessIdentifier"]; // parse for AppPID
int killPID = [AppPID intValue]; // define integer from NSString
kill((killPID), SIGSTOP); // interrupt app launch
NSString* AppPath = [userInfo objectForKey:#"NSApplicationPath"]; // read application path
NSString* AppBundleID = [userInfo objectForKey:#"NSApplicationBundleIdentifier"]; // read BundleID
NSString* AppName = [userInfo objectForKey:#"NSApplicationName"]; // read AppName
NSLog(#":::%#:::%#:::%#:::%#", AppPID, AppPath, AppBundleID, AppName);
}
#end
int main( int argc, char ** argv) {
[[almon alloc] init];
return 0;
}
// build: gcc -Wall almon.m -o almon -lobjc -framework Cocoa
// run: ./almon
Note: when I build it, it will run fine, but if you do it with Xcode 10 on High Sierra, you will get ld warnings, which you can ignore, however.
My question: Is there a way to also detect a launch of a background application, e.g. a menu bar application like Viscosity etc.? Apple says that
the system does not post
[NSWorkspaceWillLaunchApplicationNotification] for background apps or
for apps that have the LSUIElement key in their Info.plist file.
If you want to know when all apps (including background apps) are
launched or terminated, use key-value observing to monitor the value
returned by the runningApplications method.
Here: https://developer.apple.com/documentation/appkit/nsworkspacewilllaunchapplicationnotification?language=objc
I would at least try to add support for background apps etc. to the listener, but I don't know how to go about it. Any ideas?
As the document suggests, you use Key-Value Observing to observe the runningApplications property of the shared workspace object:
static const void *kMyKVOContext = (void*)&kMyKVOContext;
[[NSWorkspace sharedWorkspace] addObserver:self
forKeyPath:#"runningApplications"
options:NSKeyValueObservingOptionNew // maybe | NSKeyValueObservingOptionInitial
context:kMyKVOContext];
Then, you would implement the observation method (using Xcode's ready-made snippet):
- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
if (context != kMyKVOContext)
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
if ([keyPath isEqualToString:#"runningApplications"])
{
<#code to be executed when runningApplications has changed#>
}
}

Handle any application closing in objective c

I want to execute my method when any application is closing. My code is:
#interface FO: NSObject
- (void)applicationKilled:(NSNotification*)notification;
- (void)appDidLaunch:(NSNotification*)notification;
#end
#implementation FO
- (void)applicationKilled:(NSNotification*)notification {
NSLog(#"success");
}
- (void)appDidLaunch:(NSNotification*)notification {
NSLog(#"app info: %#", [notification userInfo]);
}
#end
#implementation Main:NSObject
FO fo;
NSString * filePath = "...MyPath";
NSString * application = "..MyApplication";
int main(int argc, const char * argv[]) {
fo = [[FO alloc]init];
[Main MyMethod];
while(1==1) {
...some code;
}
return 0;
}
+(void) MyMethod {
center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserver:fo selector:#selector(appDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
[center addObserver:fo selector:#selector(applicationKilled:) name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
[[NSWorkspace sharedWorkspace] openFile:filePath withApplication:application]; }
#end
However, appDidLaunch method is not firing, even if i'll open another application in finder. Also applicationKilled method is never firing.
When i'm executing following code
[center postNotificationName:NSWorkspaceDidLaunchApplicationNotification
object:self];
appDidLaunch method is firing OK. Where can be a problem? Should this methods be fired every time when some application is opened or closed?
CRD is on the right track. You absolutely must have a runloop to receive this notification. For example:
#implementation Main : NSObject
- (void)applicationDidFinishLaunching:(NSApplication *)app {
[Main MyMethod];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ... The rest of your program ...
});
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyDelegate *delegate = [Main new];
[[NSApplication sharedApplication] setDelegate:delegate];
[NSApp run];
}
return 0;
}
I've put "the rest of your program" into a dispatch_async because you must not block the main thread. The usual way that Mac apps work is not with a big while (YES) loop. The usual way is by registering for various events and then waiting from them to happen. That's what the run loop is for. But if you have to manage your own loop (you generally shouldn't, but if you must), then you need to move it off of the main queue.
Assuming you are using ARC and also guessing as the information you give seems to be incomplete:
In your updated question you show fo declared as a local variable of MyMethod. The method addObserver:selector:name:object: does not keep a strong reference to the observer. After MyMethod returns the local fo object will be reclaimed, you now have no observer to call methods on.
However, while the above would explain why your code doesn't work it wouldn't explain why your app does not crash - and you don't report that it crashes. Running the code you give above causes the app to crash. So it appears that you've missed some information out or at least not reported the crash.
Guess Two
You have no run loop.
Many parts of the framework rely on there being a run loop which dispatches incoming events to appropriate handlers - just type "run loop" into Xcode's help. If you create a standard application using Xcode's "Cocoa Application" template the run loop is created for you by the code in main.m.
Events produced by OS X when applications start and stop are dispatched by the run loop to framework handlers which produce the corresponding notifications. Without a run loop these system events will not be handled, so no notifications.
You have:
int main(int argc, const char * argv[])
{
fo = [[FO alloc]init];
[Main MyMethod];
while(1==1)
{
...some code;
}
return 0;
}
so unless "...some code" creates a run loop the system events will not be handled.
Write your project using the standard "Cocoa Application" template and, for example, put your call to setup the notification handlers in applicationDidFinishLaunching:.
HTH

NSNotifier on Mac OSX

I'm building a mac daemon, from scratch.
Here's a simplified version of the code.
#import <stdio.h>
#import <stdlib.h>
#import <Foundation/Foundation.h>
#include "notifier.h"
int new_notification();
notifier *not;
int main () {
#autoreleasepool {
not = [[[notifier alloc] init] autorelease];
pid_t pid;
pid = fork();
if(pid > 0) {
printf("my child id is %d\n", pid);
exit(0);
}
while(1) {
int n = new_notification();
if(n > 0) {
NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = #"Hello, World!";
notification.informativeText = #"A notification";
notification.soundName = NSUserNotificationDefaultSoundName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
printf("new notification : count = %d !!\n", n);
}
sleep(1);
}
}
return 0;
}
int new_notification() {
return [not get_notifications];
}
I don't see the notification on my window though, I think I have to make my application a "key" application, if so, how do it do that? I can see the output on my terminal though, and on checking if
(NSClassFromString(#"NSUserNotificationCenter")==nil)
I get FALSE
Extended comments that I hope answer your issue...
If your app is active, the notification is unlikely to be displayed. But you'd be able to find it in the notification center. (Notifications draw a user's attention to an app that they're not already looking at.)
I'd suggest trying [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory] to make it clear your app isn't in the foreground but I'm not totally certain whether [NSApplication sharedApplication] aka NSApp is something you'd use with a daemon.
Part of your issue may actually be that without invoking [NSApplication sharedApplication] and telling it to run you never become an app, so you can't use the notification center... or become an accessory. But I'm not certain of that either.

asiHttpRequest under iOS5

I am succesfuly using AsiHttpRequest library to make url connections in my apps. However, I upgrade to iOS5 and Reachability.m file is reporting some errors (4) on following functions:
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
#pragma unused (target, flags)
NSCAssert(info, #"info was NULL in ReachabilityCallback");
NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], #"info was the wrong class in ReachabilityCallback");
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: (Reachability *) info];
} // ReachabilityCallback()
- (BOOL) startNotifier {
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) {
if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
return YES;
}
}
return NO;
} // startNotifier
1sr error: in NSCAssert line, "Cast of C pointer type 'void' to Objective-C pointer type NSObject requires a bridged cast". Why happens and how to solve it?
ANSWER: you can disable ARC for each file. Go to build settings of your project and set the -fno-objc-arc flag on all the ASIHTTPRequest files (double click to edit text). Then you must remove ASIAuthenticationDialog and any references to it that are still generating errors. It works for me.
EDIT: I remember now and yes, the problem is ARC. But you can exclude files from being complied under ARC by setting the following compiler flag in Build Phases >> Compile Sources: -fno-objc-arc. If you select all the ASIHTTPRequest files and double-click, you can set the flag for all of them in one fell swoop.
ORIGINAL POST:
I've been using ASIHTTPRequest for a couple of weeks now and I remember reading a post somewhere about problems with Reachability, I just can't remember what it was exactly.
Anyway, this is what those lines in my Reachability.m look like:
//Start listening for reachability notifications on the current run loop
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
#pragma unused (target, flags)
NSCAssert(info, #"info was NULL in ReachabilityCallback");
NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], #"info was the wrong class in ReachabilityCallback");
//We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
// in case someone uses the Reachablity object in a different thread.
NSAutoreleasePool* pool = [NSAutoreleasePool new];
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification
object: (Reachability *) info];
[pool release];
} // ReachabilityCallback()
- (BOOL) startNotifier {
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) {
if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
return YES;
}
}
return NO;
} // startNotifier