您好,脚本专家!肾上腺素刺激

Microsoft 脚本专家

毫无疑问,在我们看来为《TechNet 杂志》**撰稿是一份最受尊敬、最有价值的工作了。(当然,如果实际得到了报酬,那它的价值还会更高,但那是另外一回事了)。这份工作究竟有多么受人尊敬?让我们这样来解释一下:现在,在全世界,孩子们在被塞进被窝时都会看着他们的母亲并对她们说:“妈妈,等我长大了,我要为《TechNet 杂志》**撰写每月的脚本专栏。”

注释 当然,现在全世界有很多人都相信本专栏已由孩子们撰写了。但那同样是另一码事。

众所周知,脚本专家们非常享受为《TechNet 杂志》**撰写文章得到的盛誉;我们甚至学会了忍受狂热追星族和狗仔队。但实情是,我们从事这份工作不是为了荣耀和崇拜;而是为了从编写系统管理脚本获得纯粹的肾上腺素刺激。

无可否认,您很少会看到“纯粹的肾上腺素刺激”和“系统管理脚本”出现在同一句话中。(当然,“纯粹的肾上腺素刺激”前面的形容词是“完全没有”是个例外。)

一般人会认为系统管理脚本非常有用,但并不会让人感到非常激动;实际上,他或她很可能觉得脚本编写有些单调。原因如下:他们当中还没有一个人尝试过在 Windows Vista® 或 Windows Server® 2008 中编写引导配置数据脚本。

这个消息让您心动了,是吗?读到这里您可能就知道了,在 Windows Vista 和 Windows Server 2008 中,旧的 boot.ini 文件已被废弃,改为使用新的引导配置数据存储(在管理引导过程方面,它会提供更好的灵活性和功能)。

太酷了。但真正酷的是:托新的 Windows® 管理规范 (WMI) 提供程序的福,现在可以使用脚本来轻松地访问并完全管理引导配置数据 (BCD) 存储了。

不要担心脊柱中有种上下乱窜的奇怪感觉;它就是纯粹的肾上腺素刺激的感觉。开始和 BCD 脚本打交道后,您就会开始习惯这种感觉了。

在深入介绍之前,应声明我们无法一开始就涵盖可利用 BCD 提供程序完成的所有功能,至少在这一个专栏中是不可能的。(我们向杂志的编辑发了封电子邮件,询问是否能专门为编写引导配置数据脚本做一期合刊,但目前尚未收到他们的答复。)

要了解更多完整的信息,请参阅 go.microsoft.com/fwlink/?LinkId=116953 上的 BCD WMI 提供程序文档。与此同时,我们将向您展示一些设计用于多引导计算机(即安装了多个操作系统的计算机)的示例代码。

对了,这些脚本仅适用于 Windows Vista 和 Windows Server 2008。这是因为目前只有这两个操作系统支持 BCD,我们之前已提到这一点。

首先来看一个可以告诉我们计算机上当前正在使用哪个 OS 的脚本。当然,它并非 BCD 提供程序最令人激动的用途;毕竟,我们已可以使用 WMI 类 Win32_OperatingSystem 来确定计算机上当前正在使用哪个 OS。但是,它是一个相当简单的脚本(就 BCD 脚本而言至少是这样),并且提供了一种不错的方法来说明使用 BCD 时所需要的基本技术(至少可以说是有些不同寻常的技术)。

此外,我们不能在专栏中包含过多令人兴奋的内容:**《TechNet 杂志》已告诉我们,如过份激动和热情的“您好,脚本专家!”粉丝有不当行为,罪名会落到我们头上。考虑到我们的专栏定期出版后会在全球引发故意伤害罪,我们不能再次为兴奋买单了。

注释 记不记得甲壳虫乐队被粉丝骚扰的那些视频以及女孩子们看到 John、Paul、George 和 Ringo 时昏厥和尖叫的那些照片?这与我们每次出版新专栏的情形何其相似。

好吧,打住,这么说是有些夸张了。但毫无疑问,许多人(无论男女)在阅读了我们的某期专栏后会情绪激动。

无论如何,来看一看图 1 中的脚本,它使用 BCD 提供程序来判断计算机当前正在使用那个 OS。在尝试运行该脚本之前,您应该知道必须以管理员身份运行,否则该脚本会失败。

图 1 获取当前的操作系统

Const BcdLibraryString_Description = &h12000004
Const Current = "{fa926493-6f1c-4193-a414-58f0b2456d1e}"

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore
objStore.OpenObject Current, objDefault

objDefault.GetElement BcdLibraryString_Description, objElement
Wscript.Echo "Current operating system: " & objElement.String

这当然并不表示只需以管理员权限登录某个帐户即可。您需要通过右键单击“开始”菜单中的“命令提示符”并以管理员身份选择“运行”,来打开一个命令提示符窗口。然后,可通过命令提示符运行该脚本。

那么,此脚本的工作原理是什么?您知道,我们很担心您会继续问下去。不过没关系,我们会尽我们所能地进行解释(即,至少保证一小段时间内不扩大兴奋)。

首先,我们定义两个常量:BcdLibraryString_Description 和 Current。BcdLibraryString_Description 指我们要检索的对象;在本例中,该对象包含当前操作系统的“说明”(名称)。

顺便提一下,它是使用 BCD 提供程序时的一个既可爱又奇怪的地方。通常,无法仅检索某个属性的值;而是使用其中一个常量(以及 GetElement 方法)来检索另一个对象,然后回显属于该对象的某个属性的值。当然,这有点滑稽可笑,但您知道人们常说的那句话吗:过程与目标同等重要。

注释 这让您对他们的目标感到困惑,是吗?

至于常量 Current,它只是用于代表当前正在使用的操作系统的一个 GUID。值

 {fa926493-6f1c-4193-a414-58f0b-2456d1e} 

被视为是一个“众所周知”的 GUID(它又一次证明 Microsoft 人实际非常具有幽默感)。

虽然这个 GUID 众所周知,我们还是执意把它包括进来,以防诸位中有人记不得以下两个值中哪一个是正确的:

{fa926493-6f1c-4193-a414-58f0b2456d1e} 
{fa926493-6f1c-4193-a414-58f0b2456d1f}

注释 如果您想知道 GUID 应发音成“gwid”还是“goo-id”,我们可以告诉您更多内情,比如前述的 GUID 必须为 V1 GUID,就像第三组数字如何以 4 开头一样。但我们已保证过将兴奋感保持在最低程度。我们将信守承诺。

正如您可能已猜到的,在使用 BCD 提供程序时,随时都需要使用常量、十六进制值和 GUID。我们无法在本月专栏中介绍到所有内容,但您可以通过访问先前提到的 MSDN® 上的 BCD 页面 (go.microsoft.com/fwlink/?LinkId=116953) 来了解更多信息。前提是您能经受得住所有兴奋带来的震撼。

定义完两个常量后,接下来连接本地计算机上的 WMI 服务。能使用此脚本来检索远程计算机的引导配置数据吗?当然可以。

坦白地讲,如果这都做不到的话,这些 BCD 也太没价值了。为检索远程计算机(再次强调一下,远程计算机必须运行 Windows Vista 或 Windows Server 2008)的引导配置数据,只需将该计算机的名称指定给变量 strComputer 即可:

strComputer = "atl-fs-001"

还应指出 WMI 连接字符串中的一对关键字项。首先,您可能已注意到连接字符串中同时包含了“备份”和“恢复”权限,这也是 {(Backup,Restore)} 结构的作用。这重要吗?如果希望脚本有效,那它就非常重要:如果未明确包括这两个权限,脚本将失败。

其次,请注意我们并未连接到系统管理脚本中最常用的 root\cimv2 命名空间。我们是连接到 root\WMI 命名空间并直接绑定到 BCDStore 类。下面这些代码就用来解决这个问题:

"\root\wmi:BcdStore" 

它还不是令人兴奋的部分。好戏还是后面呢。

除了少数例外,对于大多数人来说,编写的所有 BCD 脚本都包括相同的三个步骤:定义常量、连接到 WMI 服务然后打开 BCD 存储。我们已完成步骤 1 和步骤 2,下列代码行将执行步骤 3:

objStoreClass.OpenStore "", objStore

正如我们所说到的,我们将打开 BCD 存储(即存储所有引导配置信息的操作系统实体)。要打开该存储,只需调用 OpenStore 方法并向该方法传递两个参数:

  • 空字符串 ("")。这是告诉脚本我们想要打开默认存储。
  • objStore。它位于我们向该脚本提供的一个“输出”参数中。我们向该方法提供变量的名称,作为回报,该方法会返回一个对象,它将该变量名用作对象引用(在本例中,即代表 BCD 存储的对象)。

一旦打开存储,就可使用 OpenObject 方法来检索另一对象(存储在输出参数 objDefault 中):

objStore.OpenObject Current, objDefault

这个新对象是什么?没错:它就是当前正在使用的操作系统。这是因为我们向 OpenObject 传递了常量 Current(即代表当前操作系统的众所周知的 GUID)。

好了,现在我们知道计算机上当前正在使用哪个操作系统了,对吧?差不多是这样。要获取该信息,我们还需要使用 GetElement 来检索代表该操作系统的“说明”的一个对象:

objDefault.GetElement _
  BcdLibraryString_Description, objElement

现在,您能保证自己不会过于兴奋,也不会犯下故意伤害罪吗?很好。如果是那样的话,可以告诉您:现在可回显 String 属性的值,并最终确定计算机当前正在使用的操作系统:

Wscript.Echo "Current operating system: " _
  & objElement.String

请保持冷静。要记得您的承诺:不要犯故意伤害罪。我们知道这确实很难。但请尽力而为。试着深呼吸,在我们编写 BCD 脚本时这一招始终非常管用。

正如我们所讲到的,仅仅是确定计算机正在使用哪个操作系统就充满了这么多艰难险阻;其实上有更轻松的方法可完成该项任务。但从积极的一面来看,您现在已了解 BCD 脚本的工作原理,即意味着您可以做一些脚本编写者还没做过的事情。(冷静,再冷静。)例如,在多引导计算机上,始终有一个操作系统已标记为“默认”;如果计算机重新启动而周围没有人告诉它其他指示,则计算机将自动加载默认操作系统。在 Windows Vista(以及 BCD 提供程序)之前,脚本编写者是无法确定计算机上的默认操作系统的。但现在,只需运行一个像图 2 中那样的脚本就可以轻松做到这一点了。

图 2 确定默认操作系统

Const BcdLibraryString_Description = &h12000004
Const BootMgrId = "{9dea862c-5cdd-4e70-acc1-f32b344d4795}"
Const DefaultType = &h23000003

strComputer = "." 

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
    strComputer & "\root\wmi:BcdStore")
objStoreClass.OpenStore "", objStore

objStore.OpenObject BootMgrId, objBootMgr 

objBootMgr.GetElement DefaultType, objDefaultOSIdentifier
objStore.OpenObject objDefaultOSIdentifier.Id, objDefault

objDefault.GetElement BcdLibraryString_Description, objElement 
WScript.Echo "Default operating system: " & objElement.String

你们说对了:它确实会让过度兴奋,不是吗?但必竟很少,是吧?我们将不再详细解释该脚本;假如您并未错过前面的 10 或 12 个段落的话,您应当能够自行遵循该逻辑。但您可能注意到了,为了获取默认操作系统,我们不得不使用一个中间步骤:使用 OpenObject 方法打开一个“引导管理器”对象的实例。打开“引导管理器”后,可使用常量 DefaultType 来检索代表默认操作系统的一个对象。

不可否认,现在已相当地激动人心了:可确定当前的操作系统,同时可确定默认的操作系统。但您知道什么才是真正的酷吗?那就是检索在计算机上安装的所有操作系统的列表。那才是真正的激动人心!先稳定住自己的情绪,然后看一看图 3

我们做了些什么?首先定义了两个新常量:WindowsImages,可用于检索支持 BCD 的所有 OS 实例(即 Windows Vista 和 Windows Server 2008);LegacyImages,可用于检索计算机上的所有“传统”操作系统的实例。连接到 BCD 存储后,使用 EnumerateObjects 方法来检索计算机上安装的所有支持 BCD 的操作系统的实例:

objStore.EnumerateObjects _
  WindowsImages, colObjects 

图 3 查找计算机上的所有操作系统

Const BcdLibraryString_Description = &h12000004
Const WindowsImages = &h10200003
Const LegacyImages = &h10300006

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore 

objStore.EnumerateObjects WindowsImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next
Wscript.Echo

objStore.EnumerateObjects LegacyImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next

EnumerateObjects 完成任务后,建立一个 For Each 循环来遍历该集合中的所有操作系统。在该循环中,使用以下两行代码来检索并显示操作系统“说明”:

objObject.GetElement _
  BcdLibraryString_Description, objElement 
Wscript.Echo objElement.String

然后针对计算机上安装的所有传统操作系统重复该过程。

objStore.EnumerateObjects _
  LegacyImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement _
  BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next

注释 我们能理解其中的陶醉感觉,但请小心谨慎地运行 BCD 脚本;很可能人体所能承受的肾上腺素刺激数量是有限度的。未经医生的事先检查,孕妇、可能成为孕妇、永远不会成为孕妇或曾经是孕妇的女性,以及纯正的男性公民均不应运行 BCD 脚本。

好吧,您无需事先经过医生的检查。但是,如果向医生询问他对于运行 BCD 脚本的看法,听听他的回答可能会相当有趣,不是吗?

现在,让我们来尝试点真正疯狂的东西吧 — 看看是否可以更改默认操作系统。例如,假设我们有一台同时运行 Windows Vista 和 Windows Server 2008 的双引导计算机。再假设我们希望将 Windows Vista 设为默认操作系统。我们将如何做呢?好的,看一看图 4。它展示了完成此项任务的一种方法。

图 4 更改默认操作系统

Const BootMgrId = "{9dea862c-5cdd-4e70-accl-f32b344d4795}"
Const BcdLibraryString_Description = &h12000004
Const DefaultType = &h23000003
Const WindowsImages = &h10200003

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore 
objStore.EnumerateObjects WindowsImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 If Instr(objElement.String, "Vista") Then
  objStore.OpenObject BootMgrId, objBootMgr 
  objBootMgr.SetObjectElement DefaultType, objObject.ID 
 End If
Next

通过此脚本,再次打开 BCD 存储,然后使用 EnumerateObjects 来检索计算机上安装的所有支持 BCD 的操作系统的集合。在此我们设置一个 For Each 循环来遍历集合中的所有项,使用下行代码(到现在应该很熟悉了吧)检索每个操作系统的“说明”:

objObject.GetElement _
  BcdLibraryString_Description, objElement

获取给定操作系统的“说明”后,使用 InStr 函数来查看该值中是否包含词语 Vista:

If Instr(objElement.String, "Vista") Then

您说得对,这样看上去有点笨,对吧?比较酷的方法是使用 Windows Vista 的 GUID,然后直接打开该操作系统,这样就不必枚举并遍历计算机上的所有操作系统。

遗憾的是,我们必须知道 Windows Vista 的 GUID 才能使用这种很酷的方法。使用我们的方法不必知道任何事情(这对于脚本专家们倒是好事);我们需要做的就是不断地搜索直至找到标题中包含词语 Vista 的操作系统。

注释 如果计算机上安装了多个 Windows Vista 实例呢?这时您可能需要查找一下 Vista Ultimate 或 Vista Enterprise 之类的字符串。

一旦找到 Windows Vista,就使用以下两行代码来打开 Windows 引导管理器,然后将默认操作系统设为 Windows Vista:

objStore.OpenObject BootMgrId, objBootMgr 
objBootMgr.SetObjectElement _
  DefaultType, objObject.ID 

我们这个月的内容就到这里了;毕竟,即使是脚本专家,我们也无法应付随时随地无休无止的激动。但不用担心;下个月我们还会带来另一期激动人心的“您好,脚本专家!”。希望您届时已恢复过来。

Scripto 博士的脚本谜题

每月挑战不仅测试您解决谜题的能力,还测试您的脚本编写技能。

2008 年 7 月:VBScript 方块

它们可能是矩形而非方块,但那是题外话了。要解出这个谜题,需将右侧的每个方块放入左侧的空方块中,从而构成 VBScript 函数的名称。每个方块只能使用一次。下面是一个示例:

要正确地解出这个示例,需要将 OU 方块(矩形)移到第一个词语的空方块中,然后将 MS 和 OX 移动到第二个词语的空方块中。这样就构成了 VBScript 函数 UBound 和 MsgBox,如下所示:

现在您来试一试:

答案:

Scripto 博士的脚本谜题

答案:VBScript 方块,2008 年 7 月

脚本专家为 Microsoft 工作,也就是受雇于 Microsoft。在不玩、不教或不看棒球(以及各种其他活动)的时候,他们负责管理 TechNet 脚本中心。要查看相关信息,请访问 www.scriptingguys.com

© 2008 Microsoft Corporation 和 CMP Media, LLC。保留所有权利;未经允许不得复制本文的部分或全部内容。