-
本文为CocoaChina网友品位生活投稿
说起启动盘制作工具,目前国外的开发人员已经开发有《DiskMaker》 和 《Install Disk Creator》这两个软件。
DiskMaker下载地址:
http://diskmakerx.com/
Install Disk Creator 下载地址:
https://macdaddy.io/install-disk-creator/
我在写代码的时候还不知道有这两个工具,后来知道了有这两个工具了,所以本篇文章更多的是讲解下我当初的基本制作过程,顺便把源码分享给大家。
一、起因
由于本人经常折腾系统,所以很容易就搞坏了系统,然后每次都需要做系统盘,然后重做系统,很是麻烦。大概是要做一下步骤:
-
进入MAC 终端程序
-
输入"sudo"
-
输入"空格"
-
拖文件“createinstallmedia”到终端(文件位置在安装程序》右键显示包文件》Contents》Resources里)(程序自动空格,若无空格请自行空格)
-
然后输入 "--volume"
-
输入"空格"
-
拖你准备的盘符为 "disk"盘或分区到终端(程序自动空格,若无空格请自行空格)
-
输入"--applicationpath"
-
输入"空格"
-
拖OS X安装程序到终端(程序自动空格,若无空格请自行空格)
-
然后输入 "--nointeraction"
-
按“return”(即回车)
-
输入系统密码(密码不会显示,直接回车)
-
等待启动盘制作完成。
然后我思考是否能做一个程序能够更方便的制作启动盘的一个工具,然后就开始行动了。
二、方案 与 思考
制作之前,我考虑到基本上需要做到的基本功能要有:
-
确定主要核心命令执行的方案。
-
做到能够调用系统终端命令。
-
做到能够使用Root权限调用系统终端命令。
-
做到能够自动识别获取到当前的磁盘盘符。
-
做到能够区分系统盘符和移动磁盘盘符。
-
做到能够监听磁盘重命名,插入,和卸载的变化。
-
做到能够监听制作的进度。
-
做到能够监听基本的制作过程的错误处理。
-
设计一个磁盘处理相关的类,用于外部调用处理。
主要核心命令执行的三个方案:
(1)使用NSTask 执行核心命令
NSTask是MAC OS X用来执行系统终端命令的一个类,所以使用NSTask可以执行系统终端命令,但是NSTask有一个缺点,在我所知的是,无法以Root权限进行执行命令,但是正好启动盘制作工具 createinstallmedia 是必须需要Root权限执行的,所以说这个方案不考虑了。
(2)使用 AppleScript 制作脚本,OS 开发程序调用
NSAppleScript 是可以执行AppleScript脚本的 一个类,用它可以执行AppleScript脚本,在AppleScript中,可以获得Root权限进行执行命令。
DiskMaker 就是用AppleScript脚本实现的核心功能,可以下载此软件,然后右键--显示包内容,然后找到源码.
(3)使用STPrivilegedTask执行终端命令,支持Root权限
可以通过 AuthorizationRef 来获取权限,执行命令,STPrivilegedTask就是对AuthorizationRef的封装,类似于NStask,只不过可以以Root权限执行命令。
最终我选择了使用 STPrivilegedTask 来实现核心命令执行。
三、行动
UI界面:
首先,我使用Xcode 创建了Mac OS 工程,然后
在Main.storyboard调整出了基本界面。
添加了 Combox 控件用于展示磁盘列表。
添加了按钮,用于触发进行开始制作。
添加了NSView实现NSDraggingDestination代理方法,用于接收用户拖拽的系统文件。
核心功能讲解:
一些比较简单的基本控件的使用我就不详细说了,例如拖拽获取文件,combox控件的使用等等。
(1)获取监听磁盘装载与卸载
想要获取和监听磁盘装载与卸载,需要用过苹果的一个 DiskArbitration 框架,这个框架提供了可支持注册监听磁盘的装载与卸载,和信息修改等回调事件。
下面直接贴上来有关代码
+ (void)registerDiskNotice{ //创建一个新的会话
(2)区分系统磁盘和移动磁盘
为了能够区分系统磁盘和移动磁盘,肯定是先要获取磁盘的相关信息,磁盘出现后的,监听回调里面有一个DADiskRef 类型的参数 ,这个DADiskRef变量里面存储了磁盘相关的信息,通过 DADiskCopyDescription 函数,把disk对象当做参数,调用后可以获取到磁盘描述信息,类型可转换为字典,如下图:
通过打印出来的信息可看到不少相关有用的内容,例如:
-
DAMediaBSDName:
用于唯一标识的磁盘,如果磁盘有重名那么BSDName也是唯一的。
-
DAVolumeName:
是卷名,也是有用的。因为检测可以检测到磁盘和卷,一个磁盘可以有多个卷,磁盘的话,是没有DAVolumeName的,可以用于更准确的筛选。
-
DAMediaSize:
磁盘的大小
-
DADeviceProtocol:
这个是最主要的,如果是移动磁盘,DADeviceProtocol 就会是USB,所以可通过这个计算。
过滤系统磁盘 和 移动磁盘的代码:
相关宏定义:
/// 统一处理磁盘的装载与卸载- (void)diskChange:(DiskChangeType)state disk:(DADiskRef)disk{
(3)监听磁盘信息的变化,例如重命名
注册这个监听即可
//注册磁盘信息变化回调
(4)监听制作进度
这里说一个思路,制作启动盘的时候,createinstallmedia会自动格式化磁盘,然后把指定的系统盘文件,慢慢拷贝进移动盘中,所以监听制作进度,我这里使用的是时钟,不断获取磁盘中系统文件的大小,如果达到和原文件一样的大小,说明就是制作进度完成了。
(5)调用系统终端命令
主要是看文档,然后看下STPrivilegedTask 怎么使用即可,剩下就是设计逻辑,和实现方式。
我大概说下我这边的:
首先是根据各个外部的变量,进行拼接成字符串,然后传递进DSTask自定义处理的一个类中,再进行分割参数,然后设置参数,和启动工具,然后执行launch命令即可。
- (void)runShell{ if (!self.codeArray.count) { return; }
其它说明:
还有很多细节没有说到,例如判断磁盘是否装载,制作过程中断处理,combox和工具类的建立和处理,逻辑之间的数据交换,空格文件名和磁盘空格的处理,等等这些,详细的可去看源码。
另外STPrivilegedTask 默认有个BUG,中文不支持,会计算错误长度。
需要修改这一块:
for (int i = 0; i < numberOfArguments; i++) { NSString *argString = arguments[i]; NSUInteger stringLength = [argString length];
使用注意:
在使用STPrivilegedTask,我发现无法监听到执行过程中内部的一些错误回调,所以说只有尽量避免了这种情况的出现,如果使用中发现制作不成功,或者点击制作后很久没有变化,可能内部命令提示出现错误,可手动格式化磁盘一次再进行制作,或者拔插磁盘后重新进行制作。
源码地址:https://github.com/DaSens/StartupDisk