NSWorkspaceWillPowerOffNotification 从未调用过

NSWorkspaceWillPowerOffNotification never called

我正在尝试 运行 后台进程中的一个程序,它将注册系统中的每个关闭事件。

通过注册到 NSWorkspaceWillPowerOffNotification 来执行此操作,如下所示:

#import <AppKit/AppKit.h>

@interface ShutDownHandler : NSObject <NSApplicationDelegate>

- (void)computerWillShutDownNotification:(NSNotification *)notification;

@end


int main(int argc, char* argv[]) {
    NSNotificationCenter *notCenter;

    notCenter = [[NSWorkspace sharedWorkspace] notificationCenter];

    ShutDownHandler* sdh = [ShutDownHandler new];

    [NSApplication sharedApplication].delegate = sdh;

    [notCenter addObserver:sdh
                  selector:@selector(computerWillShutDownNotification:)
                      name:NSWorkspaceWillPowerOffNotification
                    object:nil];

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [[NSFileManager defaultManager] createFileAtPath:@"./output.txt" contents:nil attributes:nil];
    });

    [[NSRunLoop currentRunLoop] run];

    return 0;
}


@implementation ShutDownHandler

- (void)computerWillShutDownNotification:(NSNotification *)notification {
    NSFileHandle* file = [NSFileHandle fileHandleForUpdatingAtPath: @"./output.txt"];
    [file seekToEndOfFile];

    NSDateFormatter* fmt = [NSDateFormatter new];
    [fmt setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate* current = [NSDate date];

    NSString* dateStr = [fmt stringFromDate:current];
    [dateStr writeToFile:@"./output.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
    return NSTerminateCancel;
}

@end

出于某种我无法理解的原因,从未调用 NSWorkspaceWillPowerOffNotification 处理程序!

还在我的 .plist 中添加了以下内容:

<key>NSSupportsSuddenTermination</key>
<false/>

但关闭我的系统时仍然没有注册通知。

知道为什么吗?

当您说 运行 您的代码作为后台进程时,您的意思是它基于根据 /Library/LaunchDeamon 中的 plist 文件处理的 launchd 守护进程?在这种情况下,将不会发送通知。在 Cocoa application

下尝试 运行

似乎此通知不适用于与 AppKit 链接的 LaunchDaemon。

我仍在寻找在后台守护进程上获取这些通知的方法。

供日后参考:

我无法注册到上述事件并看到它发生了。

最后,我采用了不同的方法: apple open-source power management

在这里,您可以看到我们有通知名称,例如: "com.apple.system.loginwindow.logoutNoReturn"

您可以注册到这些网站,这样就可以了。

希望有一天这会对某人有所帮助:)

我可以通过添加 NSApplicationActivationPolicyAccessory 并用 [NSApp run] 替换 运行 循环来使 NSWorkspaceWillPowerOffNotificationapplicationShouldTerminate: 工作。如果您想继续监视所有注销(and/or 次关机尝试),它仍然不会很有用,因为它仅在用户已经登录时启动进程时才有效,并且它只能检测当前 UI 登录会话的 power-off/logout 通知和“应用应终止”委托。

属于当前 UI 登录会话的“应用程序”在用户注销时被 OS 终止。非应用程序(无 [NSApp run])进程将继续 运行,即使在用户注销后也是如此。

因此,如果您想继续监视用户注销(或尝试关闭电源),您将需要一个非应用程序(无 [NSApp run])进程。

NSWorkspaceSessionDidBecomeActiveNotificationNSWorkspaceSessionDidResignActiveNotification 可以在没有 [NSApp run] 的情况下工作,如果您想监视用户是退出还是返回。

或者您可以尝试系统配置 API(如果它适用于您的用例)。

“技术问答 QA1133:确定控制台用户登录状态”https://developer.apple.com/library/archive/qa/qa1133/_index.html

听起来 com.apple.system.loginwindow.logoutNoReturn 是无论您想做什么的最佳选择。很高兴知道!

这里是更新后的代码(虽然不是很有用):

// cc powreoff-notification.m -o poweroff-notification -framework AppKit
#import <AppKit/AppKit.h>

@interface ShutDownHandler : NSObject <NSApplicationDelegate>

- (void)computerWillShutDownNotification:(NSNotification *)notification;

@end

int main(int argc, char* argv[]) {
    @autoreleasepool {
        NSLog(@"%s", __func__);
        NSNotificationCenter* notCenter = [[NSWorkspace sharedWorkspace] notificationCenter];

        ShutDownHandler* sdh = [ShutDownHandler new];

        [NSApplication sharedApplication].delegate = sdh;

        [notCenter addObserver:sdh
                      selector:@selector(computerWillShutDownNotification:)
                          name:NSWorkspaceWillPowerOffNotification
                        object:nil];

        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            [[NSFileManager defaultManager] createFileAtPath:@"./output.txt" contents:nil attributes:nil];
        });

        // without NSApplicationActivationPolicyAccessory, the [NSApp run] process gets killed on
        // poweroff/logout instead of getting the power off notification or applicationShouldTerminate:.
        [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];

        // without [NSApp run], you won't get the power off notification or applicationShouldTerminate:.
        //[[NSRunLoop currentRunLoop] run];
        [NSApp run];

        // this process needs to be launched while the user has already logged in. otherwise,
        // you won't get the power off notification or applicationShouldTerminate:.
    }

    return 0;
}

@implementation ShutDownHandler

- (void)computerWillShutDownNotification:(NSNotification *)notification {
    NSLog(@"%s", __func__);
    NSFileHandle* file = [NSFileHandle fileHandleForUpdatingAtPath: @"./output.txt"];
    [file seekToEndOfFile];

    NSDateFormatter* fmt = [NSDateFormatter new];
    [fmt setDateFormat:@"yyyy-MM-dd HH:mm:ss\n"];
    NSDate* current = [NSDate date];

    NSString* dateStr = [fmt stringFromDate:current];
    [dateStr writeToFile:@"./output.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
    NSLog(@"%s", __func__);
    return NSTerminateCancel;
}

@end