用户帐户控制兼容性的 Windows Vista 应用程序开发要求

 

詹妮弗·艾伦

更新时间:2007 年 6 月

总结: 本白皮书旨在帮助应用程序开发人员设计与用户帐户控制兼容的支持 Windows Vista 的应用程序。 其中包括有关设计过程的详细步骤,以及代码示例、要求和最佳做法。 本文还详细介绍了 Windows Vista 中用户体验的技术更新和更改。 (71 页。)

目录

为何要控制用户帐户?
Windows Vista 汇报
适用于 Windows Vista 的新技术
UAC 体系结构
UAC 会影响应用程序吗?
设计适用于 Windows Vista 的应用程序
为标准用户部署和修补应用程序
常见问题故障排除
参考

为何要控制用户帐户?

应用程序开发人员一直创建需要过多用户权限和 Windows 特权的 Windows 应用程序,通常要求执行用户是管理员。 因此,很少有 Windows 用户以最少的用户权限和所需的 Windows 特权运行。 由于标准用户应用程序兼容性问题,许多企业寻求在部署的简易性和易用性与安全性的平衡点时,往往不得不以管理员身份部署桌面。

以下列表详细说明了在 Microsoft Windows Vista 之前的计算机上难以以标准用户身份运行的其他原因:

  1. 许多 Windows 应用程序要求登录用户是管理员,但实际上不需要管理员级访问权限。 这些应用程序在被允许运行之前执行各种管理员访问检查,包括:
    • 管理员访问令牌检查。
    • 系统保护位置中的“所有访问”访问请求。
    • 将数据写入受保护的位置,例如 %ProgramFiles%、%WinDir% 和 HKLM\Software。
  2. 许多 Windows 应用程序在设计时没有最低特权的概念,并且不会将用户和管理员功能分为两个单独的进程。
  3. 默认情况下,Windows 2000 和 Windows XP 以管理员身份创建每个新用户帐户;因此,关键 Windows 组件(如日期和时间以及电源管理控制面板)不适用于标准用户
  4. Windows 2000 和 Windows XP 管理员必须创建两个单独的用户帐户,一个用于管理任务,另一个用于执行日常任务的标准用户帐户。 因此,用户必须注销其标准用户帐户,以管理员身份重新登录或使用运行方式来执行任何管理任务。

通过用户帐户控制 (UAC) ,Microsoft 提供了一种技术来简化在企业和家庭部署标准用户桌面。

UAC 团队基于最初在 Microsoft Windows NT 3.1 操作系统中设计的 Windows 安全体系结构构建,旨在实现既灵活又更安全的标准用户模型。 在以前版本的 Windows 中,在登录过程中为管理员创建一个访问令牌。 管理员的访问令牌包括大多数 Windows 特权和大多数管理安全标识符 (SID) 。 此访问令牌可确保管理员可以安装应用程序、配置操作系统以及访问任何资源。

在 Windows Vista 中,UAC 团队对访问令牌创建过程采取了截然不同的方法。 当管理员用户登录到 Windows Vista 计算机时,将创建两个访问令牌:经过筛选的标准用户访问令牌和完全管理员访问令牌。 使用标准用户访问令牌,而不是使用管理员的访问令牌启动桌面 (Explorer.exe) 。 所有子进程都继承自桌面的初始启动 (explorer.exe进程) ,这有助于限制 Windows Vistas 攻击面。 默认情况下,所有用户(包括管理员)都以标准用户身份登录到 Windows Vista 计算机。

注意 上述语句有一个例外:与标准用户相比,来宾使用更少的用户权限和特权登录到计算机。

当管理员尝试执行管理任务(例如安装应用程序)时,UAC 会提示用户批准该操作。 当用户批准操作时,使用管理员的完全管理员访问令牌启动任务。 这是默认的管理员提示行为,可在本地安全策略管理器管理单元 (secpol.msc) 和 组策略 (gpedit.msc) 中配置。

注意启用 UAC 的 Windows Vista 计算机上的管理员帐户在管理员审批模式下也称为管理员帐户。 管理员审批模式标识管理员的默认用户体验。

每个管理提升也是特定于流程的,这会阻止其他进程在不提示用户批准的情况下使用访问令牌。 因此,管理员用户可以更精细地控制应用程序安装的内容,同时极大地影响预计登录用户使用完全管理员访问令牌运行的恶意软件。

标准用户还可以使用 UAC 基础结构在流中提升并执行管理任务。 当标准用户尝试执行管理任务时,UAC 会提示用户输入管理员帐户的有效凭据。 这是默认的标准用户提示行为,可在本地安全策略管理器管理单元 (secpol.msc) 和 组策略 (gpedit.msc) 中配置。

Windows Vista 汇报

以下更新反映了 Windows Vista 中发生的功能累积核心更改。

UAC 默认处于启用状态

因此,你可能会遇到一些尚未针对 Windows Vista UAC 组件更新的不同应用程序的兼容性问题。 如果应用程序需要管理员访问令牌 (这是从尝试运行应用程序) 时返回的“拒绝访问”错误引起的,则可以使用上下文菜单上的“以管理员身份运行”选项 (右键单击) ,以管理员身份运行程序。 本文档稍后在以管理员身份运行程序部分中介绍了如何执行此操作。

所有后续用户帐户均创建为标准用户

标准用户帐户和管理员用户帐户都可以利用 UAC 增强的安全性。 在新的安装中,默认情况下,创建的第一个用户帐户是处于管理员审批模式的本地管理员帐户, (启用 UAC) 。 然后,所有后续帐户将创建为标准用户。

默认情况下,提升提示显示在安全桌面上

默认情况下,同意和凭据提示在 Windows Vista 的安全桌面上显示。

后台应用程序的提升提示已最小化到任务栏

后台应用程序会自动提示用户在任务栏上进行提升,而不是自动转到安全桌面进行提升。 提升提示将在任务栏上最小化,并闪烁以通知用户应用程序已请求提升。 当用户浏览到网站并开始下载安装文件时,会出现后台提升的示例。 然后,在后台下载安装时,用户转到检查电子邮件。 在后台完成下载并开始安装后,会将提升检测为后台任务而不是前台任务。 此检测可防止安装在用户执行另一项任务(阅读电子邮件)时突然窃取用户屏幕的焦点。 此行为为提升提示创建更好的用户体验。 有关应用程序开发人员在请求提升时如何确保其应用程序不会最小化到任务栏的信息,请参阅本文档的后面部分。

在用户的登录路径中阻止提升

用户登录时启动并需要提升的应用程序现在在登录路径中被阻止。 如果不阻止应用程序在用户的登录路径中提示提升权限,标准用户和管理员都将不得不在每次登录时响应“用户帐户控制”对话框。 Windows Vista 通过在系统托盘中放置图标来通知用户应用程序是否已被阻止。 然后,用户可以右键单击此图标以运行在用户登录时阻止提示提升的应用程序。 用户可以通过双击托盘图标来管理禁用或从此列表中删除的启动应用程序。

在“新建安装”上默认禁用内置管理员帐户

Windows Vista 中默认禁用内置管理员帐户。 如果 Windows Vista 在从 Windows XP 升级期间确定内置管理员是唯一有效的本地管理员帐户,则 Windows Vista 将保持帐户启用状态,并将帐户置于管理员审批模式。 默认情况下,内置管理员帐户无法在安全模式下登录到计算机。 有关详细信息,请参阅以下部分。 内置管理员帐户是在安装过程中使用用户名 Administrator 创建的。

未加入域

如果至少有一个已启用的本地管理员帐户,安全模式将不允许使用禁用的内置管理员帐户登录。 相反,任何本地管理员帐户都可用于登录。 如果最后一个本地管理员帐户被无意中降级、禁用或删除,则安全模式将允许禁用的内置管理员帐户登录以实现灾难恢复。

已加入域

在所有情况下,禁用的内置管理员帐户都无法在安全模式下登录。 作为 域管理员 组成员的用户帐户可以登录到计算机以创建本地管理员(如果不存在)。

注意 如果域管理帐户以前从未登录过,则必须在具有网络的安全模式下启动计算机,因为不会缓存凭据。

注意计算机脱离后,它将还原回到前面描述的非加入域的行为。

用户帐户控制和远程方案

例如,当管理员通过远程桌面远程登录到 Windows Vista 计算机时,用户默认以标准用户身份登录到计算机。 远程管理已修改为限制网络。 如果用户在具有管理潜力的情况下运行,这有助于防止恶意软件执行应用程序“环回”。

本地用户帐户

在 Windows Vista 计算机的本地安全帐户管理器中具有管理员帐户的用户 (SAM) 数据库远程连接到 Windows Vista 计算机时,该用户在远程计算机上没有提升潜力,并且无法执行管理任务。 如果用户想要使用 SAM 帐户管理工作站,用户必须以交互方式登录到要管理的计算机。

域用户帐户

当具有域用户帐户的用户远程登录到该用户是管理员组成员的 Windows Vista 计算机时,域用户将在远程计算机上使用完全管理员访问令牌运行,UAC 将不会生效。

(ACL) 设置新建默认访问控制列表

某些 Windows 目录上的 ACL 已更改,以便在数据目录和用户受保护的目录之外启用数据共享和协作。 用户的受保护目录是其用户配置文件 (例如 C:\Users\Denise\Pictures\) ,而数据目录的示例是数据驱动器上操作系统分区外部的位置 (E.G. D:\Pictures\) 。 由于根目录 C 在此实例中受更严格的 ACL 的保护,因此用户无法在早期版本的 Windows Vista 中使用数据目录。

这些 ACL 更改可确保用户可以共享和编辑文件,而无需向“用户帐户控制”对话框提供审批。 此外,用户现在可以将文件夹设为私有。 此更改可确保用户仍可以轻松维护数据驱动器上的数据机密性和完整性。 如果其他管理员提升这些专用文件夹,这些专用文件夹仍可供其读取,并且应该用于对标准用户保留数据的私密性。

以下是 Windows XP 中 %systemroot% 和数据驱动器上的默认 ACL 设置。

Windows XP %systemroot% 和数据驱动器 ACL 设置

用户或组 访问控制项
BUILTIN\Administrators 完全控制
NT AUTHORITY\SYSTEM 完全控制
CREATOR OWNER 完全控制
BUILTIN\Users 读取

特殊访问:FILE_APPEND_DATA

特殊访问:FILE_WRITE_DATA

所有人 读取

下表详细介绍了使用 format.exe 创建的数据驱动器的新 Windows Vista 数据驱动器 ACL 设置。

Windows Vista 数据驱动器 ACL 设置

用户或组 访问控制项
BUILTIN\Administrators 完全控制
NT AUTHORITY\SYSTEM 完全控制
NT AUTHORITY\Authenticated Users 修改
BUILTIN\Users 读取和执行

泛型读取、泛型执行

下表详细介绍了新的 Windows Vista 操作系统根 (%systemroot%) ACL 设置。

Windows Vista %systemroot% ACL 设置

用户或组 访问控制项
BUILTIN\Administrators 完全控制
NT AUTHORITY\SYSTEM 完全控制
BUILTIN\Users 读取和执行
NT AUTHORITY\Authenticated Users 修改

追加数据

必需标签\高强制级别 无写入

新的 UAC 安全设置和安全设置名称更改

本文档的参考部分详细介绍了新的安全设置和安全设置名称更新。

适用于 Windows Vista 的新技术

以下部分详细介绍了适用于 Windows Vista 的新技术,包括安装程序检测、Windows Installer 4.0 的标准用户修补、用户界面特权隔离和虚拟化。

ActiveX Installer Service

ActiveX 安装程序服务使企业能够委托标准用户的 ActiveX 控制安装。 此服务可确保常规业务任务不会受到失败的 ActiveX 控制安装和更新的阻碍。 Windows Vista 还包括组策略设置,使 IT 专业人员能够定义标准用户可以从中安装 ActiveX 控件的主机 URL。 ActiveX 安装程序服务由 Windows 服务、组策略管理模板和 Internet Explorer 中的一些更改组成,是一个可选组件,仅在安装它的客户端上启用。

安装程序检测

安装程序是设计用于部署软件的应用程序,大多数都写入系统目录和注册表项。 这些受保护的系统位置通常只能由管理员用户编写;这意味着标准用户没有足够的访问权限来安装程序。 Windows Vista 以启发式方式检测安装程序,并请求管理员用户提供管理员凭据或批准,以便使用访问权限运行。 Windows Vista 还会启发式检测更新程序和未安装程序。 请注意,UAC 的设计目标是防止在用户不知情和同意的情况下执行安装,因为它们写入文件系统和注册表的受保护区域。

重要 开发新的安装程序时,就像为 Windows Vista 开发程序一样,请务必使用适当的 requestedExecutionLevel 元素嵌入应用程序清单, (请参阅步骤六:使用应用程序创建应用程序清单并嵌入应用程序清单部分) 。 当嵌入式应用程序清单中存在 requestedExecutionLevel 时,它将替代安装程序检测。

安装程序检测仅适用于:

  • 32 位可执行文件
  • 没有 requestedExecutionLevel 的应用程序
  • 以启用了 LUA 的标准用户身份运行的交互式进程

在创建 32 位进程之前,将检查以下属性以确定它是否为安装程序:

  • 文件名包括“install”、“setup”、“update”等关键字。
  • 以下版本控制资源字段中的关键字:供应商、公司名称、产品名称、文件说明、原始文件名、内部名称和导出名称。
  • 并排清单中的关键字嵌入到可执行文件中。
  • 可执行文件中链接的特定 StringTable 条目中的关键字。
  • 在可执行文件中链接的 RC 数据中的关键属性。
  • 可执行文件中字节的目标序列。

注意 关键字和字节序列派生自从各种安装程序技术观察到的常见特征。

确保全面查看本文档的全部内容,包括步骤六:使用应用程序创建和嵌入应用程序清单部分。

注意 必须启用 “用户帐户控制:检测应用程序安装并提示提升 ”设置,以便安装程序检测以检测安装程序。 此设置默认启用,可以使用安全策略管理器管理单元 (secpol.msc) 或 组策略 (gpedit.msc) 进行配置。

可以在 MSDN 库中找到 Microsoft Windows 安装程序 的常规信息和概述。

在 UAC 环境中修补应用程序

Microsoft Windows Installer 4.0 在设计时考虑到了 UAC,以便更轻松地安装和修补应用程序。 随着 Windows Installer 4.0 的推出,修补程序可以应用于应用程序,而无需重新安装较新版本的应用程序。 当应用程序部署在每台计算机安装中并且需要由用户部署修补程序而无需管理访问令牌时,此方法非常理想。 有关如何创建修补程序并将其应用于应用程序的信息,请参阅 修补Per-User托管应用程序

安全中心集成

在 Windows Vista 计算机上禁用 UAC 时,安全中心会创建警报并提示用户重新启用 UAC。 更改 UAC 设置后,计算机重启后,安全中心会显示此警报。

用户界面特权隔离

用户界面特权隔离 (UIPI) 是一种机制,可帮助将以完全管理员身份运行的应用程序与以低于同一交互式桌面管理员的帐户运行的进程隔离开来。 UIPI 特定于支持窗口和用户界面控件的窗口化和图形子系统(称为 USER)。 UIPI 阻止特权较低的应用程序使用 Windows 消息将输入从一个进程发送到更高特权的进程。 将输入从一个进程发送到另一个进程允许进程将输入注入到另一个进程,而无需用户提供键盘或鼠标操作。

UIPI 背后的概念很简单。 Windows Vista 以分层方式定义一组用户界面特权级别。 级别的性质是,较高的特权级别可以将窗口消息发送到在较低级别运行的应用程序。 但是,较低级别无法将窗口消息发送到较高级别的应用程序窗口。

用户界面特权级别为进程级别。 初始化进程时,用户子系统会调用安全子系统,以确定进程的安全访问令牌中分配的桌面完整性级别。 桌面完整性级别在创建进程时由安全子系统设置,且不会更改。 因此,用户界面特权级别也是在创建进程时由用户子系统设置的,并且不会更改。

标准用户运行的所有应用程序具有相同的用户界面特权级别。 作为标准用户,应用程序以单个特权级别运行。 UIPI 不会干扰或更改同一特权级别的应用程序之间的窗口消息传送行为。 UIPI 对属于管理员组成员且可能以标准用户身份运行应用程序的用户生效, (有时称为具有筛选访问令牌的进程) ,并且还在同一桌面上使用完全管理员访问令牌运行的进程。 UIPI 通过阻止以下行为来阻止特权较低的进程访问较高特权的进程。

特权较低的进程无法:

  • 对更高进程特权执行窗口句柄验证。
  • SendMessage 或 PostMessage 到更高特权的应用程序窗口。 这些应用程序编程接口 (API) 返回成功,但以无提示方式删除窗口消息。
  • 使用线程挂钩附加到更高权限的进程。
  • 使用日记挂钩监视更高权限的进程。
  • 将动态链接库 (DLL) 注入到更高的特权进程。

启用 UIPI 后,仍会在不同特权级别的进程之间共享以下共享 USER 资源:

  • 桌面窗口,它实际上拥有屏幕表面
  • 桌面堆只读共享内存
  • 全局原子表
  • 剪贴板

在屏幕上绘制是 UIPI 未阻止的另一个操作。 绘制到屏幕是指使用画图方法在外部输出(例如监视器)上显示内容的过程。 (GDI) 模型的 USER/图形设备接口不允许控制绘制图面;因此,特权较低的应用程序可能会绘制在较高特权应用程序窗口的表面区域。

注意 由于 Windows Shell (资源管理器) 作为标准用户进程运行,因此以标准用户身份运行的任何其他进程仍可向其发送击键。 这是处于管理员审批模式的管理员帐户在启动管理操作(例如双击Setup.exe或单击提升防护图标)时提示提升同意的主要原因。

虚拟化

重要 实现虚拟化是为了改善在 Windows Vista 上以标准用户身份运行的应用程序的应用程序兼容性问题。 开发人员不得依赖于后续版本的 Windows 中存在的虚拟化。

在 Windows Vista 之前,许多应用程序通常由管理员运行。 因此,应用程序可以自由读取和写入系统文件和注册表项。 如果这些应用程序由标准用户运行,则它们将因访问权限不足而失败。 Windows Vista 通过将写入 (以及) 的后续文件或注册表操作重定向到用户配置文件中的每个用户位置,提高了这些用户的应用程序兼容性。 例如,如果应用程序尝试写入 C:\Program Files\Contoso\Settings.ini,而用户没有写入该目录的权限,则写入将重定向到 C:\Users\<user name>\AppData\Local\VirtualStore\Program Files\contoso\settings.ini。 对于注册表,如果应用程序尝试写入HKEY_LOCAL_MACHINE\Software\Contoso\它将自动重定向到 HKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE\Software\Contoso 或 HKEY_USERS\< User SID >_Classes\VirtualStore\Machine\Software\Contoso。

下图详细介绍了 Windows Vista 中的虚拟化过程。 在此示例中,Denise 是管理员审批模式下的管理员,Brian 是标准用户。 虚拟化由两个组件组成:文件虚拟化和注册表虚拟化。

Bb530410.vistauacreqs01 (en-us,MSDN.10) .gif

图 1. 虚拟化过程

重要 开发 Windows Vista 程序时,为了降低虚拟化文件和注册表项的复杂性,请务必使用适当的 requestedExecutionLevel 嵌入应用程序清单,以便关闭文件和注册表虚拟化。

虚拟化仅针对:

  • 32 位交互式进程
  • 管理员可写文件/文件夹和注册表项

虚拟化的禁用适用于:

  • 64 位进程
  • 非交互式进程
  • 模拟的进程
  • 内核模式调用方
  • 具有 requestedExecutionLevel 的可执行文件

虚拟化和漫游:

  • 虚拟化文件/文件夹和注册表项不会漫游 (查看漫游配置文件)
  • 与不漫游的全局对象关联

文件虚拟化

文件虚拟化解决了应用程序依赖于将文件(如配置文件)存储在通常只能由管理员写入的系统位置中的能力的情况。 在这种情况下,以标准用户身份运行程序可能会导致由于访问级别不足而导致程序失败。

当应用程序写入只有管理员可写的系统位置时,Windows 会将所有后续文件操作写入虚拟存储目录下的用户特定路径,该路径位于 %LOCALAPPDATA%\VirtualStore。 稍后,当应用程序读回此文件时,系统会在虚拟存储中提供该文件。 由于 Windows 安全基础结构在没有应用程序协助的情况下处理虚拟化,因此应用程序相信它能够直接成功读取和写入程序文件。 文件虚拟化的透明度使应用程序能够感知它们正在从受保护的资源写入和读取,而实际上它们正在访问虚拟化版本。

注意 枚举文件夹和注册表中的资源时,Windows Vista 会将全局文件/文件夹和注册表项合并到单个列表中。 在此合并视图中,全局 (受保护) 资源与虚拟化资源一起列出。

重要 虚拟副本将始终首先呈现给应用程序。 例如,config.ini在 \PF\App\config.ini 和 %LOCALAPPDATA%\VirtualStore\config.ini 中可用,即使更新了\PF\App\config.ini,虚拟存储中的config.ini也始终是读取的。

下图详细说明了如何为不同用户显示虚拟化资源的全局视图和合并视图。

Bb530410.vistauacreqs02 (en-us,MSDN.10) .gif

图 2. 虚拟化资源和视图

下面是文件虚拟化过程的示例:

Woodgrove Bank 的销售代表 Syed Abbas 使用他与其他销售代表共享的计算机上的标准用户帐户运行。 Syed 经常使用电子表格应用程序更新和保存 Program Files\SalesV1\ 目录下的文件: \Program Files\SalesV1\SalesData.txt。 尽管 Program Files\SalesV1\ 受到保护,但由于 Windows Vista 文件虚拟化,文件将从电子表格应用程序的视点成功保存。 为此,将文件写入重定向到 Users\username\appdata\Virtual Store\Program Files\SalesV1\SalesData.txt。 当 Syed 打开 Windows 资源管理器并浏览到 Program Files 目录时,他会看到SalesData.txt文件的全局视图。

注意 要使 Syed 发现其虚拟化文件,他必须使用资源管理器工具栏上的 “兼容性文件 ”按钮导航到虚拟存储。

但是,在另一位销售代表 Stuart Munson 登录到工作站后,他不会在 Program Files\SalesV1\ 目录中看到SalesData.txt文件。 如果其他用户使用计算机并写入 \Program files\SalesV1\SalesData.txt 文件,该写入也将虚拟化到该用户的虚拟存储。 Syed 更新和保存的文件将保持独立于系统上的其他虚拟化文件。

注册表虚拟化

注册表虚拟化类似于文件虚拟化,但适用于HKEY_LOCAL_MACHINE\SOFTWARE下的注册表项。 此功能允许依赖于将配置信息存储在 HKEY_LOCAL_MACHINE\SOFTWARE 中的应用程序在标准用户帐户下运行时继续运行。 密钥和数据将重定向到HKEY_CLASSES_ROOT\VirtualStore\SOFTWARE。 与文件虚拟化情况一样,每个用户都有应用程序存储在 HKLM 中的任何值的虚拟化副本。

注册表虚拟化详细信息

  • 可以在软件配置单元中打开/关闭单个密钥
  • reg.exe中用于关键级别虚拟化控制的新 FLAGS 选项:允许递归启用/禁用虚拟化和控制“开放访问权限策略”
  • ZwQueryKey:以编程方式查询密钥的虚拟化标志。
  • 虚拟化发生在 WoW64 重定向之上
  • 在 64 位和 32 位注册表视图中都已启用:HKU\{SID}_Classes\VirtualStore\Machine\Software 和 HKU\{SID}_Classes\VirtualStore\Machine\Software\SYSWOW3264
  • 大多数旧版 32 位应用将使用 32 位视图

虚拟化仅用于帮助应用程序与现有程序兼容。 为 Windows Vista 设计的应用程序不应对敏感系统区域执行写入操作,也不应依赖虚拟化来纠正不正确的应用程序行为。 更新现有代码以在 Windows Vista 上运行时,开发人员应确保在运行时,应用程序仅将数据存储在每用户位置或计算机位置中 %alluserprofile% (CSIDL_COMMON_APPDATA) ,这些位置具有访问控制列表 (ACL) 设置正确设置。

重要 随着更多应用程序迁移到 Windows Vista,Microsoft 打算从 Windows 操作系统的未来版本中删除虚拟化。 例如,在 64 位应用程序上禁用虚拟化。

虚拟化建议

虚拟化仅用于帮助应用程序与现有程序兼容。 为 Windows Vista 设计的应用程序不应对敏感系统区域执行写入操作,也不应依赖虚拟化来纠正不正确的应用程序行为。 更新现有代码以在 Windows Vista 上运行时,开发人员应确保在运行时,应用程序仅将数据存储在每用户位置或 %alluserprofile% 内具有访问控制列表 (ACL) 设置的计算机位置。

重要 随着更多应用程序迁移到 Windows Vista,Microsoft 打算从 Windows 操作系统的未来版本中删除虚拟化。 例如,在 64 位应用程序上禁用虚拟化。

  • 为交互式应用程序添加具有相应 requestedExecutionLevel 的应用程序清单。 这将关闭清单应用程序的虚拟化。
  • 不要将注册表用作进程间通信机制。 服务和用户应用程序将具有不同的密钥视图。
  • 在 Windows Vista 上测试应用程序:确保以标准用户身份运行的进程不会写入 %systemroot% 等全局命名空间。
  • 对于筛选器驱动程序开发人员:检查高度范围。 请参阅文件系统筛选器。 这些值必须高于 FSFilter 虚拟化。
  • 请记住,虚拟化资源是全局资源的每个用户副本。

访问令牌更改

当用户登录到 Windows Vista 计算机时,Windows 会查看用户帐户拥有的管理 Windows 特权和相对 ID (RID) ,以确定用户是否应接收两个访问令牌 (筛选访问令牌和) 完全访问令牌。 如果以下任一情况成立,Windows 将为用户创建两个访问令牌:

  1. 用户的帐户包含以下任何 RID。
    • DOMAIN_GROUP_RID_ADMINS
    • DOMAIN_GROUP_RID_CONTROLLERS
    • DOMAIN_GROUP_RID_CERT_ADMINS
    • DOMAIN_GROUP_RID_SCHEMA_ADMINS
    • DOMAIN_GROUP_RID_ENTERPRISE_ADMINS
    • DOMAIN_GROUP_RID_POLICY_ADMINS
    • DOMAIN_ALIAS_RID_ADMINS
    • DOMAIN_ALIAS_RID_POWER_USERS
    • DOMAIN_ALIAS_RID_ACCOUNT_OPS
    • DOMAIN_ALIAS_RID_SYSTEM_OPS
    • DOMAIN_ALIAS_RID_PRINT_OPS
    • DOMAIN_ALIAS_RID_BACKUP_OPS
    • DOMAIN_ALIAS_RID_RAS_SERVERS
    • DOMAIN_ALIAS_RID_PREW2KCOMPACCESS
    • DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS
    • DOMAIN_ALIAS_RID_CRYPTO_OPERATORS
  2. 用户的帐户包含除标准用户帐户的权限以外的任何权限。 标准用户帐户仅包含以下权限。
    • SeChangeNotifyPrivilege
    • SeShutdownPrivilege
    • SeUndockPrivilege
    • SeIncreaseWorkingSetPrivilege
    • SeTimeZonePrivilege

筛选的令牌包含哪些特权取决于原始令牌是否包含上面列出的任何受限 RIDS。 如果令牌中包含任何受限制的 RID,则删除除以下项之外的所有特权:

  • SeChangeNotifyPrivilege
  • SeShutdownPrivilege
  • SeUndockPrivilege
  • SeReserveProcessorPrivilege
  • SeTimeZonePrivilege

如果令牌中没有受限的 RID,则仅删除以下权限:

  • SeCreateTokenPrivilege
  • SeTcbPrivilege
  • SeTakeOwnershipPrivilege
  • SeBackupPrivilege
  • SeRestorePrivilege
  • SeDebugPrivilege
  • SeImpersonatePrivilege
  • SeRelabelPrivilege

第一个访问令牌(称为筛选访问令牌)具有以前的 RID ((如果存在) 访问令牌中标记为USE_FOR_DENY_ONLY),并且删除了以前未列出的管理 Windows 特权。 当用户启动应用程序时,将默认使用筛选的访问令牌。 未修改的完全访问令牌(称为链接访问令牌)附加到筛选的访问令牌,并在发出请求以使用完全管理访问令牌启动应用程序时使用。

有关 RID 的详细信息,请参阅 MSDN 库文章 SID 字符串 [安全性]

有关 Windows 特权的详细信息,请参阅 MSDN 库文章 授权常量 [安全性]

UAC 体系结构

下图显示了 Windows Vista 中可执行文件启动的进程流。

Bb530410.vistauacreqs03 (en-us,MSDN.10) .gif

图 3. UAC 体系结构

下面介绍了 UAC 体系结构图中显示的进程流,以及可执行文件尝试启动时如何实现 UAC。

标准用户启动路径

Windows Vista 标准用户启动路径类似于 Windows XP 启动路径,但包含一些修改。

  • ShellExecute () 调用 CreateProcess () 。
  • CreateProcess () 调用 AppCompat、Fusion 和安装程序检测来评估应用程序是否需要提升。 然后检查可执行文件以确定其 requestedExecutionLevel,该请求存储在可执行文件的应用程序清单中。 AppCompat 数据库存储应用程序的应用程序兼容性修补程序条目的信息。 安装程序检测检测安装程序可执行文件。
  • CreateProcess () 返回 Win32 错误代码,指出ERROR_ELEVATION_REQUIRED。
  • ShellExecute () 专门查找此新错误,并在收到错误后,通过调用应用程序信息服务 (AIS) 尝试提升的启动。

提升的启动路径

Windows Vista 提升的启动路径是新的 Windows 启动路径。

  • AIS 接收来自 ShellExecute () 的调用,并重新评估请求的执行级别和组策略以确定是否允许提升并定义提升用户体验。
  • 如果请求的执行级别需要提升,服务将使用从 ShellExecute () 传入的 HWND,在调用方交互式桌面 (基于组策略) 启动提升提示。
  • 用户同意或提供有效凭据后,如有必要,AIS 将检索与相应用户关联的相应访问令牌。 例如,请求 highestAvailable 的 requestedExecutionLevel 的应用程序将为仅是备份操作员组成员的用户检索不同的访问令牌,而不是为本地 Administrators 组的成员检索不同的访问令牌。
  • AIS 重新发出 CreateProcessAsUser () 调用,提供管理员访问令牌并指定调用方的交互式桌面。

UAC 会影响应用程序吗?

应用程序是否受 UAC 影响取决于应用程序的当前状态。 在许多情况下,无需更改就符合 Microsoft Windows 安全要求。 但是,某些应用程序(包括业务线 (LOB) 应用程序)可能需要更改其安装、功能和更新过程,才能在 Windows Vista UAC 环境中正常工作。

注意 如果应用程序在 Windows XP 上作为标准用户运行良好,则它将在 Windows Vista 上作为标准用户运行良好。

为什么需要删除应用程序的管理依赖项?

提高整体计算环境安全性的一个基本步骤是允许用户在不使用其管理访问令牌的情况下运行。 如果应用程序仅在用户是管理员时才运行或安装,则强制用户以不必要的提升访问权限运行应用程序。 根本问题是,当用户始终被迫使用提升的访问令牌运行应用程序时,欺骗性或恶意代码很容易修改操作系统,或者更糟的是,会影响其他用户。

Microsoft 的目标是让客户了解应用程序不应不必要地以管理员身份运行,并在要求他们批准应用程序以管理员身份运行的请求时提出质疑。 UAC 是帮助实现这一目标的基本组件,对于为所有用户还原更安全的计算环境将大有裨意。

降低应用程序的总拥有成本

标准用户帐户非常吸引 IT 管理员,这些管理员希望提高对托管计算机的安全性和控制,同时降低总拥有成本 (TCO) 。 由于标准用户帐户无法进行系统更改,因此与降低 TCO 以及更好地控制应用程序安装和系统范围的修改有直接关系。 标准用户帐户也对家长与孩子共享计算机的家庭用户很有吸引力。 Microsoft Windows Vista 包括集成的家长控制,只有将儿童用户帐户创建为标准用户才能成功实现。 标准用户帐户也不能更改或删除其他用户创建的文件。 他们无法无意或故意读取其他用户配置文件中的文件、感染系统文件或更改系统共享的可执行文件。 标准用户帐户可全面改善计算机安全和家长控制。

默认保护

在 Microsoft,Microsoft 可信计算计划的宗旨已根植于软件开发中。 因此,提高安全性一直是 Windows Vista 开发过程中不可或缺的一部分。

可信计算的安全支柱包括三个基础知识:设计安全、默认安全以及部署安全。 你和其他 ISV 如何开发应用程序来促进操作系统的整体安全性,将是在 Windows Vista 中实现可信计算的关键成功因素。

本指南的其余部分旨在帮助开发人员了解如何:

  • 编写不需要用户作为管理员来执行日常任务的应用程序
  • 使用 Windows® Installer 4.0 UAC 修补技术创建安装包,这些技术可很好地部署到企业中的标准用户桌面,并在家庭中正确更新。
  • 确定标准用户和管理功能,并推断管理任务,实现 UAC 兼容性
  • 编写利用 UAC 功能的应用程序用户界面

对于 UAC 的成功,应用程序开发人员必须接受最低特权理念,并设计其应用程序以便在使用标准用户帐户运行时正常运行。

Windows Vista 版本的目标之一是向所有开发人员宣传和鼓励在管理员审批模式下为标准用户和管理员设计的原则。 实现此目标将有助于防止针对单个应用程序的各种攻击,并降低此类攻击危及系统安全的可能性。 虽然现在可以通过要求管理员使用两个帐户在一定程度上实现这些目标,但往往由于以下原因而失败:

  • 几乎不可能控制具有完全管理员访问令牌的用户。 管理员可以安装应用程序并运行所需的任何应用程序或脚本。 IT 经理一直在寻求创建“标准桌面”的方法,以便用户以标准用户身份登录。 标准桌面可大大降低技术支持成本并减少 IT 开销。
  • 每当用户希望执行管理操作时,在帐户之间切换时会产生大量开销。
  • 执行管理操作后,用户可能会忘记切换回其标准用户帐户,或者认为切换回的工作量太大。

因此,用户可能决定始终登录到其管理帐户,从而破坏安全措施。 为了帮助缓解此问题,UAC 引入了管理员审批模式的概念。 管理员审批模式用户帐户是启用了 UAC 的系统上本地管理员组的成员的用户帐户。

在企业中,管理员审批模式将用作迁移到 Windows Vista 的桥梁技术。 理想情况下,企业将以标准用户身份运行所有用户,并禁用标准用户的提升提示。 此设置支持使用软件部署技术(例如 Microsoft Systems Management Server (SMS) )部署安装的托管标准桌面。

重要 Microsoft 仍建议域管理员组的成员继续在 Windows Vista 中维护两个单独的用户帐户:标准用户帐户和域管理员用户帐户。 所有域管理都应使用域管理员帐户完成。 若要进一步增强安全性,请考虑在域环境中部署智能卡解决方案。 有关详细信息,请参阅 使用智能卡进行安全访问规划指南

下面是管理员审批模式的 Windows Vista 设计目标:

  • 对于属于管理员组成员的用户,无需使用两个单独的帐户:此目标仅通过使用标准用户访问令牌运行程序来实现,除非用户提供使用完整管理访问令牌的批准。
  • 防止使用完整管理访问令牌运行的进程被以标准用户身份运行的那些进程访问或修改。
  • 提供管理员工作区和标准用户工作区之间的无缝转换。

目前,大多数 Windows 应用程序必须以管理员身份运行,但实际上不执行管理操作。 这些应用程序是 Microsoft Windows 9x 操作系统理念的副产品:“每个人都是管理员”。

下面是有问题的应用程序的示例:

  • 不必要地写入 HKEY_LOCAL_MACHINE (HKLM 的应用程序) 或文件系统中的系统文件。
  • 使用 Web 界面促进业务线应用程序的 ActiveX 安装。
  • 不必要地请求访问需要完整管理访问令牌的资源的应用程序。

下一部分介绍影响 ISV 的 Windows Vista 新技术。

如何确定应用程序是否具有管理依赖项?

为了帮助开发人员、ISV 和组织评估其应用程序,Microsoft 提供了 Microsoft 标准用户分析器。 标准用户分析器可用于帮助标识应用程序不符合 UAC 的行为。 Microsoft 建议开发人员运行此工具,以确定在标准用户帐户下运行应用程序时出现的问题。 即使应用程序已在 Windows XP 上的标准用户帐户下正确安装和运行,也应执行这些测试。 应用程序可能会执行操作(例如尝试写入系统注册表位置),并根据系统的行为做出决策,例如查找错误响应。 由于添加了新的应用程序兼容性支持,Windows Vista 的行为可能与早期版本的 Windows 操作系统不同。 因此,建议使用新版本的标准用户分析器测试所有应用程序。

标准用户分析器将记录应用程序遇到的所有管理操作,包括注册表/文件系统访问和提升的 API 调用。 此数据存储在日志文件中,并显示在工具中。 除了许多其他依赖项外,标准用户分析器还标识以下常见依赖项:

  • 对仅对受信任用户限制所请求访问权限的对象的依赖关系。

    例如,HKEY_LOCAL_MACHINE仅向管理员和 SYSTEM 授予KEY_WRITE,请求KEY_WRITE HKEY_LOCAL_MACHINE的应用程序将无法在启用 UAC 的情况下工作。

  • 使用具有安全后果的 Windows 权限(例如SE_DEBUG_PRIVILEGE),该权限允许调试其他用户的进程,并且仅授予管理员。

如果我拥有合法的管理员应用程序,有哪些要求?

对于根据设计执行合法管理操作的应用程序,Microsoft 已实现对当前 Windows XP 应用程序清单架构的 trustInfo 部分的扩展。 可以使用这些新属性向系统指示你有一个合法的管理应用程序;系统将自动请求用户批准,以使用完整的管理访问令牌启动应用程序。 有关如何扩展应用程序清单的信息,请参阅本文档中的使用应用程序创建和嵌入应用程序清单部分。

设计适用于 Windows Vista 的应用程序

以下列表表示设计适用于 Windows Vista 的应用程序的工作流:

  1. 测试应用程序的 Windows Vista 应用程序兼容性
  2. 将应用程序分类为标准用户、管理员或混合用户应用程序
  3. 重新设计应用程序的功能,实现 UAC 兼容性
  4. 重新设计应用程序的用户界面
  5. 重新设计应用程序的安装程序
  6. 使用管理应用程序创建和嵌入应用程序清单
  7. 测试应用程序
  8. 对应用程序进行签名
  9. 确定是否继续执行 Windows Vista 徽标计划

1.测试应用程序兼容性

可以通过安装标准用户分析器轻松执行应用程序与 UAC 的兼容性测试。

若要利用标准用户分析器的图形日志显示,必须安装 Microsoft 应用程序验证程序。 应用程序验证程序 是 Microsoft 网站上的免费下载。

以下过程说明如何使用标准用户分析器识别无法在 Windows Vista 上正确运行的 Windows Vista 管理应用程序。

可以使用两种方法来利用标准用户分析器:以标准用户身份启动应用程序或以管理员身份启动应用程序:

以标准用户身份启动应用程序。

在此实例中,标准用户分析器在诊断模式下运行。 应用程序将在遇到第一个错误时失败,标准用户分析器将报告失败的原因。

以管理员身份启动应用程序。

在此实例中,标准用户分析器在预测模式下运行。 应用程序将能够运行其课程,标准用户分析器将预测并概述应用程序在以标准用户身份运行时可能遇到的错误。

修复并解决 bug 后,无需使用标准用户分析器即可以标准用户身份再次执行此过程,以确保应用程序在 Windows Vista 上按预期运行。

确定 Windows Vista 前应用程序的应用程序兼容性问题

  1. 以管理员身份在管理员审批模式下登录到 Windows Vista 计算机。
  2. 单击 “开始”,单击“ 所有程序”,然后单击“ 标准用户分析器”。
  3. 在标准用户分析器中,对于“目标应用程序”,指定要测试的应用程序的完整目录路径,或者单击“ 浏览 ”按钮以使用 Windows 资源管理器查找程序可执行文件。
  4. 单击“ 启动 ”,然后单击“用户帐户控制”对话框中的“ 继续 ”。
  5. 测试应用程序启动后,在应用程序中执行标准管理任务,并在完成后关闭应用程序。
  6. 在标准用户分析器中,检查每个选项卡上的输出。使用此数据来确定程序可能存在的兼容性问题。

2. 将应用程序分类为标准用户、管理员或混合用户应用程序

Windows Vista 中的管理应用程序通常混合使用管理和标准用户功能。 因此,在决定应用程序在 Windows Vista 中的工作方式时,必须考虑许多选项。 可以通过提示用户进行审批,完全删除管理功能或将其与标准用户帐户功能分开。

帮助对应用程序进行分类的问题

回答以下问题以确定应用程序是否需要重新设计才能实现 Windows Vista 兼容性:

  • 应用程序是否以标准用户身份运行?
  • 是否可以修复管理功能以不再需要管理员访问令牌?
  • 是否可以从程序的功能中删除管理部分?

应用程序是否以标准用户身份运行?

若要回答此问题,请确保标准用户使用应用程序或功能。 如果功能的任何部分要求用户是管理员,则此问题的回答为“否”。

如何验证标准用户使用应用程序或控制面板:

  • 以标准用户和管理员身份全面测试应用程序或控制面板。 验证标准用户和管理员的用户交互是否完全相同。
  • 检查设置在注册表中的存储位置。 如果 HKLM 中存储了任何设置,应用程序或控制面板很可能需要管理员访问令牌。
  • 如果任何设置是每台计算机,则应用程序或控制面板将需要管理员访问令牌。
  • 如果任何设置在其他用户的配置文件中执行任何操作,则应用程序或控制面板将需要管理员访问令牌。

是否可将管理功能修复为不再需要管理员访问令牌?

如果应用程序或控制面板具有需要完全管理员访问令牌的设置或交互,是否可以将其更改为以标准用户身份正常工作? 具体而言,程序是否可以改为在每用户设置中存储信息? 如果不能,则此问题的回答为“否”。

Windows 计算器) Calc.exe (可以修复的功能/设置类型的一个很好的示例。 在 Windows XP 中,“Scientific”与“Standard”的设置是每台计算机的设置,这意味着需要完整的管理访问令牌才能更改设置。 在 Windows Vista 中,此设置存储在用户的配置文件中。

如何验证是否可以从程序的功能中删除管理部分:

  • 以标准用户和管理员身份全面测试应用程序或控制面板。 这两种类型的用户的体验是否可以相同?

  • 是否可以降低写入 HKLM 密钥所需的访问控制列表 (ACL) ?

    注意 本课程不应掉以轻心。 请谨慎,不要通过降低 ACL 提供的控制来损害系统的整体安全性。

  • 是否可以将用户界面更改为设置每用户状态而不是全局状态 (,并且不通过用户界面) 公开全局状态修改?

是否可以从程序的功能中删除管理部分?

你的功能是否绝对必须具有此功能? 如果无法剪切管理功能,则此问题的回答为“否”。

若要确定是否可以从程序的功能中删除管理部分,请执行以下操作:

  • 以标准用户和管理员身份测试控制面板。 保留此功能的用户方案是什么?
  • 此设置/功能是否在其他地方公开? 也许控制面板中的功能是多余的。

分析对应用程序进行分类的答案

如果你对上述任何问题回答了“是”

如果有任何) ,在应用程序或控制面板中 (进行必要的更改,以消除那些需要用户具有完全管理访问令牌的项目。

以下列表详细介绍了拥有真正标准用户应用程序的好处:

  • 你的功能同样适用于所有用户。 这是理想的状态,因为大多数功能不应需要完整的管理员访问令牌。
  • 用户永远不会看到功能提升提示。
  • 无需管理员访问令牌,功能更加安全。

如果你对上述所有问题都回答了“否”

必须修改应用程序或控制面板,使该功能适用于 UAC。

验证应用程序或控制面板是否适用于 UAC:

最后,以标准用户和管理员身份测试应用程序或控制面板。 确保 (上述问题) 的其他选项不能用于此特定应用程序或控制面板。

3. 重新设计应用程序的功能,实现 UAC 兼容性

在对应用程序进行分类并确定是否必须为 UAC 重新设计应用程序后,请使用本部分中的信息。

重新设计适用于 Windows Vista 的应用程序的一大组成部分将在其核心上检查应用程序的用户访问模型。

所有 Windows Vista 应用程序的要求

指定 requestedExecutionLevel

若要使 UAC 正常运行,操作系统必须能够识别哪些代码需要提升的权限,哪些代码不需要提升的权限。

在 Windows Vista 中,这些更改要求将应用程序标记为允许操作系统确定应在哪个上下文中启动应用程序的信息。 例如,需要将标准用户应用程序标记为作为调用方运行,并且系统需要标识已启用辅助功能的应用程序。

不向 Rundll32 注册组件

某些应用程序使用 Windows Rundll32 可执行文件来运行组件。 但是,此方法不符合 Windows Vista 开发要求。 直接调用 Rundll32 会导致 UAC 兼容性问题。 当应用程序依赖于 Rundll32 可执行文件执行其执行时,Rundll32 代表应用程序调用应用程序信息服务 (AIS) 以启动 UAC 提升提示。 因此,UAC 提升提示不知道原始应用程序,并将请求提升的应用程序显示为“Windows 主机进程 (Rundll32) ”。如果没有请求提升的应用程序的明确说明和图标,用户无法识别应用程序并确定提升该应用程序是否安全。

如果应用程序调用 Rundll32 来运行组件,请使用以下工作流重新设计执行调用。

  1. 为应用程序创建新的单独的可执行文件。
  2. 在新的可执行文件中,调用使用 Rundll32 指定的 DLL 中导出的函数。 如果 DLL 没有 .lib,则可能需要加载 DLL。
  3. 在资源文件中,为可执行文件创建并添加新图标。 当应用程序请求提升时,此图标将显示在用户帐户控制提升提示中。
  4. 为可执行文件提供一个简短且有意义的名称。 应用程序请求提升时,此名称将显示在用户帐户控制提升提示中。
  5. 为可执行文件创建并嵌入应用程序清单文件,并使用请求的执行级别 requireAdministrator 对其进行标记。 使用应用程序创建和嵌入应用程序清单部分详细介绍了此过程。
  6. 验证码对新的可执行文件进行签名。 验证码对应用程序进行签名部分详细介绍了此过程。

卸载应用程序后,用户应能够重新安装它,而不会出错。

标准用户应用程序的要求

下面是设计在标准用户帐户下正确运行的应用程序时要记住的事项的摘要。 开发人员在应用程序的设计阶段应牢记这些要求。

安装

  • 从不执行 (管理操作,例如在首次运行时完成安装过程) ;应在初始设置过程中完成此操作。
  • 切勿直接写入 Windows 目录或子目录。 使用正确的方法来安装字体等文件。
  • 如果需要自动更新应用程序,请使用适合标准用户使用的机制,例如 Windows Installer 4.0 用户帐户控制修补来完成更新。

保存状态

  • 不要将每用户信息或用户可写信息写入程序文件或程序目录。
  • 请勿在文件系统中使用硬编码路径。 利用 KnownFolders API 和 ShGetFolder 查找写入数据的位置。

在标准用户帐户下运行和测试

如果要编写非管理应用程序(如 LOB 应用程序)或用户应用程序(如游戏),则必须始终将应用程序数据写入标准用户有权访问的位置。 下面是一些建议的要求。

  • 将每用户数据写入用户配置文件:CSIDL_APPDATA。
  • 将每台计算机数据写入 Users\All Users\Application Data: CSIDL_COMMON_APPDATA。
  • 应用程序不能依赖于任何管理 API。 例如,预期成功调用 SetTokenInformation () Windows 函数的程序将在标准用户帐户下失败。

快速用户切换 (FUS) 感知

应用程序通常由运行应用程序的用户以外的用户安装。 例如,在家庭中,这意味着父级将为子级安装应用程序。 在企业中,部署系统(如短信或组策略播发)将使用管理员帐户安装应用程序。

如果首次运行时不存在每用户设置,请重新生成它们。 不要假定设置过程负责设置。

管理员应用程序的要求

使用 HWND 属性作为前台应用程序进行确认

后台应用程序会自动提示用户在任务栏上进行提升,而不是自动转到安全桌面进行提升。 提升提示将在任务栏上显示为最小化,并闪烁以通知用户应用程序已请求提升。 当用户浏览到网站并开始下载安装文件时,会出现后台提升的示例。 然后,在后台下载安装时,用户将转到检查电子邮件。 在后台完成下载并开始安装后,会将提升检测为后台任务,而不是前台任务。 此检测可防止安装在用户执行另一项任务(即阅读电子邮件)时突然窃取用户屏幕的焦点。 此行为为提升提示创建更好的用户体验。

但是,某些前台应用程序当前在 Windows Vista 上提示为后台应用程序。 此行为是不存在父 HWND 的结果。 为了确保 Windows Vista 将应用程序确认为前台应用程序,必须使用 ShellExecute、CreateElevatedComObject (COM) 或托管代码调用传递父 HWND。

UAC 提升机制使用 HWND 作为确定高程是背景高程还是前景高程的一部分。 如果应用程序确定为后台应用程序,则提升将作为闪烁按钮放置在任务栏上。 与请求前台访问的任何应用程序一样,用户必须单击该按钮,然后才能继续提升。 即使应用程序实际上可能具有前台,不传递 HWND 也会导致发生这种情况。

以下代码示例演示如何使用 ShellExecute 传递 HWND:

BOOL RunAsAdmin( HWND hWnd, LPTSTR lpFile, LPTSTR lpParameters )
{
    SHELLEXECUTEINFO   sei;
    ZeroMemory ( &sei, sizeof(sei) );

    sei.cbSize          = sizeof(SHELLEXECUTEINFOW);
    sei.hwnd            = hWnd;
    sei.fMask           = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI;
    sei.lpVerb          = _TEXT("runas");
    sei.lpFile          = lpFile;
    sei.lpParameters    = lpParameters;
    sei.nShow           = SW_SHOWNORMAL;

    if ( ! ShellExecuteEx ( &sei ) )
    {
        printf( "Error: ShellExecuteEx failed 0x%x\n", GetLastError() );
        return FALSE;
    }
    return TRUE;
}

以下代码示例演示如何使用提升名字对象传递具有 CreateElevatedComObject 的 HWND。 它假定你已在当前线程上初始化 COM。 有关提升名字对象的详细信息,请参阅本文档的步骤 4。

HRESULT CreateElevatedComObject(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void ** ppv)
{
    BIND_OPTS3 bo;
    WCHAR  wszCLSID[50];
    WCHAR  wszMonikerName[300];

    StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0])); 
    HRESULT hr = StringCchPrintf(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
    if (FAILED(hr))
        return hr;
    memset(&bo, 0, sizeof(bo));
    bo.cbStruct = sizeof(bo);
    bo.hwnd = hwnd;
    bo.dwClassContext  = CLSCTX_LOCAL_SERVER;
    return CoGetObject(wszMonikerName, &bo, riid, ppv);

BIND_OPTS3是 Windows Vista 中的新增功能。 它派生自 BIND_OPTS2。 它的定义如下:

typedef struct tagBIND_OPTS3 : tagBIND_OPTS2
{
    HWND hwnd;
} BIND_OPTS3, * LPBIND_OPTS3;

唯一的加法是 HWND 字段 hwnd。 此句柄表示一个窗口,该窗口在启用安全桌面提示时成为提升 UI 的所有者。

以下代码示例演示如何在托管代码中传递 HWND,以确保父对话了解 HWND 及其用法。

System.Diagnostics.Process newProcess = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo("D:\SomeProgram.exe");
info.UseShellExecute = true;
info.ErrorDialog = true;
info.ErrorDialogParentHandle = this.Handle;
newProcess.StartInfo = info;
newProcess.Start();

在用户的登录路径中不提示提升

用户登录时启动并需要提升的应用程序现在在登录路径中被阻止。 在不阻止应用程序提示用户登录路径中提升的情况下,标准用户和管理员每次登录时都需要响应“用户帐户控制”对话框。 如果应用程序已被阻止,Windows Vista 会通过在系统托盘中放置图标来通知用户。 然后,用户可以右键单击此图标,以运行在用户登录时阻止提示提升的应用程序。 用户可以通过双击托盘图标来管理禁用或从此列表中删除的启动应用程序。

本文档的参考部分提供了演示如何使用任务计划程序为用户执行提升的 C++ 代码示例。

请勿使用运行方式启动提升的进程

Windows XP 和 Windows Server 2003 中的 “运行方式...” 选项已替换为上下文菜单上的 “以管理员身份运行” 选项, (右键单击 Windows Vista 中的可执行文件) 时可用。 当标准用户选择 “以管理员身份运行” 选项时,用户会看到本地计算机上的活动管理员列表。 还会显示具有更高权限的标准用户,例如“备份操作员”组的成员。 当管理员选择 “以管理员身份运行 ”选项时,“用户帐户控制”对话框会立即提示用户在运行应用程序之前继续。

用户必须在命令提示符下使用 runas 命令才能以其他用户身份运行应用程序。

重要 请注意,无论其是具有备份操作员或管理员等权限的标准用户,runas 都无法使用提升的访问令牌启动应用程序。 runas 命令授予用户使用不同凭据启动应用程序的能力。 使用不同帐户启动应用程序的最佳方法是使用服务以编程方式执行操作,而不是依赖用户以其他用户身份运行组件。 如果程序以编程方式使用 runas 命令,请确保它不打算启动提升的进程。

如果应用程序要求用户使用其他用户帐户运行应用程序的部分,请确保公开具有命令提示符选项的 runas 命令。 下表详细介绍了 runas 命令的可用参数。

运行方式参数

参数 说明
/noprofile 指定不应加载用户的配置文件。 这使应用程序能够更快地加载,但可能导致某些应用程序出现故障。
/配置 文件 指定应加载用户的配置文件。 这是默认设置。
/Env 使用当前环境,而不是用户的。
/netonly 如果指定的凭据仅用于远程访问,请使用此参数。
/savecred 使用以前由用户保存的凭据。 此选项在 Windows XP 家庭版上不可用,将被忽略。
/smartcard 如果要提供的凭据来自智能卡,请使用此参数。
/user 用户的用户名。 用户名应以 USER\DOMAIN 或 USER@DOMAIN 的形式提供。
/showtrustlevels 显示可用作 /trustlevel 参数的参数的信任级别。
/trustlevel /showtrustlevels 中枚举的级别之一。
节目 可执行文件的命令行。

示例:

runas /noprofile /user:mymachine\Denise cmd

注意:

  • 仅在出现提示时输入用户的密码。
  • /profile 参数与 /netonly 参数不兼容。
  • /savecred 参数与 /smartcard 参数不兼容。

控制台应用程序的要求

控制台应用程序在控制台窗口中显示其输出,而不是单独的用户界面。 如果应用程序需要完全管理员访问令牌才能运行,则需要从提升的控制台窗口启动该应用程序。

必须对控制台应用程序执行以下操作:

将应用程序标记为“asInvoker”

为此,可以创作应用程序清单,在其中设置了 RequestedExecutionLevel == asInvoker。 此设置允许来自非提升上下文的调用方创建进程,从而允许他们继续执行步骤 2。

如果应用程序在没有完全管理员访问令牌的情况下运行,请提供错误消息

如果在非提升的控制台中启动应用程序,则应用程序应提供简短消息并退出。 建议的消息是:

“拒绝访问。 使用所选选项需要管理员权限。 使用管理员命令提示符完成这些任务。”

启动失败时,应用程序还应返回错误代码ERROR_ELEVATION_REQUIRED,以便于编写脚本。

脚本要求

可以将脚本视为一组按预定义顺序运行的应用程序,以及一个应用程序被引导到另一个应用程序的结果。

为了使脚本符合 UAC 要求,请检查脚本的逻辑并添加“测试”,以确保在脚本中执行操作之前, (或运行脚本的人员) 有足够的权限来执行该任务。

批量操作的要求

如果应用程序执行的任务包含对多个对象的操作,并且其中一些可能需要用户的管理访问令牌,则在首次需要时显示提升提示。 如果用户批准提升,则执行其余任务。 否则,终止批处理操作。 此行为与当前的多选/复制/删除操作一致。

帮助标识管理员的 API

  • IsUserAnAdmin ()
  • GetTokenInformation ()

管理员和标准用户之间本质上不同的注册表/处理访问权限

  • MAXIMUM_ALLOWED
  • KEY_WRITE
  • 应用于注册表项时删除 ()
  • 其他类似于 HKLM 的关键字 (XP) 上的 MAXIMUM_ALLOWED 打开:
  • SHELLKEY_HKLM_EXPLORER
  • SHELLKEY_HKLM_SHELL

将应用重新定向到 HKLM 注册表值和虚拟化的其他 API

  • WritePrivateProfileString (,,,“system.ini”) ;
  • CheckSectionAccess (“system.ini”,...) ;

4. 重新设计应用程序的用户界面,实现 UAC 兼容性

使用本部分中的准则开发应用程序的用户界面,实现 UAC 兼容性。 在应用程序的开发中严格遵循这些准则可确保应用程序在 Windows Vista 中具有一致且可预测的用户体验。

  • UAC 对 Windows 用户体验的影响
  • UAC 用户体验的目标
  • 提升提示
  • 用户体验流程流
  • 提升入口点
  • 用户界面实现
  • 何时向应用程序的用户界面添加防护图标
  • 仅限管理员的应用程序的关键决策

重要 仅对应用程序的用户界面进行折射将无法满足 UAC 兼容性的要求。 应用程序的核心功能必须符合 Windows Vista 标准用户模型要求。 上一步的步骤 3:重新设计应用程序的功能,实现 UAC 兼容性中详细介绍了这些要求。

UAC 对 Windows 用户体验的影响

管理员将感受到对用户体验的最大和最直接的影响。 管理员用户现在需要提供完成管理任务的权限。 此外,标准用户现在可以请求管理员为当前登录的会话中的某些管理任务授予权限。

UAC 用户体验的目标

UAC 用户体验的总体目标是在用户体验中提供可预测性:

  • 对于管理员来说,这意味着用户始终知道他/她何时需要授予运行提升的任务的权限。

这是请求用户自己的管理员访问令牌的行为,以便他/她可以进行管理员所需的更改。

  • 对于标准用户,这意味着他们将知道何时:
    • 需要为管理任务提供管理员批准 (家庭和非托管环境)
    • 或者,当 无法完成 (显式禁止提升的托管环境的任务时,) 必须联系技术支持

设计目标

以下列表包含 UAC 设计目标。

消除不必要的提升

用户应仅需要提升权限才能执行需要管理员访问令牌的任务。 所有其他任务都应设计为无需提升。 Windows Vista 之前的软件通常通过写入 HKLM 或 HKCR 注册表部分或 Program Files 或 Windows 系统文件夹来不必要地要求管理员访问令牌。

可预测

管理员需要知道哪些任务需要提升。 如果他们无法准确预测提升的需求,则更有可能在不应执行管理任务时同意。 标准用户需要知道哪些任务需要管理员执行,或者根本不能在托管环境中执行。

需要最少的工作量

需要更高特权访问令牌的任务应设计为需要单个提升。 需要多次提升的任务很快就会变得乏味。

还原为标准用户

完成需要更高访问令牌级别的任务后,程序应还原到标准用户状态。

提升提示

提升提示是基于现有 Windows 用户界面构建的。 提升提示显示有关请求提升的可执行文件的上下文信息,上下文因应用程序是否为 Authenticode 签名而异。 提升提示分为两种变体:同意提示和凭据提示。

管理员在尝试执行管理任务时,会以管理员审批模式向管理员显示同意提示。 这是管理员审批模式下管理员的默认用户体验,可以在本地安全策略管理器管理单元中配置 (secpol.msc) 和 组策略。

下图是用户帐户控制同意提示的示例。

Bb530410.vistauacreqs04 (en-us,MSDN.10) .gif

图 4。 用户帐户控制许可提示

凭据提示

当标准用户尝试执行管理任务时,凭据提示会显示给他们。 这是标准用户的默认用户体验,可以在本地安全策略管理器管理单元中配置 (secpol.msc) 和 组策略。

下图是用户帐户控制凭据提示的示例。

Bb530410.vistauacreqs05 (en-us,MSDN.10) .gif

图 5。 用户帐户控制凭据提示

下表概述了 Windows Vista 中每种用户帐户类型的默认提示样式。

默认提升提示行为

用户帐户类型 提升提示设置
标准用户 提示输入凭据
管理员审批模式下的管理员帐户 提示要求同意

用户体验流程流

UAC 用户体验流程由三个不同的组件组成:

  1. 高程入口点 (例如,) 显示 UAC 防护图标的控件或链接。
  2. 提升提示 (请求同意或管理员凭据) 。
  3. 提升的进程。

以下示例工作流总结了上述组件之间的关系:

  1. 处于管理员审批模式的管理员登录到 Windows Vista 计算机。
  2. 然后,用户决定为计算机添加另一个管理员用户。
  3. 用户单击“开始”,单击“控制面板”,然后单击标题为“允许程序通过 Windows 防火墙”的“安全性”部分中的链接,该链接以防护图标内联方式显示。
  4. 此时会显示一个同意提示,请求用户进行审批。
  5. 用户单击“ 继续 ”,并创建提升的进程。
  6. 在 Windows 防火墙设置中,用户修改 Windows 防火墙设置,然后单击“ 确定”,这将终止提升的进程。
  7. 用户继续以标准用户的身份在计算机上工作。

注意 提升入口点不会记住状态 (例如,从受防护的位置或任务) 向后导航时,入口点不会记住已发生提升。 因此,用户需要重新提升才能再次输入任务/链接/按钮。

提升入口点

对于入口点,盾牌图标将附加到某些控件 (例如按钮、命令链接、超链接) ,以指示下一个直接步骤需要提升。

盾牌图标

盾牌图标是 UAC 提升点的主要用户界面修饰。 此图标表示 Windows Vista 和以前版本的 Windows 中与安全相关的活动,此关系在 Windows Vista 中继续。

下图是盾牌图标的示例。

Bb530410.vistauacreqs06 (en-us,MSDN.10) .gif

图 6。 防火墙图标

盾牌图标将在 UAC 用户体验的所有三个组件中扮演关键角色。

使用 Windows 资源管理器查看系统时,任何标记为在启动时请求管理员访问令牌的应用程序都会在其图标上自动修饰一个盾牌字形。 这允许用户知道哪些应用程序在启动时将请求提升。

盾牌图标属性:

  • 在整个 UAC 用户体验中一致的外观。
  • 不反映任何视觉状态 (例如活动、悬停、禁用等) 。
  • 不记得状态。

在用户体验中,标有盾牌图标的入口点可以采用三种一致的控件样式:

  • UAC 按钮
  • UAC 超链接
  • UAC 命令链接

这些样式适用于可以显示这些用户界面元素的所有方案,例如向导、属性页、控制面板框架、资源管理器等。每个样式都意味着在用户单击 UAC 用户界面控件后,将立即显示提升提示。

本部分还讨论了第四个 UAC 用户界面入口点 UAC 图标覆盖。 可执行文件是否接收图标覆盖不受应用程序开发人员控制。 对于 requestedExecutionLevel 设置为 requireAdministrator 的可执行文件,Windows Vista 在应用程序的图标上覆盖一个盾牌图标。

UAC 防护按钮

UAC 防护按钮应在按下时要求提升提示提示用户批准或凭据的任何用户界面按钮中使用。

UAC 防护按钮可用作提交按钮 (例如,向导) 中的 “下一步 ”,或用作显示其他设置用户界面的按钮, (例如,在属性对话框中) 更改 设置

UAC 防护按钮由两个用户界面组件组成:

  • 防火墙图标
  • Text label

UAC 防护按钮采用一种打包方式,以便开发人员可以使用它代替普通按钮。 UAC 按钮还支持在文本标签的左侧或右侧呈现盾牌图标。 此外,开发人员可以选择在显示 UAC 按钮时隐藏/显示盾牌图标。

以下屏幕截图是 UAC 防护按钮的示例。

Bb530410.vistauacreqs07 (en-us,MSDN.10) .gif

图 7。 UAC 防护按钮

UAC 超链接

UAC 超链接应用于任何用户界面超链接,单击该超链接时,将需要提升提示来提示用户进行审批或凭据。

UAC 超链接由以下组件组成:

  • 防火墙图标
  • 超链接控件

UAC 超链接未打包为盾牌图标,供开发人员使用。 开发人员需要获取盾牌图标资源,并将其呈现在超链接旁边。

以下屏幕截图是 UAC 超链接的示例。

Bb530410.vistauacreqs08 (en-us,MSDN.10) .gif

图 8。 UAC 超链接

UAC 命令链接

应在任何用户界面按钮中使用 UAC 命令链接,单击该按钮时需要提升提示以提示用户进行审批或凭据。

UAC 命令链接应仅用作提交按钮 (例如对话框中) 的“执行此操作”。

UAC 命令链接包含以下组件:

  • 防火墙图标
  • 标准命令链接组件
  • 链接文本
  • 备注文本

UAC 命令链接的打包方式是开发人员可以使用 UAC 命令链接代替普通命令链接。 UAC 命令链接支持在命令链接的左侧或右侧呈现盾牌图标。

下面是 UAC 命令链接的示例。

Bb530410.vistauacreqs09 (en-us,MSDN.10) .gif

图 9. UAC 命令链接

图标覆盖

在 Windows Vista 中,如果可执行文件需要提升才能启动,则应使用盾牌图标“标记”可执行文件的图标,以指示这一事实。 可执行文件的应用程序清单必须标记“requireAdministrator”,以将可执行文件指定为需要完整的管理访问令牌。 根据安装程序检测启发法,防护图标覆盖也将自动放置在被认为需要提升的可执行文件上。 例如,名为 setup.exe 的文件将自动收到盾牌图标覆盖,即使可执行文件没有嵌入的应用程序清单也是如此。

下图是 UAC 图标覆盖的示例。

Bb530410.vistauacreqs10 (en-us,MSDN.10) .gif

图 10. UAC 图标覆盖

注意 本文档的“使用应用程序创建和嵌入应用程序清单”部分提供了有关如何使用可执行文件创建和嵌入应用程序清单的指南。

用户界面实现

盾牌图标实现和 API

本部分提供有关开发人员在迁移或实现新的管理应用程序功能时可用的图标和 API 的初步信息。

盾牌图标实现和 API

图标 API
Shield 用户资源:IDI_SHIELD
Button Button_SetElevationRequired (hwndButton)
Syslink/Hyperlink syslink 旁边的布局IDI_SHIELD
命令链接 加载IDI_SHIELD并设置为命令链接图标
上下文菜单 DefCM 中静态命令的图标支持

将盾牌图标添加到用户界面

添加一个小图标:

#include <shellapi.h>
SHSTOCKICONINFO sii;
sii.cbSize = sizeof(sii);
SHGetStockIconInfo(SIID_SHIELD, SHGSI_ICON | SHGSI_SMALLICON, &sii);
hiconShield  = sii.hIcon;

添加大图标:

SHSTOCKICONINFO sii;
sii.cbSize = sizeof(sii);
SHGetStockIconInfo(SIID_SHIELD, SHGSI_ICON | SHGSI_LARGEICON, &sii);
hiconShield  = sii.hIcon;

添加自定义大小的图标:

SHSTOCKICONINFO sii;
sii.cbSize = sizeof(sii);
SHGetStockIconInfo(SIID_SHIELD, SHGSI_ICONLOCATION, &sii);
hiconShield  = ExtractIconEx(sii. ...);

注意 通常,不应将盾牌图标直接添加到用户界面。 建议使用在控件中嵌入屏蔽图标的继续方法之一。 此外,只需在用户界面中添加盾牌图标将无法确保 UAC 兼容性。 还必须限制应用程序的整个用户体验, (添加 requestedExecutionLevel,修复任何标准用户 bug,并确保用户界面用户友好且与 UAC 兼容) 。

向按钮添加盾牌图标

标准按钮控件 (PUSHBUTTON,DEFPUSHBUTTON) 已得到增强,允许你添加图标和显示的文本,而无需设置BS_ICON或BS_BITMAP样式。

若要显示盾牌图标,请调用 commctrl.h) 中定义的以下宏 (:

Button_SetElevationRequiredState(hwndButton, fRequired);

注意 hwndButton 是按钮的 HWND;fRequired 确定是显示 (TRUE) 还是) UAC 防护图标隐藏 (FALSE。

向 Windows Installer 按钮添加盾牌图标

使用内部表支持创作的 Windows Installer 对话框可以通过在控件上设置 ElevationShield 属性,向用户界面对话框序列的最后一个按钮添加防护。

将盾牌图标添加到向导上的“下一步”按钮

重要 显示 UAC 防护图标的“下一步”按钮仅在 AeroWizards (PSH_AEROWIZARD) 中受支持。

若要在 AeroWizard 中特定页面的“下一步”按钮上显示盾牌图标,请使用以下代码:

case WM_NOTIFY:
    if (reinterpret_cast<NMHDR*>(lParam)->code == PSN_SETACTIVE)
    {
        // Show next button
        //
        // Note new wParam flag -- when PSWIZBF_ELEVATIONREQUIRED flag
        // is specified, it indicates that the next page will require
        // elevation, so if the next button is being shown, show it as
        // a shield button.

        SendMessage(GetParent(hwndDlg), 
                    PSM_SETWIZBUTTONS, 
                    PSWIZBF_ELEVATIONREQUIRED, 
                    PSWIZBF_NEXT);

        // return 0 to accept the activation
        SetWindowLong(hwndDlg, DWLP_MSGRESULT, 0); 
    }
    break;

向任务对话框按钮添加盾牌图标

谨慎 任务对话框按钮永远不需要 UAC 防护图标。 任务对话框按钮上的“按下”操作应提交/取消和关闭任务对话框。 这样一个按钮然后向用户显示提升提示会很奇怪。

提升模式对话框

使用提升名字对象提升表示模式对话框的 COM 对象。

任务:

  • 将对话框移动到 COM 对象中。
  • 公开 ShowDialog () 方法。
  • 使用 API CreateElevatedComObject () 创建 COM 对象并调用 ShowDialog () 。

此 API 将在完成提升过程后以管理员身份运行 COM 对象的实例。

注意 此 API 的调用更复杂版本可用。 简化版本将在更高版本的 Windows Vista 中提供。

用户教育和帮助指南

当用户界面被重新分解并置于按钮后面时,ISV 应评估是否有必要更改按钮名称。 Microsoft 强烈建议不要将 “高级 ”用作提升任务的按钮标签。 请改用更具描述性且易于理解的标签,例如 “更改设置” 或建议按钮后面的内容的术语。

仅限管理员的用户界面指南

如果应用程序始终由管理员启动,则无需在应用程序的用户界面中添加其他防护。 这是因为应用程序将被提升,其执行的所有操作都将提升,因此不需要进一步提升。

注意 如果在仅限管理员的用户体验中具有指向其他管理员用户界面的链接,则用户界面将启动提升的目标。 因此,无需在仅管理的应用程序中放置任何防护。

何时将盾牌图标添加到应用程序的用户界面

管理选择应用程序

提升的进程或 COM 对象

初始应用程序无需提升即可启动。 用户界面中需要管理访问令牌的项使用盾牌图标作为标识进行修饰。 此修饰向用户指示使用该功能需要管理员批准。 当应用程序检测到其中一个按钮已被选中时,它有以下两个选项。

  • 应用程序使用 ShellExeucute () 启动第二个程序来执行管理任务。 第二个程序将标记为 requireAdministrator 的 requestedExecutionLevel,从而提示用户进行审批。 第二个程序将使用完整的管理访问令牌运行,并能够执行所需的任务。
  • 应用程序使用 CreateElevatedComObject () 启动 COM 对象。 此 API 将在批准后启动具有完整管理访问令牌的 COM 对象,并且此 COM 对象能够执行所需的任务。

此方法提供最丰富的用户体验,是处理管理功能的首选方法。

以下列表详细介绍了提升的进程或 COM 对象的要求:

  • 控制面板应实现盾牌装饰及其所需的体系结构。
  • 开发人员必须确定盾牌应该在用户界面上的位置。
  • 开发人员必须执行体系结构工作,将业务逻辑与用户界面对象分离为 COM 对象。
  • 检测到盾牌图标的 OnClick 事件时,开发人员必须调用 UAC 提升进程。

以下列表详细介绍了正确设计提升的进程或 COM 对象的好处:

  • 这是这两种用户类型的最佳整体用户体验。 用户界面将启动,可供所有人查看,并且该用户界面上的所有 UAC 功能都将可供所有人访问。 仅当需要管理员任务时,用户才会尝试提升以完成任务。
  • 现在完成这项工作将使你完全符合 UAC 标准,继续前进。
  • 用户界面/COM 分离是一种很好的体系结构做法。

单击防护图标会使应用程序启动提升的程序或提升的 COM 对象来执行该任务。

仅限管理员的应用程序

在这种情况下,应用程序的初始启动需要管理员批准。 此方法称为“启动前提示”。 启动后,应用程序使用完整的管理访问令牌运行,因此可以执行所需的管理任务。 此方法对开发人员来说是最少的工作。 应用程序的清单标有 requireAdministrator 的 requestedExecutionLevel。

重要 虽然开发人员需要最少的工作量,但请注意,就像 Windows Vista 中的其他管理应用程序一样,管理员必须提升权限才能使用此应用程序,标准用户将无法使用该应用程序。

以下列表详细介绍了仅限管理员的应用程序的要求:

  • 应用程序清单应包含 requestedExecutionLevel 标记设置为 requireAdministrator。
  • 在 Windows 使用完全管理访问令牌启动应用程序之前,系统会提示用户进行管理员批准。

以下列表详细介绍了正确设计仅限管理员的应用程序的好处:

  • 操作系统不必“猜测”设置应用程序是否为管理应用程序。
  • 系统会自动向标准用户提供该操作是管理操作的提示。 例如,当看到标记为 requireAdministrator 的应用程序图标时,图标中嵌入了一个防护板。
  • 在 Windows Vista 上,如果将应用程序标记为 requireAdministrator,你知道启动应用程序后,它将使用完全管理员访问令牌运行。 用户必须提升权限才能 (以管理员审批模式的管理员身份或使用“以管理员身份运行”) 运行应用程序。

注意 将应用程序标记为 requireAdministrator 不会以无提示方式提升应用程序。 用户仍必须授予提升许可才能启动应用程序。 无法将 Windows Vista 中的应用程序标记为无提示提升。

以下列表详细介绍了设计仅限管理员的应用程序的注意事项:

  • 此用户体验意味着所有用户都将看到提升提示, (凭据提示或同意提示) 用户界面可见。 这也意味着,在使用管理员凭据进行身份验证之前,没有人能够简单地查看当前设置
  • 如果在安装应用程序上标记 requireAdministrator,则应注意,运行安装程序的用户与可能使用该应用程序的用户不同。 因此,在管理设置过程中,不应修改HKEY_CURRENT_USER (HKCU) 和其他每用户设置,例如写入用户配置文件。

重要 必须假定运行管理应用程序的用户不同于计算机上的普通用户。

需要管理员访问令牌的可执行文件标有盾牌图标覆盖。

混合应用程序

混合应用程序是一个可由用户运行的应用程序 - 系统的所有用户 (标准用户、处于管理员审批模式的管理员,以及两者之间的管理员(如备份操作员) )。 这也是一个“启动前提示”应用程序。 应用程序将使用调用程序的访问令牌运行,并且将为标准用户正常启动, (没有提升提示) 。然后,程序必须在运行时修改其行为,以根据获取的管理访问令牌禁用用户无法使用的功能。

混合应用程序在启动后无法获取其他管理权限;因此,它不提供前面所述的提升的进程或 COM 对象方法的灵活性。 这对于需要高于标准用户的访问令牌但比完全管理员少的应用程序最有用。

例如,Microsoft 管理控制台 (MMC) 标记为 highestAvailable。 如果真正的标准用户运行 MMC,MMC 将作为标准用户应用程序启动,而无需任何提升尝试或提示。 如果用户是“拆分令牌”用户,例如管理员审批模式下的管理员或备份操作员,则操作系统将提示用户同意使用用户的“最高”可用权限启动 MMC。 对于具有备份操作员权限的标准用户,在提升后,MMC 将使用标准用户 + 备份操作员启动,但只不过是其他操作。 如果管理员启动 MMC,则提升后,MMC 将作为完全管理员应用程序运行。

正确设计混合应用程序的好处是,应用程序可供系统的所有用户使用,即使某些功能可能已禁用。

以下列表详细介绍了设计混合应用程序的注意事项:

  • 开发人员必须根据用户可用的管理 Windows 权限和用户权限动态更改应用程序的行为。
  • 标准用户无法对用户界面上的管理级函数执行操作。 一旦程序运行 (管理员在打开用户界面) 之前必须提升权限,就不可能进行提示提升。

注意 前一个项目符号有一种解决方法。 管理员可以在标准用户的计算机上启动提升的命令提示符,并从命令提示符运行应用程序。 例如,右键单击命令提示符,选择“ 以管理员身份运行”,然后在命令提示符中键入“applicationname.exe”。

在管理员审批模式下,用户体验在标准用户和管理员之间分支。

示例混合应用程序:备份应用程序

应用程序可由备份操作员组的成员启动。 然后,程序将验证用户可用的最高级别的管理 Windows 特权和用户权限是否足以运行程序。 有关程序启动行为的详细信息,请参阅本文档的应用程序清单标记和应用程序启动行为部分。

设计Administrator-Only应用程序的关键决策

后端业务对象

本部分概述了开发人员在开发提供最佳用户体验的管理应用程序时可以选择的三种模型。

  • 管理员 Broker 模型
  • Back-End服务模型
  • 管理员 COM 对象模型

管理员 Broker 模型

在 管理员 Broker 模型中,应用程序分为两个独立的可执行文件:标准用户可执行文件和管理可执行文件。 开发人员使用应用程序清单使用 asInvoker 的 requestedExecutionLevel 标记标准用户程序,并使用 requireAdministrator 的 requestedExecutionLevel 标记管理程序。 用户将首先启动标准用户程序。 当用户尝试执行标准用户程序知道需要完整管理员访问令牌的操作时,它会执行 ShellExecute () 并启动管理程序。 Windows ShellExecute () API 查看清单并请求用户批准,然后再使用用户的完整管理访问令牌运行应用程序。 然后,管理程序可以执行管理任务。

注意 管理可执行程序可以使用共享内存、本地 RPC 或命名管道与标准用户可执行文件实现进程间通信。 如果管理程序确实启用了与标准用户可执行文件的通信,开发人员需要使用良好的安全做法来验证来自较低特权程序的所有输入。

注意 第二个程序启动后,两个程序之间没有通信通道

以下列表详细信息用于管理代理模型:

  • 向导 - 当硬件向导意识到所需的驱动程序未安装在计算机上或位于企业的批准位置时,它需要一个能够将驱动程序移动到计算机存储中的提升的应用程序。
  • Autorun.exe调用Setup.exe - 首次放入游戏 CD 时,autorun.exe所需的操作是设置应用程序。 第二次插入 CD 时,默认操作是玩游戏。

使用管理代理模型的一个好处是,它可能是开发人员最容易实现的机制。

以下列表详细介绍了使用 管理员 Broker 模型的一些缺点:

  • 从应用程序到应用程序的转换可能会让用户感到困惑。 可能很难让用户了解新应用程序在监视器上“弹出”的原因。
  • 此外,状态更难在这两个应用程序之间传递。 例如,你不会使用它在标准用户控制面板 (CPL) 与其管理员对应之间传递状态,只是为了允许同一 CPL 具有管理功能和非管理功能。 标准用户 CPL 必须将其状态存储在某个位置。
  • 在两个程序之间拆分功能时,通常会有很多复制的代码。

若要实现管理代理模型,请创建两个程序 (一个标准用户和一个管理) ,用相应的清单 requestedExecutionLevel 标记它们,并使用 ShellExecute () 从标准用户程序启动管理程序。

Back-End服务模型

在后端服务模型中,应用程序再次分解为两个独立的可执行文件:一个是向用户提供用户界面的标准用户可执行文件,另一个是在系统上运行的后端服务。 Microsoft 远程过程调用 (RPC) 用于在两者之间进行通信。 前端应用程序标记为 requestedExecutionLevel asInvoker,后端服务作为 SYSTEM 运行。 应用程序和后端服务之间的通信是使用 RPC 完成的。

后端服务模型的一个用途是控制可能影响系统的程序,例如防病毒程序或反间谍软件) 。 前端应用程序提供登录用户和控制服务方面的方式。

使用后端服务模型的主要好处是无需提升提示。

以下列表详细介绍了使用后端服务模型的一些缺点:

  • 服务需要限制前端应用程序可以告知其执行的活动类型。 例如,防病毒服务可能允许标准用户启动系统扫描,但不允许禁用实时病毒检查。
  • 向系统添加不必要的服务可能会影响整个系统。 确保你的服务确实是 Windows Vista 实现所必需的,并且服务架构正确。

若要实现后端服务模型,请创建标准用户前端应用程序和后端服务。 在产品安装期间在系统中安装服务。

管理员 COM 对象模型

此处包含此模型,但本文档前面对此进行了详细讨论。 管理 COM 对象模型允许动态管理提升从应用程序或控制面板内执行特定操作。

使用管理员 COM 对象模型的主要好处是,它为用户提供了最佳用户体验。

以下列表详细介绍了使用管理员 COM 对象模型的一些缺点:

  • 开发人员需要最多的工作,因为每个应用程序功能都必须评估和测试管理员功能,并且该功能必须由后端 COM 对象提供。
  • 用户需要提供提升审批。
  • 生成的标准用户应用程序和管理员后端 COM 对象的“单元”现在是“可驱动”的,不受 UIPI 和其他隔离机制的保护。

若要实现管理 COM 对象模型,请创建标准用户前端应用程序并启动提升的后端 COM 对象以执行管理任务。

5. 重新设计应用程序的安装程序

以下最佳做法适用于 Windows Vista 或 UAC 环境中性能良好的应用程序安装。 此列表并不全面。 有关 Windows Vista 徽标要求的更详细说明(包括 UAC 要求),请参阅 Windows Vista 徽标文档和 Windows Vista 徽标指南文档最新草稿的深入版本。

在重新设计应用程序时使用这些要求。

将 Windows Installer 4.0 用于安装包。

以下许多要求已集成到 Windows Installer 引擎中。 将 Windows Installer 用于安装包将帮助你满足以下 Windows Vista 安装要求。

使用版本控制的文件,在安装过程中不要降级文件。

文件版本控制可确保安装完成后的最终安装状态正确。 如果没有文件版本,将需要一些特殊的处理,以确保你的安装在许多不同的安装方案中正常工作。 此外,在安装版本控制文件时,请勿降级版本,尤其是共享文件。 降级版本可能对应用程序有利,但它经常会导致其他应用程序出现问题。 通过在 Windows Installer 包中声明文件的正确版本,Windows Installer 本机支持此功能。

安装应用程序并将每个用户数据存储在不同位置。

应用程序应安装在“程序文件”目录下的文件夹中。 若要对此进行配置,可以使用 Windows Installer 包的 Direcotry 表中的 ProgramFilesFolder 属性,每用户配置数据应存储在 \Users\username\AppData 目录下的文件或 HKCU 根目录下的注册表项中。 用户数据、模板和应用程序创建的文件在 \Users\username 子目录中都有适当的位置。 尽管过去未强制实施,但由于许多用户会使用完全管理员访问令牌运行程序,因此未将信息放置在正确位置的应用程序可能会失败。 当关闭虚拟化时尤其如此。

安装共享组件时,请使用一致的文件夹位置。

应使用 Windows Installer 包的 Directory 表中的 CommonFilesFolder 属性将共享组件安装到 Common Files 目录。 管理共享组件可能会有问题,如果可能,应避免这样做。 未一致安装共享组件的开发人员最终可能会收到指向较旧组件的组件对象模型 (COM) 注册信息。 Windows Installer 合并模块 (MSM) 专为使共享组件能够一致地安装在所有安装共享组件的包的上下文中。 当对共享组件的修改导致现有应用程序失败时,会出现其他问题。 解决此问题的一种方法是使用 Microsoft .NET 或 Win32 版本控制程序集生成应用程序。

如果安装失败,请执行安装程序回滚。

部分安装的软件可能会以奇怪和意外的方式失败,从而提供糟糕的用户体验。 Windows Installer 支持此回滚功能。

不要在用户的配置文件中安装应用程序快捷方式。

虽然将应用程序图标添加到 Windows 中的每个已知曝光点可能很诱人,但通常会导致用户感到他们已失去对计算机的控制。 然后,用户被迫手动删除这些快捷方式,使计算机恢复所需的外观。 如果开发人员想要将图标添加到桌面,请在安装过程中向用户请求权限。 Windows Vista 解决了安装后应用程序以及最近使用的应用程序列表的可发现性,以避免大型“开始”菜单遍历。

避免在用户登录时自动启动后台应用程序。

尽管可以在安装期间将程序添加到启动组或运行密钥,但这会增加系统的开销。 随着时间的推移,用户系统的性能可能会显著降低。 如果应用程序可以从后台任务中受益,请允许它可供用户配置。 此外,通过 HLKM 运行密钥添加启动任务可能会阻止标准用户帐户在将来修改行为。 如果用户希望在登录时启动应用程序,请将信息存储在 HKCU 的运行密钥中。

遵循干净删除逻辑。

用户可能会删除应用程序,不仅可以释放磁盘空间,还可以在安装应用程序之前将计算机返回到其状态。 应用程序的卸载过程应正确且完全删除应用程序。 Windows Installer 默认为以下规则:

  • 所有非共享应用程序文件和文件夹。
  • 引用计数 (引用计数) 为零的共享应用程序文件。
  • 注册表项,可能由其他程序共享的键除外。
  • 应用程序在安装时创建的“开始”菜单中的所有快捷方式。
  • 用户首选项可能被视为用户数据并被抛在后面,但应包含一个完全清除删除的选项。
  • 如果不使用 Windows Installer) ,卸载程序本身 (。

6.使用应用程序创建和嵌入应用程序清单

在 Windows Vista 中,标记应用程序的正确方法是在程序中嵌入应用程序清单,告知操作系统应用程序需要什么。 在 Windows Vista 版本中,有一些规定允许未清单或未签名的代码使用完整的管理访问令牌运行。

注意 在将来的版本中,运行提升的应用程序的唯一方法是具有一个已签名的清单,用于标识应用程序所需的特权级别。

应用程序清单架构

应用程序清单不是 Windows Vista 版本的新增功能。 Windows XP 中使用了清单来帮助应用程序开发人员识别测试应用程序的 DLL 版本等内容。 提供执行级别是该现有清单架构的扩展。

Windows Vista 应用程序清单已使用属性进行了增强,这些属性允许开发人员使用请求的执行级别标记其应用程序。 下面是此格式。

<requestedExecutionLevel
level="asInvoker|highestAvailable|requireAdministrator"
uiAccess="true|false"/>

可能请求的执行级别值

说明 注释
asInvoker 应用程序使用与父进程相同的访问令牌运行。 建议用于标准用户应用程序。 根据本文档中提供的指南,使用内部高程点进行耐火处理。
highestAvailable 应用程序以当前用户可以获取的最高特权运行。 建议用于混合模式应用程序。 计划在将来的版本中对应用程序进行折射。
requireAdministrator 应用程序仅为管理员运行,并要求使用管理员的完整访问令牌启动应用程序。 建议用于仅限管理员的应用程序。 不需要内部高程点。 应用程序已在提升后运行。

注意 仅当托管应用程序支持特定类型的托管应用程序时,托管应用程序才能成为标准用户或仅限管理员的应用程序。 例如,MMC.exe现在仅托管管理单元,Explorer.exe仅托管标准用户代码。

系统行为

应用程序标记 虚拟?
无名
asInvoker
requireAdministrator
highestAvailable

应用程序清单标记和应用程序启动行为

本部分详细介绍了提升提示的行为,具体取决于父进程访问令牌、用户帐户控制:管理员审批模式策略中管理员的提升提示行为和用户帐户控制:标准用户策略的提升提示行为,以及应用程序请求的执行级别标记。

应用程序是否可以运行,以及它可以获取哪些用户权限和管理 Windows 特权取决于应用程序兼容性数据库中应用程序请求的执行级别与启动应用程序的用户帐户可用的管理权限的组合。 下表根据此类可能的组合确定了可能的运行时行为。

本地 Administrators 组成员的应用程序启动行为

父进程访问令牌 本地管理员组成员的同意策略 None 或 asInvoker highestAvailable requireAdministrator
标准用户 无提示 应用程序以标准用户身份启动 应用程序使用完整的管理访问令牌启动;无提示 应用程序使用完整的管理访问令牌启动;无提示
标准用户 提示要求同意 应用程序以标准用户身份启动 应用程序使用完整的管理访问令牌启动;提示同意 应用程序使用完整的管理访问令牌启动;提示同意
标准用户 提示输入凭据 应用程序以标准用户身份启动 应用程序使用完整的管理访问令牌启动;提示输入凭据 应用程序使用完整的管理访问令牌启动;提示输入凭据
) 禁用管理员 (UAC 不适用 应用程序使用完整的管理访问令牌启动;无提示 应用程序使用完整的管理访问令牌启动;无提示 应用程序使用完整的管理访问令牌启动;无提示

标准用户帐户的应用程序启动行为

父进程访问令牌 标准用户的同意策略 asInvoker highestAvailable requireAdministrator
标准用户 无提示 应用程序以标准用户身份启动 应用程序以标准用户身份启动 应用程序无法启动
标准用户 提示输入凭据 应用程序以标准用户身份启动 应用程序以标准用户身份启动 在运行应用程序之前提示输入管理员凭据
) 禁用标准用户 (UAC 不适用 应用程序以标准用户身份启动 应用程序以标准用户身份启动 应用程序可能会启动,但稍后会失败

具有附加权限的标准用户的应用程序启动行为 (例如备份操作员)

父进程访问令牌 标准用户的同意策略 asInvoker highestAvailable requireAdministrator
标准用户 无提示 应用程序以标准用户身份启动 应用程序以具有附加权限的标准用户身份启动 应用程序无法启动
标准用户 提示输入凭据 应用程序以标准用户身份启动 在运行应用程序之前提示输入凭据 在运行应用程序之前提示输入管理员凭据
) 禁用标准用户 (UAC 空值 应用程序以标准用户身份启动 应用程序以具有附加权限的标准用户身份启动 应用程序可能会启动,但稍后会失败

uiAccess 值

可能的 uiAccess 值

说明
False 应用程序不需要将输入驱动到桌面上另一个窗口的用户界面。 未提供辅助功能的应用程序应将此标志设置为 false。 例如,需要将输入驱动到桌面上的其他窗口的应用程序 (屏幕键盘,) 应将此值设置为 true。
True 允许应用程序绕过用户界面控制级别,以将输入驱动到桌面上更高权限的窗口。 此设置应仅用于用户界面辅助技术应用程序。

重要将 uiAccess 标志设置为 true 的应用程序必须经过 Authenticode 签名才能正常启动。 此外,应用程序必须驻留在文件系统中的受保护位置。 \Program Files\ 和 \windows\system32\ 当前是两个允许的受保护位置。

如何使用 Microsoft Visual Studio 创建嵌入清单

Visual Studio 提供在可移植可执行文件 (PE) 映像的资源部分中自动嵌入 XML 清单文件的功能。 本部分介绍如何使用 Visual Studio 创建包含清单的已签名 PE 映像。 因此,此清单可以包含必要的 requestedExecutionLevel 属性,使应用程序能够在 Windows Vista 上以所需的特权级别运行。 启动程序时,将从 PE 的资源部分中提取清单信息,并由操作系统使用。 无需使用 Visual Studio 图形用户界面 (GUI) 来包含清单。 在源代码中完成必要的更改后,使用命令行工具进行编译和链接时,也会在生成的 PE 映像中包含清单。

清单文件

若要标记应用程序,请首先创建要与目标应用程序一起使用的清单文件。 这可以使用任何文本编辑器完成。 清单文件的名称应与扩展名为 .manifest 的target.exe相同,如以下示例所示。

Executable: IsUserAdmin.exe 
Manifest:IsUserAdmin.exe.manifest
Sample application manifest file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <assemblyIdentity version="1.0.0.0"
     processorArchitecture="X86"
     name="IsUserAdmin"
     type="win32"/> 
  <description>Description of your application</description> 
  <!-- Identify the application security requirements. -->
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="requireAdministrator"
          uiAccess="false"/>
        </requestedPrivileges>
       </security>
  </trustInfo>
</assembly>

清单中需要针对应用程序进行调整的部分以粗体标记。 它们包括以下类型:

  • 程序集标识
  • 名称
  • 类型
  • 说明
  • requestedExecutionLevel 中的属性

使用 Visual Studio 2005 for Windows Vista 应用程序在 C/C++ 代码中生成应用程序清单

重要 如果应用程序打算在 Windows Vista 和 Windows XP 上运行,则必须遵循下一部分详述的过程:使用 Microsoft Visual Studio 2005 生成和嵌入适用于 Windows XP 和 Windows Vista 应用程序的清单。

接下来,必须通过在应用程序的资源文件中添加一行,将清单附加到可执行文件, (.rc 文件) 让 Microsoft Visual Studio 在 PE 文件的资源节中嵌入清单。 若要实现此目的,请将清单与要生成的项目的源代码放在同一目录中,然后编辑 .rc 文件以包含以下行。

#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST "IsUserAdmin.exe.manifest"

重新生成应用程序后,清单应嵌入可执行文件的资源部分。

使用 Microsoft Visual Studio 2005 for Windows XP 和 Windows Vista 应用程序生成和嵌入清单

在 Visual Studio 2005 中,C/C++ 集成开发环境 (IDE) 接口,该接口允许在目标可执行文件中包含其他清单文件,对 XML 执行一些处理,这会插入重复的 xmlns 标记。 因此,如果应用程序应在 Windows Vista 和 Windows XP 上运行,则无法使用先前记录的有关如何在 Visual Studio 2005 C++ 项目中包含清单的方法。 修改了以下过程,以在 trustInfo 节中包含显式版本标记。

计划对 mt.exe 工具进行修复,以解决它在 XML 中生成重复命名空间声明的问题。 在新版本的 mt.exe 可用之前,可以通过将版本标记显式添加到清单的 trustinfo 部分来避免合并清单的问题。 示例清单如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
      <ms_asmv2:security>
         <ms_asmv2:requestedPrivileges>
            <ms_asmv2:requestedExecutionLevel level="asInvoker">
            </ms_asmv2:requestedExecutionLevel>
         </ms_asmv2:requestedPrivileges>
      </ms_asmv2:security>
   </ms_asmv2:trustInfo>
</assembly>

C 或 C++ 项目

以下过程详细介绍了如何在 Visual Studio 2005 中为 C 或 C++ 项目类型创建清单。

在 Microsoft Visual Studio 2005 中为 C 或 C++ 项目创建清单

  1. 在 Microsoft Visual Studio 2005 中打开项目
  2. 在“项目”下,选择“ 属性”。
  3. 在“属性”中,选择“ 清单工具”,然后选择“ 输入和输出”。
  4. 在“其他清单文件”下添加应用程序 清单文件的名称。
  5. 重新生成应用程序。

注意 包含显式版本标记的更新清单将允许应用程序在 Windows Vista 和 Windows XP 上正确运行。

托管代码 (C#、J# 和 Visual Basic)

Visual Studio 当前不会将默认清单嵌入托管代码中。 对于托管代码,开发人员只需使用 mt.exe 将默认清单插入目标可执行文件。 步骤如下:

使用 mt.exe将默认清单文件插入目标可执行文件

  1. 使用文本编辑器(如 Windows 记事本)创建默认清单文件 temp.manifest。
  2. 使用 mt.exe 插入清单。 命令将为: mt.exe –manifest temp.manifest –outputresource:YourApp.exe;#1

在 Visual Studio 后期生成中将应用程序清单添加为步骤

也可以作为生成后步骤自动添加应用程序清单。 此选项适用于 C/C++ 和 C# 和 J# 这两种托管代码语言。

注意 IDE 当前不包括 Visual Basic 应用程序的生成后选项。

将以下行作为生成后任务放在 项目属性中

mt.exe -manifest "$(ProjectDir)$(TargetName).exe.manifest" 
-updateresource:"$(TargetDir)$(TargetName).exe;#1"

7. 测试应用程序

测试重新设计的应用程序或新应用程序是否与标准用户分析器兼容。 本文档前面在测试应用程序的 UAC 兼容性部分中介绍了详细说明此过程的过程。

使用以下工作流测试应用程序。

测试应用程序的最终 UAC 兼容性

  1. 使用标准用户分析器工具测试应用程序。
  2. 在管理员审批模式下以管理员身份登录到 Windows Vista 计算机并运行程序。 确保测试所有功能并记下用户体验。 相应地提交任何提升或用户界面 bug。
  3. 以标准用户身份登录到 Windows Vista 计算机并运行程序。 确保测试所有功能,并注意与管理员审批模式用户体验中的管理员相比,标准用户体验中的任何差异或失败。 相应地提交任何提升和用户体验 bug。

8. 验证码对应用程序进行签名

应用程序现在包含清单,将在应用程序启动时检测到该清单和分析信息。 但是,可执行文件可能会被篡改。 为防止出现这种情况,应使用 Authenticode 签名对应用程序进行签名。 请注意,Windows Vista 能够阻止使用完全管理员访问令牌启动任何未签名的应用程序。 如果希望应用程序在锁定环境中正常运行,同时显示用户更友好的用户界面,则应使用 Authenticode 签名对其进行签名。

若要对应用程序进行签名,可以从 makecert.exe 生成证书,或者从商业证书颁发机构之一获取代码签名密钥, (CA) ,例如 VeriSign、Thawte 或 Microsoft CA。

注意 如果应用程序在安装应用程序的客户的目标计算机上受信任,则需要商业证书。

如果使用 makecert.exe 文件生成签名密钥对,请注意,它仅生成 1024 位密钥。 验证码签名应至少为 2048 位密钥。 makecert.exe文件应仅用于测试目的。

以下过程详细介绍了使用 makecert.exe 生成签名密钥对的高级别要求。 示例和makecert.exe参数遵循此过程。

使用makecert.exe生成签名密钥对

  1. 生成证书。
  2. 对代码进行签名。
  3. 安装测试证书。

示例签名过程

以下过程作为示例提供,不应严格遵循。 例如,将测试证书名称替换为证书名称,并确保你定制映射到特定 CA 和开发环境的过程。

步骤 1:生成证书

makecert -r -pe -ss PrivateCertStore -n "CN=Contoso.com(Test)" 
ContosoTest.cer

makecert.exe参数

参数 说明
/r 创建自签名证书
/体育 使证书的私钥可导出到签名计算机。
/ss StoreName 将存储测试证书的证书存储名称。 示例:PrivateCertStore
/n X500Name 证书使用者的 X500 名称。 示例:Contoso.com (测试)
CertificateName.cer 证书名称。 示例:ContosoTest.cer

步骤 2:对代码进行签名

Signtool sign /v /s PrivateCertStore /n Contoso.com(Test) /t 
http://timestamp.verisign.com/scripts/timestamp.dll file.exe

步骤 3:安装测试证书

安装测试证书

  1. 右键单击“ 命令提示符 ”并选择“ 以管理员身份运行”,启动提升的命令窗口。
  2. 在命令提示符中,键入 mmc.exe ,然后按 Enter
  3. 在 mmc 中,选择“ 文件 ”,然后选择“ 添加/删除管理单元...”
  4. 在“添加或删除管理单元”中,选择“ 证书”,单击“ 添加”,然后单击“ 确定”。
  5. 在“证书管理单元”对话框中,选择“ 计算机帐户 ”,然后单击“ 下一步”。
  6. 在“选择计算机”中,选择“ 本地计算机”,然后单击“ 确定”。
  7. 在“添加或删除管理单元”中,单击“ 确定”。
  8. 在“证书”管理单元中,导航到“ 受信任的根证书颁发机构”,右键单击“ 证书”,选择“ 所有任务”,然后选择“ 导入...”
  9. 在证书导入向导中,导入测试证书 ContosoTest.cer。

9. 参与 Windows Vista 徽标计划

Microsoft 提供 Windows Vista 徽标计划,帮助客户识别满足平台功能和质量目标的全面基线定义的系统和外设,以确保为最终用户提供出色的计算体验。

为标准用户部署和修补应用程序

通常,企业必须考虑如何以自动化方式在用户的工作站上安装应用程序,从而降低管理成本。 此问题基本上有两个部分:第一,应如何打包这些应用程序进行部署,第二,应使用哪种技术来部署它们。 对于较小的企业环境,可能不需要可靠的自动化部署机制。

假设企业已经清点了在其环境中运行的软件,下一步是重新打包这些应用程序以供部署。 Microsoft 建议使用 Windows Installer 格式,因为它具有将按用户设置与每计算机设置分开管理的独特功能。 这种类型的管理通常不能用于其他打包格式,尤其是仅由具有更多特权的帐户(如 SYSTEM)运行的部署可执行文件。 MSDN 库包含许多有关 Windows Installer 的文章;一个建议是 Windows Installer 文档路线图

Windows Installer 格式包括用户通过 组策略 (Microsoft IntelliMirror) 以及短信来控制这些应用程序的安装。 若要使用文件扩展名或快捷方式启用按需安装,基于 Windows Installer 的包中的下表必须填充广告数据:快捷方式、扩展名、图标和谓词。 建议还填充类、MIME、ProgID 和 TypeLib。 有关 IntelliMirror 和按需安装的详细信息,请阅读 修补Per-User托管应用程序

还有其他安装程序技术允许应用程序按用户安装并支持自动更新,例如 ClickOnce。 这意味着安装程序不需要管理员或更高的权限进行安装,并且只要计算机连接到网络,用户将始终运行最新版本。 它还对 IT 专业人员控制这些应用程序的安装的能力进行了一些限制。

ClickOnce 部署是一种 Microsoft .NET 安装技术,当用户单击清单链接(例如网站中的清单、CD 上的清单)或通用命名约定 (UNC) 路径时,会自动安装和配置客户端应用程序。 默认情况下,应用程序会将自身复制到“临时 Internet 文件”文件夹,并在受限的环境中运行。

注意 即使应用程序已使用 IT 强名称进行签名,该名称赋予它完全信任,你仍然无法执行任何需要管理员权限的操作,例如访问文件系统和注册表的某些部分。 但是,ClickOnce 应用程序以每用户应用程序为目标,因此这应该不会造成问题。

重要 ClickOnce 不应用于部署执行管理操作的应用程序。

部署到单台计算机

若要为单台计算机部署应用程序,管理员必须在该计算机上“发布”该应用程序。

部署到域中的所有用户

若要为域中的所有用户播发,管理员必须通过组策略部署“发布”应用程序。 目前,只有 Windows Server® 2003 操作系统和 Windows 2000 Server 操作系统基于 组策略 的软件部署组件才能利用此功能。

使用 Windows Installer 4.0 以标准用户身份修补应用程序

标准用户帐户修补使 Windows Installer 包作者能够识别可由未来的标准用户应用的已签名修补程序。 必须满足以下条件才能使用 Windows Installer 4.0 启用标准用户修补:

  • 该应用程序是使用 Windows Installer 4.0 安装的。
  • 该应用程序最初是按计算机安装的。
  • MsiPatchCertificate 表存在于原始窗口安装程序包 (.msi 文件) 中并填充。
  • 这些修补程序已通过 MsiPatchCertificate 表中列出的证书进行数字签名。
  • 可以根据数字签名验证修补程序。
  • 尚未通过设置 MSIDISABLELUAPATCHING 属性或 DisableLUAPatching 策略来禁用标准用户帐户修补。

Windows Installer 4.0 标准用户卸载行为

标准用户应用的 Windows Installer 4.0 修补程序的预期行为是标准用户也可以将其删除。

常见问题故障排除

以下部分详细介绍了 Windows Vista 中应用程序遇到的常见问题。

常见问题包括:

  • ActiveX 安装问题
  • ActiveX 文档未安装
  • 需要应用程序、框架或加载项
  • 安装/修补需要管理权限
  • 每用户应用程序设置位置
  • 应用程序默认保存在受保护的目录中

ActiveX 安装问题

ActiveX 控件必须由管理员安装。 ActiveX 控件通常用于业务线应用程序,以扩展 Web 浏览器功能,以创建更灵活的用户界面,或提升对在 Web 浏览器中运行的应用程序通常被拒绝的计算机资源的访问。 ActiveX 控件通常是通过在网页中嵌入对 ActiveX 控件的引用来安装的。 如果本地计算机上不存在控件,这将导致 Microsoft Internet Explorer 下载并安装该控件。 通常,以这种方式下载的 ActiveX 控件驻留在 %HOMEPATH%\Local Settings\Temporary Internet Files 目录中,该目录可由标准用户写入。 但是,若要在 Internet Explorer 中运行,控件必须具有多个注册表项,这是非管理员无法实现的。

解决方法

从应用程序中删除 ActiveX 控件几乎总是会导致功能丢失。 因此,除非 ActiveX 控件提供一些不属于网站核心功能的视觉或功能增强功能,否则不建议进行修正。 例如,非股票相关门户上的股票代码。

在大多数情况下,打包 ActiveX 控件以便通过短信或组策略进行安装是正确的解决方案。 但是,大多数控件不会包含在基本映像中,因此网站必须修改其页面才能正常失败。 这应包括检测缺少的 ActiveX 控件和重定向到托管桌面软件请求页。

ActiveX 文档未安装

ActiveX 文档是 Microsoft Visual Basic 4 和 Microsoft Visual Basic 5 中已弃用的技术。 可以像 ActiveX 控件一样下载它们。

解决方法

由于 Visual Basic 4 和 Visual Basic 5 已弃用,Microsoft 建议你替换该应用程序。 应该可以在客户端安装过程中安装 ActiveX 文档;但是,如果不通过短信或组策略重新部署,文档的更新将受到限制。

应用程序、框架或外接程序必需

许多应用程序依赖于其他软件,这些软件在默认情况下可能未安装,因为它们在计算机上已可用,或者其他应用程序不提供可供第三方使用的可分发二进制文件。 在正常情况下,系统会指示用户获取并安装其他软件。 在托管桌面下,无法安装。 示例包括 Adobe Acrobat、Microsoft Office、Office Web 组件、WinZip 和 IT Microsoft .NET 安全策略。

解决方法

确定依赖项后,可以将其与基础映像一起打包,或通过按需 SMS 安装提供。 应用程序可能必须更改通知最终用户缺少的软件的方式,将用户定向到短信安装站点而不是制造商。

安装/修补需要管理权限

由于程序安装需要将文件添加到程序文件,因此它始终需要管理权限,因此必须以具有提升权限的用户身份运行。

注意还可以将带有短信或组策略的修补程序与“添加或删除程序” (ARP) 控制面板一起“推送”。 用户选择要安装的软件,系统安装程序执行其余操作-用户不必是管理员。 对于初始安装,可以通过打包软件以推送安装代理来处理此问题。但是,某些应用程序依赖于频繁的自动更新,这些更新可能与集中管理的应用程序模型不一致。

检测更新并尝试自行修补的应用程序将无法这样做,因为它们将无权修改系统目录中的文件。

解决方法

  • 打包应用程序/修补程序,以便通过短信进行部署。 只要应用程序无需) 管理权限即可检测到升级 (可用,并且可以重定向到预配站点。
  • 询问应用程序是否需要提升的计算机权限,例如文件系统、注册表访问或 COM 互操作性。 如果没有,则可以将应用程序重写为 ClickOnce 部署包,该包将在 Microsoft .NET 沙盒中运行。
  • 转换为没有任何客户端依赖项的 Web 应用程序。

Per-User应用程序设置位置

对于 Windows Vista,需要在运行时更改的应用程序设置应存储在以下位置之一:

  • CSIDL_APPDATA
  • CSIDL_LOCAL_APPDATA
  • CSIDL_COMMON_APPDATA

用户保存的文档应存储在CSIDL_MYDOCUMENTS中。

注意 用户的 Documents 文件夹不再存储在 “文档”和“设置”下。 在 Windows Vista 中,文件系统上名为 “用户” 的新根目录现在包含计算机用户的配置文件。

由于这些目录已更改,因此建议开发人员使用 CSIDL 以独立于系统的方式查找特定已知目录的路径。 有关详细信息,请参阅 CSIDL

应用程序需要对文件系统具有写入访问权限。 在托管桌面下运行时,应用程序仅对以下文件夹及其子级具有写入权限。

  • CSIDL_PROFILE
  • CSIDL_COMMON_APPDATA

注意 标准用户无法写入 Users\Common。

  • C:\Users\Common>cd “Application Data”
    • C:\Users\Common\Application Data>echo File > File.txt
    • C:\Users\Common\Application Data>

应用程序不应尝试写入其他位置,如下所示:

  • C:\Windows。
  • C:\Windows\System32。
  • Program Files\{application}。
  • C:\{application}。

注意 如果用户创建了文件夹,则此操作将有效,默认情况下,“用户”组的成员可以执行此操作。

不允许应用程序尝试专门创建 C:\Users\Profiles\{user},因为用户只能在 C:\Users\{user} 下创建文件夹。 根据 Microsoft 在以前版本的操作系统上存储 Documents 文件夹的位置,所选位置似乎会混淆。

需要在运行时更改的应用程序设置应存储在以下位置之一:

  • CSIDL_APPDATA
  • CSIDL_LOCAL_APPDATA
  • CSIDL_COMMON_APPDATA

用户保存的文档应存储在 CSIDL_MYDOCUMENTS 文件夹中。

所有路径不应进行硬编码,但应使用 Environment.GetFolderPath () 函数。

应用程序默认在受保护的目录中保存

某些应用程序允许用户保存数据或将数据导出到其本地计算机。 通常,对话框默认为 C:\等位置,标准用户对其没有写入权限。 此外,当编写文件的代码失败时,某些应用程序响应不佳,因为操作系统拒绝了访问。

解决方法

假设用户只能写入自己的配置文件。 对于用户有意保存的文档,初始化对话框以在 Documents (Environment.GetFolderPath (Environment.SpecialFolder.Personal) 中启动。 请记住, “保存 ”对话框将允许用户浏览到除用户配置文件以外的其他位置,因此应用程序应包含逻辑,以确保如果用户选择的目录与位于其配置文件中的目录不同的目录,则该应用程序会正常失败。

参考

本部分包括虚拟化参考和安全设置参考。

虚拟化参考

文件虚拟化

  • 虚拟化 (%SYSTEMROOT%,%PROGRAMDATA%,%PROGRAMFILES%\ (子目录)
  • 重定向到:%LOCALAPPDATA%\VirtualStore
  • 排除的二进制可执行文件:.exe、.dll、.sys

注册表虚拟化:

  • 虚拟化 (HKLM\SOFTWARE)
  • 重定向到:HKCU\Software\Classes\VirtualStore\MACHINE\SOFTWARE\<Application Registry Keys>
  • 从虚拟化中排除的密钥
  • HKLM\Software\Classes
  • HKLM\Software\Microsoft\Windows
  • HKLM\Software\Microsoft\Windows NT

适用范围

  • 虚拟存储不漫游
  • 相应的全局对象不会漫游
  • 仅对交互式标准用户启用
  • 为非交互式进程禁用
  • 为 64 位可执行文件禁用
  • 对于在其应用程序清单(用于分离的模型) (requestedExecutionLevel) 请求执行级别的可执行文件禁用
  • 为内核模式和模拟调用方禁用
  • 仅虚拟化管理员可写注册表项和文件

UAC 安全设置参考

此参考详细介绍了可用于使用组策略或计算机本地安全策略管理 UAC 的安全设置。

注意 本节中介绍的过程用于管理非托管计算机。 若要使用 组策略 在托管环境中集中管理设置,请使用 Active Directory 用户和计算机 (dsa.msc) ,而不是本地安全策略管理器管理单元 (secpol.msc) 。

配置 UAC 安全设置

以下过程详细介绍了如何使用安全策略管理器配置 UAC 安全设置。 该过程详细介绍了管理员审批模式下管理员的默认用户体验。

使用安全策略管理器查看/设置 UAC 安全设置

  1. 单击“ 开始” 按钮,在搜索框中键入 secpol.msc ,然后按 Enter
  2. 在“用户帐户控制”同意提示符下,单击“ 继续”。
  3. 在“本地安全设置”中,展开“ 本地策略”,然后单击“ 安全选项”。
  4. 右键单击要更改的安全设置,然后选择“ 属性”。

以下过程详细介绍了如何使用 组策略配置 UAC 安全设置。 该过程详细介绍了管理员审批模式下管理员的默认用户体验。

使用组策略对象编辑器查看/设置 UAC 安全设置

  1. 单击“ 开始” 按钮,在搜索框中键入 gpedit.msc ,然后按 Enter
  2. 在“用户帐户控制”同意提示符下,单击“ 继续”。
  3. 在“组策略”中,展开“用户配置”,然后展开“安全选项”。
  4. 右键单击要更改的安全设置,然后选择“ 属性”。

UAC 安全设置

下表列出了可配置的 UAC 安全设置。 可以使用安全策略管理器 (secpol.msc) 配置这些设置,也可以使用 组策略 (gpedit.msc) 集中管理这些设置。

UAC 安全设置

设置 说明 默认值
用户帐户控制:管理员内置管理员帐户的审批模式。 有两种可能的设置:
  • 已启用:内置管理员将在管理员审批模式下以管理员身份运行。
  • 已禁用:管理员使用完整的管理员访问令牌运行。
  • 对于新安装和升级禁用,其中内置管理员不是计算机上唯一的本地活动管理员。 默认情况下,内置管理员帐户在已加入域的计算机上安装和升级时处于禁用状态。
  • 当 Windows Vista 确定内置管理员帐户是计算机上唯一的活动本地管理员时,为升级启用。 如果 Windows Vista 确定这一点,则升级后,内置管理员帐户也会保持启用状态。 默认情况下,内置管理员帐户在已加入域的计算机上安装和升级时处于禁用状态。
用户帐户控制: 管理员批准模式中管理员的提升提示行为 此设置有三个可能的值:
  • 在不提示的情况下提升:以无提示方式提升。
  • 提示输入凭据:要求用户在继续之前输入其登录密码。
  • 同意提示:在提升之前请求用户批准。 这是默认设置。 

此设置确定在运行具有更高权限的程序之前如何提示用户。 此策略仅在启用 UAC 时生效。

提示要求同意
用户帐户控制: 标准用户的提升提示行为 确定在运行具有更高权限的程序之前如何提示用户。 此策略仅在启用 UAC 时生效。 以下是此设置的可用配置选项:
  • 自动拒绝提升请求:当应用程序想要执行管理任务时,系统不会提示用户。 应用程序将无法启动,并向用户显示访问被拒绝或等效的错误。
  • 提示输入凭据:要求用户在继续之前输入其登录密码。
提示输入凭据
用户帐户控制: 检测应用程序安装并提示提升 有两个可能的值:
  • 已启用:当 Windows Vista 检测到安装程序时,系统会提示用户提供同意或凭据。
  • 已禁用:应用程序安装将以非确定性方式以无提示方式失败或失败。
已启用
用户帐户控制: 只提升签名并验证的可执行文件 有两个可能的值:
  • 已启用:仅运行已签名的可执行文件。 此设置阻止运行未签名的应用程序。
  • 已禁用:将运行已签名和未签名的代码。
已禁用
用户帐户控制: 仅提升安装在安全位置的 UIAccess 应用程序 有两个可能的值:
  • 已启用:系统仅向从 %ProgramFiles% 或 %windir% 下启动的可执行文件授予 UIAccess 权限和用户权限。 这些目录上的 ACL 可确保可执行文件不是用户可修改 (否则会允许特权提升) 。 从其他位置启动的 UIAccess 可执行文件将启动, (即将运行“asInvoker”) 。
  • 已禁用:位置检查未完成,因此在用户批准后,所有 UIAccess 应用程序都将使用用户的完全访问令牌启动。
已启用
用户帐户控制: 以管理员批准模式运行所有管理员 有两个可能的值:
  • 已启用:尝试执行管理操作时,将提示管理员和标准用户。 提示样式取决于策略。
  • 已禁用:UAC 实质上是“关闭”,并且自动启动 AIS 服务处于禁用状态。
已启用
用户帐户控制: 提示提升时切换到安全桌面 有两个可能的值:
  • 已启用:在安全桌面上显示 UAC 提升提示。 安全桌面只能从 Windows 进程接收消息,这消除了来自恶意软件的消息。 因此,无法在安全桌面上欺骗同意和凭据提示。
  • 已禁用:UAC 提升提示显示在用户桌面上。
已启用
用户帐户控制: 将文件和注册表写入错误虚拟化到每用户位置 有两个可能的值:
  • 已启用:应用程序清单中缺少应用程序兼容性数据库条目或请求的执行级别标记不符合 UAC。 使用不符合的软件的环境应保持启用此设置。
  • 已禁用:符合 UAC 的应用程序不应写入到保护区并导致写入失败。 因此,仅使用符合 UAC 的应用程序的环境应禁用此设置。 如果禁用此设置,尝试写入 Program Files 和 %systemroot% 的不符合应用程序将以无提示方式失败。
已启用

注意 在大多数情况下,不建议 使用“不提示提升 ”选项。 在不提示的情况下提升将允许以标准用户身份运行的应用程序在未经用户同意的情况下启动管理应用程序,并有效地绕过 UAC。

任务计划程序代码示例

以下 C++ 代码示例演示如何使用任务计划程序为用户执行提升。 因此,应用程序可以在登录期间使用管理员凭据自动提升,并且 Windows Vista 不会阻止该应用程序。 必须在设置过程中为应用程序创建管理员用户,以便此解决方案正常工作。

//---------------------------------------------------------------------
//  This file is part of the Microsoft .NET Framework SDK Code Samples.
// 
//  Copyright (C) Microsoft Corporation.  All rights reserved.
// 
//This source code is intended only as a supplement to Microsoft
//Development Tools and/or on-line documentation.  See these other
//materials for detailed information regarding Microsoft code samples.
// 
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------

/****************************************************************************
* Main.cpp - Sample application for Task Scheduler V2 COMAPI                * Component: Task Scheduler                          
* Copyright (c) 2002 - 2003, Microsoft Corporation 
* This sample creates a task to at the time registered to start the desired task.                                                             *
****************************************************************************/
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <comdef.h>
#include <comutil.h>
//Include Task header files - Included in Windows Vista Beta-2 SDK from MSDN
#include <taskschd.h>
#include <conio.h>
#include <iostream>
#include <time.h>

using namespace std;

#define CLEANUP \
pRootFolder->Release();\
        pTask->Release();\
        CoUninitialize();

HRESULT CreateMyTask(LPCWSTR, wstring);

void __cdecl wmain(int argc, wchar_t** argv)
{
wstring wstrExecutablePath;
WCHAR taskName[20];
HRESULT result;

if( argc < 2 )
{
printf("\nUsage: LaunchApp yourapp.exe" );
return;
}

// Pick random number for task name
srand((unsigned int) time(NULL));
wsprintf((LPWSTR)taskName, L"Launch %d", rand());

wstrExecutablePath = argv[1];

result = CreateMyTask(taskName, wstrExecutablePath);
printf("\nReturn status:%d\n", result);

}
HRESULT CreateMyTask(LPCWSTR wszTaskName, wstring wstrExecutablePath)
{
    //  ------------------------------------------------------
    //  Initialize COM.
TASK_STATE taskState;
int i;
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if( FAILED(hr) )
    {
        printf("\nCoInitializeEx failed: %x", hr );
        return 1;
    }

    //  Set general COM security levels.
    hr = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        0,
        NULL);

    if( FAILED(hr) )
    {
        printf("\nCoInitializeSecurity failed: %x", hr );
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Create an instance of the Task Service. 
    ITaskService *pService = NULL;
    hr = CreateElevatedComObject( CLSID_TaskScheduler,
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           IID_ITaskService,
                           (void**)&pService );  
    if (FAILED(hr))
    {
        printf("Failed to CoCreate an instance of the TaskService class: %x", hr);
        CoUninitialize();
        return 1;
    }
        
    //  Connect to the task service.
    hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
    if( FAILED(hr) )
    {
        printf("ITaskService::Connect failed: %x", hr );
        pService->Release();
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Get the pointer to the root task folder.  This folder will hold the
    //  new task that is registered.
    ITaskFolder *pRootFolder = NULL;
    hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
    if( FAILED(hr) )
    {
        printf("Cannot get Root Folder pointer: %x", hr );
        pService->Release();
        CoUninitialize();
        return 1;
    }
    
    //  Check if the same task already exists. If the same task exists, remove it.
    hr = pRootFolder->DeleteTask( _bstr_t( wszTaskName), 0  );
    
    //  Create the task builder object to create the task.
    ITaskDefinition *pTask = NULL;
    hr = pService->NewTask( 0, &pTask );

    pService->Release();  // COM clean up.  Pointer is no longer used.
    if (FAILED(hr))
    {
        printf("Failed to CoCreate an instance of the TaskService class: %x", hr);
        pRootFolder->Release();
        CoUninitialize();
        return 1;
    }
        

    //  ------------------------------------------------------
    //  Get the trigger collection to insert the registration trigger.
    ITriggerCollection *pTriggerCollection = NULL;
    hr = pTask->get_Triggers( &pTriggerCollection );
    if( FAILED(hr) )
    {
        printf("\nCannot get trigger collection: %x", hr );
CLEANUP
        return 1;
    }
  
    //  Add the registration trigger to the task.
    ITrigger *pTrigger = NULL;
    
    hr = pTriggerCollection->Create( TASK_TRIGGER_REGISTRATION, &pTrigger );     
    pTriggerCollection->Release();  // COM clean up.  Pointer is no longer used.
    if( FAILED(hr) )
    {
        printf("\nCannot add registration trigger to the Task %x", hr );
        CLEANUP
        return 1;
    }
    pTrigger->Release();

    //  ------------------------------------------------------
    //  Add an Action to the task.     
    IExecAction *pExecAction = NULL;
    IActionCollection *pActionCollection = NULL;

    //  Get the task action collection pointer.
    hr = pTask->get_Actions( &pActionCollection );
    if( FAILED(hr) )
    {
        printf("\nCannot get Task collection pointer: %x", hr );
        CLEANUP
        return 1;
    }
    
    //  Create the action, specifying that it is an executable action.
    IAction *pAction = NULL;
    hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction );
    pActionCollection->Release();  // COM clean up.  Pointer is no longer used.
    if( FAILED(hr) )
    {
        printf("\npActionCollection->Create failed: %x", hr );
        CLEANUP
        return 1;
    }

    hr = pAction->QueryInterface( IID_IExecAction, (void**) &pExecAction );
    pAction->Release();
    if( FAILED(hr) )
    {
        printf("\npAction->QueryInterface failed: %x", hr );
        CLEANUP
        return 1;
    }

    //  Set the path of the executable to the user supplied executable.
hr = pExecAction->put_Path( _bstr_t( wstrExecutablePath.c_str() ) );  

//hr = pExecAction->put_Path( (BSTR)wstrExecutablePath );  
    if( FAILED(hr) )
    {
        printf("\nCannot set path of executable: %x", hr );
        pExecAction->Release();
        CLEANUP
        return 1;
    }
    hr = pExecAction->put_Arguments( _bstr_t( L"" ) );  
//    hr = pExecAction->put_Arguments( _bstr_t( L"ArgumentsToYourExecutable--HelpFileToOpen" ) );  
   if( FAILED(hr) )
    {
        printf("\nCannot set arguments of executable: %x", hr );
        pExecAction->Release();
        CLEANUP
        return 1;
    }
    
    //  ------------------------------------------------------
    //  Save the task in the root folder.
    IRegisteredTask *pRegisteredTask = NULL;
    hr = pRootFolder->RegisterTaskDefinition(
            _bstr_t( wszTaskName ),
            pTask,
        TASK_CREATE, 
_variant_t(_bstr_t( L"S-1-5-32-545")),//Well Known SID for \\Builtin\Users group
_variant_t(), 
TASK_LOGON_GROUP,
            _variant_t(L""),
            &pRegisteredTask);
    if( FAILED(hr) )
    {
        printf("\nError saving the Task : %x", hr );
        CLEANUP
        return 1;
    }
    printf("\n Success! Task successfully registered. " );
for (i=0; i<100; i++)//give 10 seconds for the task to start
{
pRegisteredTask->get_State(&taskState);
if (taskState == TASK_STATE_RUNNING)
{
printf("\nTask is running\n");
break;
}
Sleep(100);
}
if (i>= 100) printf("Task didn't start\n");

//Delete the task when done
    hr = pRootFolder->DeleteTask(
            _bstr_t( wszTaskName ),
            NULL);
    if( FAILED(hr) )
    {
        printf("\nError deleting the Task : %x", hr );
        CLEANUP
        return 1;
    }

    printf("\n Success! Task successfully deleted. " );

//  Clean up.
    CLEANUP
    CoUninitialize();
    return 0;
}