Shell 启动程序

使用 Shell 启动器,可以将展台设备配置为使用几乎任何应用程序或可执行文件作为自定义 shell。 你指定的应用程序将替换通常在用户登录时运行的默认 Shell (explorer.exe)。

还可以配置 Shell 启动程序来为不同的用户或用户组启动不同的 Shell 应用程序。

可用作自定义 Shell 的应用程序和可执行文件有一些例外:

  • 不能将以下可执行文件用作自定义 shell: C:\\Windows\\System32\\Eshell.exe。 将 Eshell.exe 用作默认 Shell 将导致用户登录后黑屏。
  • 不能将通用 Windows 应用用作自定义 shell。
  • 不能使用自定义 shell 启动通用 Windows 应用,例如“设置”应用。
  • 不能使用启动其他进程并作为自定义 shell 退出的应用程序。 例如,无法在 Shell 启动器中指定 write.exe 。 Shell 启动程序启动自定义 shell 并监视进程以识别自定义 shell 何时退出。 Write.exe 创建 32 位 wordpad.exe 进程并退出。 由于 Shell 启动程序不知道新创建的wordpad.exe进程,因此 Shell 启动程序会根据 Write.exe的退出代码执行操作,然后重启自定义 shell。
  • 无法阻止系统关闭。 对于 Shell 启动器 V1 和 V2,不能通过在图形应用程序中收到WM_QUERYENDSESSION消息时返回 FALSE 或在通过控制台应用程序中的 SetConsoleCtrlHandler 函数添加的处理程序例程中返回 FALSE 来阻止会话结束。

注意

不能同时在同一系统上配置 Shell 启动程序和已分配的访问权限。

使用 Shell 启动程序 V2,可以将通用 Windows 应用指定为自定义 shell。 查看“使用 Shell 启动程序创建 Windows 10 信息亭”,了解 Shell 启动程序 v1 和 Shell 启动程序 V2 之间的差异。

ShellLauncher 在启动自定义 Shell 之前处理 Run 和 RunOnce 注册表项,因此你的自定义 Shell 无需处理其他应用程序和服务的自动启动。

当你的自定义 Shell 退出时,Shell 启动程序也会处理系统的行为。 如果默认行为不能满足你的需求,则可以配置 shell 退出行为。

除了使用 Shell 启动程序之外,还可以使用控制访问其他桌面应用程序和系统组件的方法,例如组策略AppLocker移动设备管理

注意

在 Windows 10 中提供的 Shell 启动程序 v1 中,只能将 Windows 桌面应用程序指定为替代 Shell。 在 Windows 10 版本 1809 及更高版本中提供的 Shell 启动程序 v2 中,还可以将 UWP 应用指定为替代 Shell。

若要在版本 1809 中使用 Shell 启动程序 v2,需要安装 KB4551853 更新

Shell 启动程序 v1 与 Shell 启动程序 v2 之间的差异

Shell 启动程序 v1 将默认 Shell explorer.exe 替换为 eshell.exe,它可以启动 Windows 应用程序。 Shell 启动程序 v2将 explorer.exe 替换为 customshellhost.exe。 这个新的可执行文件可以启动 Windows 桌面应用程序或 UWP 应用。 除了允许你将 UWP 应用用于替换 shell 之外,Shell 启动器 v2 还提供了更多增强功能:

  • 可以使用自定义 Windows 桌面应用程序,然后可以启动 UWP 应用,例如触摸键盘。
  • 从自定义 UWP Shell 中,可以启动辅助视图并在多个监视器上运行。
  • 自定义 Shell 应用全屏运行,并可以按用户需求全屏运行其他应用。 有关不同应用组合的示例 XML 配置,请参阅 Shell 启动程序 v2 示例

要求

Windows 10 企业版或 Windows 10 教育版。

术语

  • 打开,启用:使设置可用于设备并可选择性地将设置应用到设备。
  • 配置: 自定义设置或子设置。
  • 嵌入式 Shell 启动程序:此功能在 Windows 10 版本 1511 中称为嵌入式 Shell 启动程序。
  • 自定义 Shell 启动程序:此功能在 Windows 10 版本 1607 及更高版本中称为 Shell 启动程序。

打开 Shell 启动程序

Shell 启动器是一个可选组件,在 Windows 10 中默认未打开。 在配置之前,必须打开它。 如果尚未安装 Microsoft Windows,可以在自定义Windows 10映像 (.wim) 中打开和配置 Shell 启动程序。 如果已安装 Windows,则必须在应用预配包来配置 Shell 启动程序之前打开 Shell 启动程序。

使用控制面板启用 Shell 启动程序

  1. “搜索 Web 和 Windows ”字段中,键入 “程序和功能” ,然后按 Enter 或点击或选择“ 程序和功能 ”将其打开。
  2. “程序和功能 ”窗口中,选择“ 打开或关闭 Windows 功能”。
  3. “Windows 功能 ”窗口中,展开“ 设备锁定 ”节点,选中或清除 Shell 启动器的复选框,然后选择“ 确定”。
  4. “Windows 功能”窗口指示 Windows 正在搜索所需的文件并显示进度条。 找到后,该窗口指示 Windows 正在应用更改。 完成后,窗口会指示请求的更改已完成。
  5. 选择“ 关闭 ”以关闭 “Windows 功能 ”窗口。

注意

开启 Shell 启动程序不需要重启设备。

通过调用 WESL_UserSetting 启用 Shell 启动程序

  1. 通过调用 Windows Management Instrumentation (WMI) 类 WESL_UserSetting 中的 WESL_UserSetting.SetEnabled 函数启用或禁用 Shell 启动程序。
  2. 如果使用 WESL_UserSetting 启用或禁用 Shell 启动程序,则更改不会影响当前登录的任何会话;必须注销并重新登录。

此示例使用名为 install.wim 的 Windows映像,但你可以使用相同的过程来应用预配包(有关 DISM 的更多信息,请参阅什么是部署映像服务和管理

使用 DISM 启用 Shell 启动程序

  1. 使用管理员特权打开命令提示符。

  2. 在以下步骤中,将 install.wim 复制到硬盘驱动器上的临时文件夹 (,我们假设它名为 C:\wim) 。

  3. 创建新目录。

    md c:\wim
    
  4. 装载映像

    dism /mount-wim /wimfile:c:\bootmedia\sources\install.wim /index:1 /MountDir:c:\wim
    
  5. 启用该功能。

    dism /image:c:\wim /enable-feature /all /featureName:Client-EmbeddedShellLauncher
    
  6. 提交更改。

    dism /unmount-wim /MountDir:c:\wim /Commit
    

使用 Windows 配置设计器启用 Shell 启动程序

Shell 启动程序设置也可用作 Windows 配置设置,因此你可以将这些设置配置为在映像运行时应用。 你可以设置一个或所有 Shell 启动程序设置,方法是使用 Windows 配置设计器创建预配包,然后在映像部署时间或运行时应用预配包。 如果尚未安装 Windows,并且你正在使用 Windows 配置Designer创建安装媒体,并且映像中包含 Shell 启动程序设置,或者在安装过程中应用预配包,则必须在安装介质上使用 DISM 启用 Shell 启动程序,才能成功应用预配包。

使用以下步骤创建包含 ShellLauncher 设置的预配包。

  1. 按照为 Windows 10 创建预配包中的说明,在 Windows 配置设计器中生成一个预配包。
  2. 在“可用的自定义项”页中,选择“运行时设置”>“SMISettings”>“ShellLauncher”。
  3. 将“Enable”的值设置为“ENABLE”。 将显示更多用于配置 Shell 启动器的选项,你可以根据需要设置值。
  4. 完成设置配置并创建预配包后,可以将包应用于映像部署时间或运行时。 有关详细信息,请参阅应用预配包。 将包应用于Windows 10 企业版映像的过程是相同的。

配置 Shell 启动器

有两种方法可以配置 Shell 启动程序:

  1. 在 Windows 10 版本 1803 中,你可以使用分配的访问配置服务提供程序 (CSP) 的 ShellLauncher 节点来配置 Shell 启动程序。 有关详细信息,请参阅 AssignedAccess CSP。 使用此方法配置 Shell 启动程序也会在设备上自动启用 Shell 启动程序(如果设备支持)。
  2. 直接在 PowerShell 脚本或应用程序中使用 Shell 启动程序 WMI 提供程序。

可以为 Shell 启动程序配置以下选项:

  • 启用或禁用 Shell 启动程序。
  • 为特定用户或组指定 Shell 配置。
  • 为特定用户或组删除 Shell 配置。
  • 更改默认 Shell 配置。
  • 获取有关特定用户或组的 Shell 配置的信息。

在用户登录之前,任何更改才会生效。

为不同的用户帐户启动不同的 Shell

默认情况下,Shell 启动程序运行默认 Shell,该 Shell 是在设计时创建操作系统映像时指定的。 默认 Shell 设置为 Cmd.exe,但可以将任何可执行文件指定为默认外壳程序。

如果不想运行默认 shell,可以将 Shell 启动器配置为为特定用户或组启动不同的 shell。 例如,可以将设备配置为为来宾帐户运行自定义应用程序 shell,但为管理员帐户运行标准的 Windows 资源管理器 shell,以便为设备提供服务。

如果在运行时使用 WMI 提供程序为用户或组配置 Shell 启动程序,则必须使用该用户或组的安全标识符 (SID) ;不能使用用户名或组名称。

有关常见安全标识符的详细信息,请参阅众所周知的 SID

如果当前登录的帐户属于两个或多个组,并且每个组定义了不同的配置,则 Shell 启动程序将使用找到的第一个配置。 未定义搜索顺序,因此建议避免将用户分配到具有不同 Shell 启动器配置的多个组。

在 Shell 退出时执行一个操作

当自定义 Shell 退出时,Shell 启动程序可以执行以下四项操作之一:

操作 说明
0 重启 shell。
1 重启设备。
2 关闭设备。
3 不执行任何操作。

重要

请确保 Shell 应用程序不会自动退出,也不会被任何功能(如对话框筛选器)自动关闭,因为这可能会导致无限循环退出和重新启动,除非返回代码操作设置为不执行任何操作。

默认返回代码操作

可以使用 DefaultReturnCodeAction 设置为 Shell 启动程序定义默认返回代码操作。 如果不更改初始值,则默认返回代码操作将设置为 0 (零) ,这指示 Shell 启动程序在 shell 退出时重启 shell。

将退出代码映射到 Shell 启动程序操作

Shell 启动程序可以根据 Shell 返回的退出代码执行特定操作。 对于 Shell 返回的任何给定退出代码,可以配置 Shell 启动程序执行的操作,方法是将该退出代码映射到其中一个 Shell退出操作。

如果退出代码与定义的值不匹配,Shell 启动程序将执行默认的返回代码操作。

例如,你的 Shell 可能会返回 -1、0、1 或 255 的退出代码值,具体取决于 Shell的退出方式。 你可以将 Shell 启动程序配置为:

  • 当 Shell 返回值为 -1 的退出代码时,重新启动设备 (1)
  • 当 Shell 返回值为 0 的退出代码时,重新启动 Shell (0)
  • 当 Shell 返回值为 1 的退出代码时,不执行任何操作 (3)
  • 当 Shell 返回值为 255 的退出代码时,关闭设备 (2)

自定义返回代码操作映射将如下所示:

退出代码 操作
-1 1(重新启动设备)
0 0(重新启动 shell)
1 3(不执行任何操作)
255 2(关闭设备)

设置自定义 Shell

根据需要修改以下 PowerShell 脚本并在设备上运行该脚本。

# Check if shell launcher license is enabled
function Check-ShellLauncherLicenseEnabled
{
    [string]$source = @"
using System;
using System.Runtime.InteropServices;

static class CheckShellLauncherLicense
{
    const int S_OK = 0;

    public static bool IsShellLauncherLicenseEnabled()
    {
        int enabled = 0;

        if (NativeMethods.SLGetWindowsInformationDWORD("EmbeddedFeature-ShellLauncher-Enabled", out enabled) != S_OK) {
            enabled = 0;
        }
        return (enabled != 0);
    }

    static class NativeMethods
    {
        [DllImport("Slc.dll")]
        internal static extern int SLGetWindowsInformationDWORD([MarshalAs(UnmanagedType.LPWStr)]string valueName, out int value);
    }

}
"@

    $type = Add-Type -TypeDefinition $source -PassThru

    return $type[0]::IsShellLauncherLicenseEnabled()
}

[bool]$result = $false

$result = Check-ShellLauncherLicenseEnabled
"`nShell Launcher license enabled is set to " + $result
if (-not($result))
{
    "`nThis device doesn't have required license to use Shell Launcher"
    exit
}

$COMPUTER = "localhost"
$NAMESPACE = "root\standardcimv2\embedded"

# Create a handle to the class instance so we can call the static methods.
try {
    $ShellLauncherClass = [wmiclass]"\\$COMPUTER\${NAMESPACE}:WESL_UserSetting"
    } catch [Exception] {
    write-host $_.Exception.Message; 
    write-host "Make sure Shell Launcher feature is enabled"
    exit
    }


# This well-known security identifier (SID) corresponds to the BUILTIN\Administrators group.

$Admins_SID = "S-1-5-32-544"

# Create a function to retrieve the SID for a user account on a machine.

function Get-UsernameSID($AccountName) {

    $NTUserObject = New-Object System.Security.Principal.NTAccount($AccountName)
    $NTUserSID = $NTUserObject.Translate([System.Security.Principal.SecurityIdentifier])

    return $NTUserSID.Value
}

# Get the SID for a user account named "Cashier". Rename "Cashier" to an existing account on your system to test this script.

$Cashier_SID = Get-UsernameSID("Cashier")

# Define actions to take when the shell program exits.

$restart_shell = 0
$restart_device = 1
$shutdown_device = 2
$do_nothing = 3

# Examples. You can change these examples to use the program that you want to use as the shell.

# This example sets the command prompt as the default shell, and restarts the device if the command prompt is closed. 

$ShellLauncherClass.SetDefaultShell("cmd.exe", $restart_device)

# Display the default shell to verify that it was added correctly.

$DefaultShellObject = $ShellLauncherClass.GetDefaultShell()

"`nDefault Shell is set to " + $DefaultShellObject.Shell + " and the default action is set to " + $DefaultShellObject.defaultaction

# Set Internet Explorer as the shell for "Cashier", and restart the machine if Internet Explorer is closed.

$ShellLauncherClass.SetCustomShell($Cashier_SID, "c:\program files\internet explorer\iexplore.exe www.microsoft.com", ($null), ($null), $restart_shell)

# Set Explorer as the shell for administrators.

$ShellLauncherClass.SetCustomShell($Admins_SID, "explorer.exe")

# View all the custom shells defined.

"`nCurrent settings for custom shells:"
Get-WmiObject -namespace $NAMESPACE -computer $COMPUTER -class WESL_UserSetting | Select Sid, Shell, DefaultAction

# Enable Shell Launcher

$ShellLauncherClass.SetEnabled($TRUE)

$IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

"`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

# Remove the new custom shells.

$ShellLauncherClass.RemoveCustomShell($Admins_SID)

$ShellLauncherClass.RemoveCustomShell($Cashier_SID)

# Disable Shell Launcher

$ShellLauncherClass.SetEnabled($FALSE)

$IsShellLauncherEnabled = $ShellLauncherClass.IsEnabled()

"`nEnabled is set to " + $IsShellLauncherEnabled.Enabled

注意

前面的脚本包含多个配置选项的示例,包括删除自定义 shell 和禁用 Shell 启动器。 它不应按原样运行。

Shell 启动程序用户权限

自定义 shell 使用所登录帐户的用户权限的相同级别启动。 这意味着,具有管理员权限的用户可以执行任何需要管理员权限的系统操作,包括启动具有管理员权限的其他应用程序,而没有管理员权限的用户则不能。

警告

如果你的 Shell 应用程序需要管理员权限,从而需要提升权限,并且你的设备上存在用户帐户控制 (UAC),则你必须禁用 UAC,让 Shell 启动程序来启动 Shell 应用程序。