添加移除开机启动项
一 写plist到~/Library/LaunchAgents/ 目录下
- // 配置开机默认启动
- -(void)installDaemon{
- NSString* launchFolder = [NSString stringWithFormat:@"%@/Library/LaunchAgents",NSHomeDirectory()];
- NSString * boundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey];
- NSString* dstLaunchPath = [launchFolder stringByAppendingFormat:@"/%@.plist",boundleID];
- NSFileManager* fm = [NSFileManager defaultManager];
- BOOL isDir = NO;
- //已经存在启动项中,就不必再创建
- if ([fm fileExistsAtPath:dstLaunchPath isDirectory:&isDir] && !isDir) {
- return;
- }
- //下面是一些配置
- NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
- NSMutableArray* arr = [[NSMutableArray alloc] init];
- [arr addObject:[[NSBundle mainBundle] executablePath]];
- [arr addObject:@"-runMode"];
- [arr addObject:@"autoLaunched"];
- [dict setObject:[NSNumber numberWithBool:true] forKey:@"RunAtLoad"];
- [dict setObject:boundleID forKey:@"Label"];
- [dict setObject:arr forKey:@"ProgramArguments"];
- isDir = NO;
- if (![fm fileExistsAtPath:launchFolder isDirectory:&isDir] && isDir) {
- [fm createDirectoryAtPath:launchFolder withIntermediateDirectories:NO attributes:nil error:nil];
- }
- [dict writeToFile:dstLaunchPath atomically:NO];
- [arr release]; arr = nil;
- [dict release]; dict = nil;
- }
关于启动项的配置可以去开发文档搜索:Creating launchd Daemons and Agents。
取消开机启动则只要删除~/Library/LaunchAgents/ 目录下相应的plist文件即可。
- // 取消配置开机默认启动
- -(void)unInstallDaemon{
- NSString* launchFolder = [NSString stringWithFormat:@"%@/Library/LaunchAgents",NSHomeDirectory()];
- BOOL isDir = NO;
- NSFileManager* fm = [NSFileManager defaultManager];
- if (![fm fileExistsAtPath:launchFolder isDirectory:&isDir] && isDir) {
- return;
- }
- NSString * boundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey];
- NSString* srcLaunchPath = [launchFolder stringByAppendingFormat:@"/%@.plist",boundleID];
- [fm removeItemAtPath:srcLaunchPath error:nil];
- }
二.使用LoginItemsAE
在开发文档中搜索LoginItemsAE即可搜到它的源码,包含LoginItemsAE.c和LoginItemsAE.h两个文件。其原理是写配置信息到~/Library/Preferences/com.apple.loginitems.plist 文件。打开com.apple.loginitems.plist文件找到CustomListItems那一项,展开就可以看到开机启动项的一些信息(包括app名称,所在路径。。。)
图1:com.apple.loginitems.plist 开机启动项内容
下面简单介绍下LoginItemsAE.h 中的几个API。
- //返回开机启动项列表,传入itemsPtr地址即可,
- extern OSStatus LIAECopyLoginItems(CFArrayRef *itemsPtr);
- //添加开机启动项,hideIt参数一般是传 NO
- extern OSStatus LIAEAddURLAtEnd(CFURLRef item, Boolean hideIt);
- //移除开机启动项
- extern OSStatus LIAERemove(CFIndex itemIndex);
是不是觉得上面的接口不是很好用呢,特别是移除启动项的那个接口,必须得知道要移除的index,如果能根据文件路径移除就好了。下面用Objective-C语法重新封装这几个接口,更方便调用。
- #import "UKLoginItemRegistry.h"
- @implementation UKLoginItemRegistry
- +(NSArray*) allLoginItems
- {
- NSArray* itemsList = nil;
- OSStatus err = LIAECopyLoginItems( (CFArrayRef*) &itemsList ); // Take advantage of toll-free bridging.
- if( err != noErr )
- {
- NSLog(@"Couldn't list login items error %ld", err);
- return nil;
- }
- return [itemsList autorelease];
- }
- +(BOOL) addLoginItemWithPath: (NSString*)path hideIt: (BOOL)hide
- {
- NSURL* url = [NSURL fileURLWithPath: path];
- return [self addLoginItemWithURL: url hideIt: hide];
- }
- //根据文件路径移除启动项
- +(BOOL) removeLoginItemWithPath: (NSString*)path
- {
- int idx = [self indexForLoginItemWithPath: path];
- return (idx != -1) && [self removeLoginItemAtIndex: idx]; // Found item? Remove it and return success flag. Else return NO.
- }
- +(BOOL) addLoginItemWithURL: (NSURL*)url hideIt: (BOOL)hide // Main bottleneck for adding a login item.
- {
- OSStatus err = LIAEAddURLAtEnd( (CFURLRef) url, hide ); // CFURLRef is toll-free bridged to NSURL.
- if( err != noErr )
- NSLog(@"Couldn't add login item error %ld", err);
- return( err == noErr );
- }
- +(BOOL) removeLoginItemAtIndex: (int)idx // Main bottleneck for getting rid of a login item.
- {
- OSStatus err = LIAERemove( idx );
- if( err != noErr )
- NSLog(@"Couldn't remove login intem error %ld", err);
- return( err == noErr );
- }
- +(int) indexForLoginItemWithURL: (NSURL*)url // Main bottleneck for finding a login item in the list.
- {
- NSArray* loginItems = [self allLoginItems];
- NSEnumerator* enny = [loginItems objectEnumerator];
- NSDictionary* currLoginItem = nil;
- int x = 0;
- while(( currLoginItem = [enny nextObject] ))
- {
- if( [[currLoginItem objectForKey: UKLoginItemURL] isEqualTo: url] )
- return x;
- x++;
- }
- return -1;
- }
- +(int) indexForLoginItemWithPath: (NSString*)path
- {
- NSURL* url = [NSURL fileURLWithPath: path];
- return [self indexForLoginItemWithURL: url];
- }
- +(BOOL) removeLoginItemWithURL: (NSURL*)url
- {
- int idx = [self indexForLoginItemWithURL: url];
- return (idx != -1) && [self removeLoginItemAtIndex: idx]; // Found item? Remove it and return success flag. Else return NO.
- }
- @end
上面的代码是不是觉得亲切多了啊?
不过这几个接口有点缺陷:只能用i386来编译,用x86_64编译会报错的。
三. 使用LaunchServices修改启动项
可以使用LaunchServices/LSSharedFileList.h 里面的方法来更改启动项,但是这些方法只支持10.5及以上的系统。下面简单的介绍下这些方法。
- //这个方法返回启动项列表
- extern LSSharedFileListRef
- LSSharedFileListCreate(
- CFAllocatorRef inAllocator,
- CFStringRef inListType,
- CFTypeRef listOptions)
- //添加新的启动项
- extern LSSharedFileListItemRef LSSharedFileListInsertItemURL(
- LSSharedFileListRef inList,
- LSSharedFileListItemRef insertAfterThisItem,
- CFStringRef inDisplayName,
- IconRef inIconRef,
- CFURLRef inURL,
- CFDictionaryRef inPropertiesToSet,
- CFArrayRef inPropertiesToClear)
- //移除启动项
- extern OSStatus LSSharedFileListItemRemove(
- LSSharedFileListRef inList,
- LSSharedFileListItemRef inItem)
- //最后一个方法用来解析启动项的 URL,用来检索启动项列表里的东西
- extern OSStatus LSSharedFileListItemResolve(
- LSSharedFileListItemRef inItem,
- UInt32 inFlags,
- CFURLRef * outURL,
- FSRef * outRef)
使用下面两个方法来封装上面的这些API,使更易于使用。你也可以改成传入app路径添加启动项。- (void) addAppAsLoginItem:(NSString *)appPath,把这句NSString * appPath = [[NSBundle mainBundle] bundlePath];注视掉就行了。
- -(void) addAppAsLoginItem{
- NSString * appPath = [[NSBundle mainBundle] bundlePath];
- // This will retrieve the path for the application
- // For example, /Applications/test.app
- CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:appPath];
- // Create a reference to the shared file list.
- // We are adding it to the current user only.
- // If we want to add it all users, use
- // kLSSharedFileListGlobalLoginItems instead of
- //kLSSharedFileListSessionLoginItems
- LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL,
- kLSSharedFileListSessionLoginItems, NULL);
- if (loginItems) {
- //Insert an item to the list.
- LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems,
- kLSSharedFileListItemLast, NULL, NULL,
- url, NULL, NULL);
- if (item){
- CFRelease(item);
- }
- }
- CFRelease(loginItems);
- }
- -(void) deleteAppFromLoginItem{
- NSString * appPath = [[NSBundle mainBundle] bundlePath];
- // This will retrieve the path for the application
- // For example, /Applications/test.app
- CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:appPath];
- // Create a reference to the shared file list.
- LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL,
- kLSSharedFileListSessionLoginItems, NULL);
- if (loginItems) {
- UInt32 seedValue;
- //Retrieve the list of Login Items and cast them to
- // a NSArray so that it will be easier to iterate.
- NSArray *loginItemsArray = (NSArray *)LSSharedFileListCopySnapshot(loginItems, &seedValue);
- int i = 0;
- for(i ; i< [loginItemsArray count]; i++){
- LSSharedFileListItemRef itemRef = (LSSharedFileListItemRef)[loginItemsArray
- objectAtIndex:i];
- //Resolve the item with URL
- if (LSSharedFileListItemResolve(itemRef, 0, (CFURLRef*) &url, NULL) == noErr) {
- NSString * urlPath = [(NSURL*)url path];
- if ([urlPath compare:appPath] == NSOrderedSame){
- LSSharedFileListItemRemove(loginItems,itemRef);
- }
- }
- }
- [loginItemsArray release];
- }
- }
详情请打开:http://cocoatutorial.grapewave.com/2010/02/creating-andor-removing-a-login-item/
四. 使用NSUserDefaults修改启动项
下面通过分类给NSUserDefaults添加新的方法。
- @implementation NSUserDefaults (Additions)
- - (BOOL)addApplicationToLoginItems:(NSString *)path {
- NSDictionary *domain = [self persistentDomainForName:@"loginwindow"];
- NSArray *apps = [domain objectForKey:@"AutoLaunchedApplicationDictionary"];
- NSArray *matchingApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"Path CONTAINS %@", path]];
- if ([matchingApps count] == 0) {
- NSMutableDictionary *newDomain = [domain mutableCopy];
- NSMutableArray *newApps = [[apps mutableCopy] autorelease];
- NSDictionary *app = [NSDictionary dictionaryWithObjectsAndKeys:path, @"Path", [NSNumber numberWithBool:NO], @"Hide", nil];
- [newApps addObject:app];
- [newDomain setObject:newApps forKey:@"AutoLaunchedApplicationDictionary"];
- [self setPersistentDomain:newDomain forName:@"loginwindow"];
- return [self synchronize];
- }
- return NO;
- }
- - (BOOL)removeApplicationFromLoginItems:(NSString *)name {
- NSDictionary *domain = [self persistentDomainForName:@"loginwindow"];
- NSArray *apps = [domain objectForKey:@"AutoLaunchedApplicationDictionary"];
- NSArray *newApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"not Path CONTAINS %@", name]];
- if (![apps isEqualToArray:newApps]) {
- NSMutableDictionary *newDomain = [domain mutableCopy];
- [newDomain setObject:newApps forKey:@"AutoLaunchedApplicationDictionary"];
- [self setPersistentDomain:newDomain forName:@"loginwindow"];
- return [self synchronize];
- }
- return NO;
- }
- @end
详情请打开:http://www.danandcheryl.com/2011/02/how-to-modify-the-dock-or-login-items-on-os-x
后面三种方法都是写配置到~/Library/Preferences/com.apple.loginitems.plist文件中,只不过实现的方式不一样罢了。