Подтвердить что ты не робот

Каков "правильный" способ определения активного приложения в OSX 10.6+?

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

[[NSWorkspace sharedWorkspace] activeApplication]

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

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

[[NSWorkspace sharedWorkspace] runningApplications]

а затем выполните цикл, проверив "активное" свойство каждого приложения. Однако следующий тестовый код не ведет себя так, как я ожидал: когда он компилируется и запускается с 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, поэтому я знаю, что это может быть ужасно уродливым для обученного глаза! Пожалуйста, простите меня!:) Любые предложения приветствуются.)

4b9b3361

Ответ 1

Ваша проблема заключается в том, что ваше приложение не может получать какие-либо события из системы, информируя его о том, что текущее приложение изменилось, и поэтому оно никогда не обновляет активное свойство в экземплярах 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];
}

Ответ 2

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

Ответ 3

В 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
    }); 
}

Ответ 4

Заметки для NSWorkspace activeApplication говорят:

Особые соображения

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

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

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

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

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