objective c - ver - ¿Cuál es la forma ''correcta'' de identificar la aplicación actualmente activa en OSX 10.6+?
sistemas operativos mac (4)
Estoy tratando de identificar qué aplicación OSX está activa actualmente. Entiendo que en OSX 10.5, esto podría hacerse con:
[[NSWorkspace sharedWorkspace] activeApplication]
sin embargo, esto ha sido desaprobado en 10.6+.
La documentación de los desarrolladores de Apple establece que esto debe hacerse a través de la propiedad ''activa'' del objeto NSRunningApplication. Pensé que una forma de abordar esto podría ser obtener la lista de todas las aplicaciones en ejecución a través de
[[NSWorkspace sharedWorkspace] runningApplications]
y luego en bucle, verificando la propiedad ''activa'' de cada aplicación. Sin embargo, el siguiente código de prueba no se comporta como esperaba: cuando se compila y ejecuta desde Terminal.app, solo la aplicación "terminal" se marca como activa, independientemente de si selecciono una aplicación diferente.
#import <Foundation/Foundation.h>
#import <AppKit/NSRunningApplication.h>
#import <AppKit/NSWorkspace.h>
int main(int argc, char *argv[]) {
while(1){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *currApp;
NSArray *runningApps;
runningApps = [[NSWorkspace sharedWorkspace] runningApplications];
for (id currApp in runningApps) {
if ([currApp isActive])
NSLog(@"* %@", [currApp localizedName]);
else
NSLog(@" %@", [currApp localizedName]);
}
sleep(1);
[pool release];
}
return 0;
}
¿Qué estoy haciendo mal? ¿He entendido mal cómo funciona la propiedad "activa"?
(Además, siéntase libre de criticar mi código del Objetivo C: este es mi primer intento del objetivo C, así que sé que probablemente sea horriblemente feo para el ojo entrenado. ¡Por favor, perdóneme! :) Cualquier sugerencia es bienvenida.)
A partir de OS X 10.7, NSWorkspace
también tiene el método conveniente:
- (NSRunningApplication *)frontmostApplication;
También puede usar llamadas de despacho de Grand Central para hacer llamadas repetitivas con tiempo de espera.
Algo como esto:
- (void) checkFrontmostApp {
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSRunningApplication* runningApp = [[NSWorkspace sharedWorkspace] frontmostApplication];
//do something
NSLog(@"frontmost app: %@", runningApp.bundleIdentifier);
[self checkFrontmostApp]; //''recursive'' call
});
}
Encuestar cada segundo para descubrir que la aplicación actual es ineficiente, y es la manera incorrecta de hacerlo. Un enfoque mucho mejor es simplemente configurar su proceso para recibir la notificación NSWorkspaceDidActivateApplicationNotification
.
@interface MDAppController : NSObject <NSApplicationDelegate> {
NSRunningApplication *currentApp;
}
@property (retain) NSRunningApplication *currentApp;
@end
@implementation MDAppController
@synthesize currentApp;
- (id)init {
if ((self = [super init])) {
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(activeAppDidChange:)
name:NSWorkspaceDidActivateApplicationNotification object:nil];
}
return self;
}
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)activeAppDidChange:(NSNotification *)notification {
self.currentApp = [[notification userInfo] objectForKey:NSWorkspaceApplicationKey];
NSLog(@"currentApp == %@", currentApp);
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
MDAppController *appController = [[MDAppController alloc] init];
[NSApp setDelegate:appController];
[NSApp run];
[pool release];
return 0;
}
Las notas para activeApplication de activeApplication
dicen:
Consideraciones Especiales
Se recomienda encarecidamente que utilice la aplicación o los métodos de activación de las clases de NSRunningApplication para recuperar esta información en aplicaciones dirigidas a Mac OS X v10.6 y posteriores.
Probablemente debería hacer un 10.6 y un nuevo conjunto de códigos y un 10.5.X y un conjunto de códigos más antiguo allí.
Por cierto, el método NSWorkspace solo se marcó como obsoleto a partir de 10.7, pero NSRunningApplication entró a partir de 10.6.
Oh, aquí hay una alternativa que es compatible con 64 bits si incluye el marco de Servicios de Aplicación:
int main (int argc, const char * argv[])
{
// insert code here...
CFShow(CFSTR("Hello, World!/n"));
ProcessSerialNumber psn;
OSErr err = GetFrontProcess(&psn);
if(err == noErr)
{
ProcessInfoRec info;
StringPtr processName = malloc(64);
if(processName)
{
bzero(processName, 64);
info.processInfoLength = sizeof(ProcessInfoRec);
info.processName = processName;
err = GetProcessInformation( &psn, &info);
if(err == noErr)
{
fprintf(stdout, "front most process name is %s", processName+1 );
}
free(processName);
}
}
return 0;
}
Su problema es que su aplicación no puede recibir ningún evento del sistema que le informe que la aplicación actual ha cambiado y, por lo tanto, nunca actualiza la propiedad activa en las instancias de NSRunningApplication
. Si utilizo el mismo código exacto, pero otra aplicación está activa cuando comienzo a ejecutar el código, en su lugar informa de esa aplicación.
Si, por el contrario, cambia su código para ejecutar NSRunLoop
del hilo principal y usa un temporizador de 1 segundo, debería funcionar.
Aquí hay un ejemplo rápido:
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
@interface Foo : NSObject
- (void)run;
@end
@implementation Foo
- (void)run {
for (NSRunningApplication *currApp in [[NSWorkspace sharedWorkspace] runningApplications]) {
if ([currApp isActive]) {
NSLog(@"* %@", [currApp localizedName]);
} else {
NSLog(@" %@", [currApp localizedName]);
}
}
NSLog(@"---");
}
@end
int main(int argc, char *argv[]) {
NSAutoreleasePool *p = [NSAutoreleasePool new];
Foo *foo = [[Foo new] autorelease];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:foo
selector:@selector(run)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] run];
[p release];
}