Какой «правильный» способ идентифицировать текущее активное приложение в OSX 10.6+? - PullRequest
22 голосов
/ 02 декабря 2011

Я пытаюсь определить, какое приложение OSX в данный момент активно. Я понимаю, что в OSX 10.5 это можно сделать с помощью:

[[NSWorkspace sharedWorkspace] activeApplication]

однако, это было устарело в 10.6 +.

В документации разработчиков Apple говорится, что это должно быть сделано через свойство 'active' объекта NSRunningApplication. Я подумал, что один из способов подойти к этому - получить список всех запущенных приложений через

[[NSWorkspace sharedWorkspace] runningApplications]

, а затем выполните цикл, проверяя свойство 'active' каждого приложения. Однако следующий тестовый код работает не так, как я ожидал: при компиляции и запуске из Terminal.app только приложение «терминал» всегда помечается как активное, независимо от того, выбрал ли я другое приложение.

#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;
}

Что я делаю не так? Я неправильно понял, как работает «активное» свойство?

(Также, пожалуйста, не стесняйтесь критиковать мой код Objective C - это моя первая попытка достижения цели C, так что я знаю, что она может быть ужасно уродливой для обученного глаза! Пожалуйста, прости меня! :) Любые предложения приветствуются .)

Ответы [ 4 ]

19 голосов
/ 02 декабря 2011

Ваша проблема в том, что ваше приложение не может получить какие-либо события от системы, информирующие его об изменении текущего приложения, и поэтому оно никогда не обновляет активное свойство в экземплярах NSRunningApplication.Если я использую точно такой же код, но другое приложение активно, когда я запускаю код, оно вместо этого сообщает об этом приложении.

Если вместо этого вы измените свой код, чтобы запустить NSRunLoop основного потока и использовать 1секундный таймер, он должен работать.

Вот краткий пример:

#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];
}
17 голосов
/ 02 декабря 2011

Опрос каждую секунду или около того, чтобы узнать, что текущее приложение неэффективно, и это неправильный путь для этого.Намного лучше подойти к этому, чтобы просто настроить свой процесс для получения уведомления 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;
}
12 голосов
/ 17 мая 2013

Начиная с OS X 10.7 NSWorkspace также имеет удобный метод:

- (NSRunningApplication *)frontmostApplication;

Также теперь вы можете использовать диспетчерские вызовы Grand Central, чтобы совершать повторяющиеся вызовы с тайм-аутом.

Примерно так:

- (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
    }); 
}
1 голос
/ 02 декабря 2011

Примечания для NSWorkspace activeApplication говорят:

Особые замечания

Настоятельно рекомендуется использовать приложение NSRunningApplication. классы currentApplication или активные методы для получения этой информации в целевых приложениях для Mac OS X v10.6 и новее.

Вы, вероятно, должны сделать набор кода 10.6 и новее и набор кода 10.5.X и старше.

B.T.W., Метод NSWorkspace был помечен как устаревший только с 10.7, но NSRunningApplication пришел с 10.6.

О, вот альтернатива, которая является 64-битной совместимой, если вы включите платформу Application Services:

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...