一 寫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文件中,只不過實現的方式不一樣罷了。