如何让程序在登录时自动启动?

时间:2022-06-19 20:46:45

How can I set a menu bar app to automatically startup on login? I want this to be default. Can i do this simply by adding a bool in the info.plist?

如何设置菜单栏应用程序以在登录时自动启动?我希望这是默认的。我可以通过在info.plist中添加一个bool来做到这一点吗?

3 个解决方案

#1


14  

You use the Session Login Items shared file list. This is the list that is displayed in System Preferences when you check your login items under your profile settings.

您使用会话登录项共享文件列表。当您在配置文件设置下检查登录项时,这是“系统偏好设置”中显示的列表。

The typical scenario in an app is to provide a checkbox in the app's preferences, that allows the user to choose wether they want to start the app or not on login. If you intend to distribute through the app store DO NOT set the app to launch on login by default. You will get rejected :)

应用程序中的典型场景是在应用程序的首选项中提供一个复选框,允许用户选择是否要在启动应用程序时启动应用程序。如果您打算通过应用程序商店分发,请不要将应用程序设置为默认登录时启动。你会被拒绝:)

So, in this scenario, we will make a property, say in the App Delegate, called "launchOnLogin" and we will bind the value of the check box to this property.

因此,在这种情况下,我们将在App Delegate中创建一个名为“launchOnLogin”的属性,我们将复选框的值绑定到此属性。

The getter method will check if our app's bundle id is in the shared list and return true or false.

getter方法将检查我们的app的bundle id是否在共享列表中并返回true或false。

The setter method ads or removes the item from the shared list.

setter方法在共享列表中广告或删除项目。

Here goes:

AppDelegate.h

@property (atomic, assign) BOOL launchOnLogin;

AppDelegate.m

- (BOOL)launchOnLogin 
{
    LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
    CFArrayRef snapshotRef = LSSharedFileListCopySnapshot(loginItemsListRef, NULL);
    NSArray* loginItems = [NSMakeCollectable(snapshotRef) autorelease];
    NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
    for (id item in loginItems) {
        LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item;
        CFURLRef itemURLRef;
        if (LSSharedFileListItemResolve(itemRef, 0, &itemURLRef, NULL) == noErr) {
            NSURL *itemURL = (NSURL *)[NSMakeCollectable(itemURLRef) autorelease];
            if ([itemURL isEqual:bundleURL]) {
                return YES;
            }
        }
    }
    return NO;
}

- (void)setLaunchOnLogin:(BOOL)launchOnLogin
{    
    NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
    LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);

    if (launchOnLogin) {
        NSDictionary *properties;
        properties = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"com.apple.loginitem.HideOnLaunch"];
        LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsListRef, kLSSharedFileListItemLast, NULL, NULL, (CFURLRef)bundleURL, (CFDictionaryRef)properties,NULL);
        if (itemRef) {
            CFRelease(itemRef);
        }
    } else {
        LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
        CFArrayRef snapshotRef = LSSharedFileListCopySnapshot(loginItemsListRef, NULL);
        NSArray* loginItems = [NSMakeCollectable(snapshotRef) autorelease];

        for (id item in loginItems) {
            LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item;
            CFURLRef itemURLRef;            
            if (LSSharedFileListItemResolve(itemRef, 0, &itemURLRef, NULL) == noErr) {
                NSURL *itemURL = (NSURL *)[NSMakeCollectable(itemURLRef) autorelease];
                if ([itemURL isEqual:bundleURL]) {
                    LSSharedFileListItemRemove(loginItemsListRef, itemRef);
                }
            }
        }
    }
}

That's pretty much it. Now if you do the binding and everything correctly, you'll see how your app appears and dissappears from the System Preferences list in realtime.

这就是它。现在,如果您正确地执行绑定和所有操作,您将看到您的应用程序如何实时显示并从系统首选项列表中消失。

The part that actually makes the app launch on login

// Get the path of the app
NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];

// Get the list you want to add the path to
LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);

// Set the app to be hidden on launch
NSDictionary *properties = @{@"com.apple.loginitem.HideOnLaunch": @YES};

// Add the item to the list
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsListRef, kLSSharedFileListItemLast, NULL, NULL, (CFURLRef)bundleURL, (CFDictionaryRef)properties,NULL);

Please note that the code above is retain/release but it is pretty trivial to convert it to ARC if you need it.

请注意,上面的代码是保留/释放,但如果需要,将它转换为ARC非常简单。

Hope this helps.

希望这可以帮助。

#2


3  

For anyone looking for an updated Swift compatible version of this code, I've made a gist here: https://gist.github.com/plapier/f8e1dde1b1624dfbb3e4

对于任何寻找此代码更新的Swift兼容版本的人,我在这里做了一个要点:https://gist.github.com/plapier/f8e1dde1b1624dfbb3e4

Just call toggleLaunchAtStartup() from within your app. (Most likely inside a checkbox IBAction).

只需从您的应用程序中调用toggleLaunchAtStartup()即可。 (很可能在复选框IBAction内)。

And the actual code for reference:

以及实际代码供参考:

import Foundation

func applicationIsInStartUpItems() -> Bool {
    return (itemReferencesInLoginItems().existingReference != nil)
}

func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
    if let appURL : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
        if let loginItemsRef = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef? {

            let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
            let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as! LSSharedFileListItemRef

            for (index, loginItem) in enumerate(loginItems) {
                let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(index) as! LSSharedFileListItemRef
                if let itemURL = LSSharedFileListItemCopyResolvedURL(currentItemRef, 0, nil) {
                    if (itemURL.takeRetainedValue() as NSURL).isEqual(appURL) {
                        return (currentItemRef, lastItemRef)
                    }
                }
            }

            return (nil, lastItemRef)
        }
    }

    return (nil, nil)
}

func toggleLaunchAtStartup() {
    let itemReferences = itemReferencesInLoginItems()
    let shouldBeToggled = (itemReferences.existingReference == nil)
    if let loginItemsRef = LSSharedFileListCreate( nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef? {
        if shouldBeToggled {
            if let appUrl : CFURLRef = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
                println("Add login item")
                LSSharedFileListInsertItemURL(loginItemsRef, itemReferences.lastReference, nil, nil, appUrl, nil, nil)
            }
        } else {
            if let itemRef = itemReferences.existingReference {
                println("Remove login item")
                LSSharedFileListItemRemove(loginItemsRef,itemRef);
            }
        }
    }
}

Used with Xcode 6.3.2, Swift 1.2

与Xcode 6.3.2,Swift 1.2一起使用

#3


2  

You can run the apple script given below from the application to add it to the Login Item.

您可以从应用程序运行下面给出的苹果脚本,将其添加到登录项。

on run args_list
    tell application "System Events" to make login item at end with properties {path:item 1 of args_list, hidden:false}
end run

Save the above script as AddToLogin.scpt and add this to project.

将上述脚本保存为AddToLogin.scpt并将其添加到项目中。

Add the code given below to the application startup

将下面给出的代码添加到应用程序启动中

NSString *appPath =[[NSBundle mainBundle] bundlePath] ;
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"AddToLogin" ofType:@"scpt"];

NSArray *argArray;
if(scriptPath)
{
    argArray = [NSArray arrayWithObjects:scriptPath, appPath, nil];
    NSTask * task = [[NSTask alloc] init];
    [task setLaunchPath:@"/usr/bin/osascript"];
    [task setArguments:argArray];
    [task launch];
    [task waitUntilExit];
}

The link given below explains the osascript way to do the same

下面给出的链接解释了osascript做同样的方法

http://hints.macworld.com/article.php?story=20111226075701552

#1


14  

You use the Session Login Items shared file list. This is the list that is displayed in System Preferences when you check your login items under your profile settings.

您使用会话登录项共享文件列表。当您在配置文件设置下检查登录项时,这是“系统偏好设置”中显示的列表。

The typical scenario in an app is to provide a checkbox in the app's preferences, that allows the user to choose wether they want to start the app or not on login. If you intend to distribute through the app store DO NOT set the app to launch on login by default. You will get rejected :)

应用程序中的典型场景是在应用程序的首选项中提供一个复选框,允许用户选择是否要在启动应用程序时启动应用程序。如果您打算通过应用程序商店分发,请不要将应用程序设置为默认登录时启动。你会被拒绝:)

So, in this scenario, we will make a property, say in the App Delegate, called "launchOnLogin" and we will bind the value of the check box to this property.

因此,在这种情况下,我们将在App Delegate中创建一个名为“launchOnLogin”的属性,我们将复选框的值绑定到此属性。

The getter method will check if our app's bundle id is in the shared list and return true or false.

getter方法将检查我们的app的bundle id是否在共享列表中并返回true或false。

The setter method ads or removes the item from the shared list.

setter方法在共享列表中广告或删除项目。

Here goes:

AppDelegate.h

@property (atomic, assign) BOOL launchOnLogin;

AppDelegate.m

- (BOOL)launchOnLogin 
{
    LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
    CFArrayRef snapshotRef = LSSharedFileListCopySnapshot(loginItemsListRef, NULL);
    NSArray* loginItems = [NSMakeCollectable(snapshotRef) autorelease];
    NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
    for (id item in loginItems) {
        LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item;
        CFURLRef itemURLRef;
        if (LSSharedFileListItemResolve(itemRef, 0, &itemURLRef, NULL) == noErr) {
            NSURL *itemURL = (NSURL *)[NSMakeCollectable(itemURLRef) autorelease];
            if ([itemURL isEqual:bundleURL]) {
                return YES;
            }
        }
    }
    return NO;
}

- (void)setLaunchOnLogin:(BOOL)launchOnLogin
{    
    NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
    LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);

    if (launchOnLogin) {
        NSDictionary *properties;
        properties = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"com.apple.loginitem.HideOnLaunch"];
        LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsListRef, kLSSharedFileListItemLast, NULL, NULL, (CFURLRef)bundleURL, (CFDictionaryRef)properties,NULL);
        if (itemRef) {
            CFRelease(itemRef);
        }
    } else {
        LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
        CFArrayRef snapshotRef = LSSharedFileListCopySnapshot(loginItemsListRef, NULL);
        NSArray* loginItems = [NSMakeCollectable(snapshotRef) autorelease];

        for (id item in loginItems) {
            LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)item;
            CFURLRef itemURLRef;            
            if (LSSharedFileListItemResolve(itemRef, 0, &itemURLRef, NULL) == noErr) {
                NSURL *itemURL = (NSURL *)[NSMakeCollectable(itemURLRef) autorelease];
                if ([itemURL isEqual:bundleURL]) {
                    LSSharedFileListItemRemove(loginItemsListRef, itemRef);
                }
            }
        }
    }
}

That's pretty much it. Now if you do the binding and everything correctly, you'll see how your app appears and dissappears from the System Preferences list in realtime.

这就是它。现在,如果您正确地执行绑定和所有操作,您将看到您的应用程序如何实时显示并从系统首选项列表中消失。

The part that actually makes the app launch on login

// Get the path of the app
NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];

// Get the list you want to add the path to
LSSharedFileListRef loginItemsListRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);

// Set the app to be hidden on launch
NSDictionary *properties = @{@"com.apple.loginitem.HideOnLaunch": @YES};

// Add the item to the list
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsListRef, kLSSharedFileListItemLast, NULL, NULL, (CFURLRef)bundleURL, (CFDictionaryRef)properties,NULL);

Please note that the code above is retain/release but it is pretty trivial to convert it to ARC if you need it.

请注意,上面的代码是保留/释放,但如果需要,将它转换为ARC非常简单。

Hope this helps.

希望这可以帮助。

#2


3  

For anyone looking for an updated Swift compatible version of this code, I've made a gist here: https://gist.github.com/plapier/f8e1dde1b1624dfbb3e4

对于任何寻找此代码更新的Swift兼容版本的人,我在这里做了一个要点:https://gist.github.com/plapier/f8e1dde1b1624dfbb3e4

Just call toggleLaunchAtStartup() from within your app. (Most likely inside a checkbox IBAction).

只需从您的应用程序中调用toggleLaunchAtStartup()即可。 (很可能在复选框IBAction内)。

And the actual code for reference:

以及实际代码供参考:

import Foundation

func applicationIsInStartUpItems() -> Bool {
    return (itemReferencesInLoginItems().existingReference != nil)
}

func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
    if let appURL : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
        if let loginItemsRef = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef? {

            let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
            let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as! LSSharedFileListItemRef

            for (index, loginItem) in enumerate(loginItems) {
                let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(index) as! LSSharedFileListItemRef
                if let itemURL = LSSharedFileListItemCopyResolvedURL(currentItemRef, 0, nil) {
                    if (itemURL.takeRetainedValue() as NSURL).isEqual(appURL) {
                        return (currentItemRef, lastItemRef)
                    }
                }
            }

            return (nil, lastItemRef)
        }
    }

    return (nil, nil)
}

func toggleLaunchAtStartup() {
    let itemReferences = itemReferencesInLoginItems()
    let shouldBeToggled = (itemReferences.existingReference == nil)
    if let loginItemsRef = LSSharedFileListCreate( nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil).takeRetainedValue() as LSSharedFileListRef? {
        if shouldBeToggled {
            if let appUrl : CFURLRef = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
                println("Add login item")
                LSSharedFileListInsertItemURL(loginItemsRef, itemReferences.lastReference, nil, nil, appUrl, nil, nil)
            }
        } else {
            if let itemRef = itemReferences.existingReference {
                println("Remove login item")
                LSSharedFileListItemRemove(loginItemsRef,itemRef);
            }
        }
    }
}

Used with Xcode 6.3.2, Swift 1.2

与Xcode 6.3.2,Swift 1.2一起使用

#3


2  

You can run the apple script given below from the application to add it to the Login Item.

您可以从应用程序运行下面给出的苹果脚本,将其添加到登录项。

on run args_list
    tell application "System Events" to make login item at end with properties {path:item 1 of args_list, hidden:false}
end run

Save the above script as AddToLogin.scpt and add this to project.

将上述脚本保存为AddToLogin.scpt并将其添加到项目中。

Add the code given below to the application startup

将下面给出的代码添加到应用程序启动中

NSString *appPath =[[NSBundle mainBundle] bundlePath] ;
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"AddToLogin" ofType:@"scpt"];

NSArray *argArray;
if(scriptPath)
{
    argArray = [NSArray arrayWithObjects:scriptPath, appPath, nil];
    NSTask * task = [[NSTask alloc] init];
    [task setLaunchPath:@"/usr/bin/osascript"];
    [task setArguments:argArray];
    [task launch];
    [task waitUntilExit];
}

The link given below explains the osascript way to do the same

下面给出的链接解释了osascript做同样的方法

http://hints.macworld.com/article.php?story=20111226075701552