Cocoa -- 添加和移除開機啓動項

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章