交互式服务

通常,服务是控制台应用程序,设计为在没有图形用户界面 (GUI) 的情况下无人参与运行。 但是,某些服务可能需要偶尔与用户交互。 本页讨论从服务与用户交互的最佳方法。

重要

从 Windows Vista 起,服务无法直接与用户交互。 因此,不应在新代码中使用标题为“使用交互式服务”一节中提到的技术。

 

从服务间接与用户交互

可以使用以下技术在所有受支持的 Windows 版本上通过服务与用户交互:

  • 使用 WTSSendMessage 函数在用户会话中显示一个对话框。

  • 创建单独的隐藏 GUI 应用程序,并使用 CreateProcessAsUser 函数在交互式用户的上下文中运行应用程序。 将 GUI 应用程序设计为通过某种进程间通信方法与服务通信 (IPC) ,例如命名管道。 该服务与 GUI 应用程序通信,以告知它何时显示 GUI。 应用程序将用户交互的结果传达回服务,以便服务可以采取适当的操作。 请注意,除非使用适当的访问控制列表 (ACL) ,否则 IPC 可以通过网络公开服务接口。

    如果此服务在多用户系统上运行,请将应用程序添加到以下密钥,使其在每个会话中运行: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run。 如果应用程序对 IPC 使用命名管道,则服务器可以通过根据会话 ID 为每个管道指定唯一名称来区分多个用户进程。

以下技术也适用于 Windows Server 2003 和 Windows XP:

  • 使用 MB_SERVICE_NOTIFICATION 调用 MessageBox 函数来显示消息框。 建议用于显示简单状态消息。 除非从单独的线程调用 MessageBox,否则请勿在服务初始化期间或从 HandlerEx 例程调用 MessageBox,以便及时返回到 SCM。

使用交互式服务

默认情况下,服务使用非交互式 窗口工作站 ,并且无法与用户交互。 但是, 交互式服务 可以显示用户界面并接收用户输入。

注意

在提升的安全上下文中运行的服务(如 LocalSystem 帐户)不应在交互式桌面上创建窗口,因为在交互式桌面上运行的任何其他应用程序都可以与此窗口交互。 这会将服务公开给登录用户执行的任何应用程序。 此外,作为 LocalSystem 运行的服务不应通过调用 OpenWindowStationGetThreadDesktop 函数来访问交互式桌面。

 

若要创建交互式服务,请在调用 CreateService 函数时执行以下操作:

  1. lpServiceStartName 参数指定 NULL,以便在 LocalSystem 帐户的上下文中运行服务。
  2. 指定 SERVICE_INTERACTIVE_PROCESS 标志。

若要确定服务是否作为交互式服务运行,请调用 GetProcessWindowStation 函数以检索窗口站的句柄,并调用 GetUserObjectInformation 函数来测试窗口工作站是否具有 WSF_VISIBLE 属性。

但请注意,以下注册表项包含一个值 NoInteractiveServices,该值控制SERVICE_INTERACTIVE_PROCESS的效果:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows

NoInteractiveServices 值默认为 1,这意味着无论服务是否具有SERVICE_INTERACTIVE_PROCESS,都不允许以交互方式运行任何服务。 当 NoInteractiveServices 设置为 0 时,允许具有 SERVICE_INTERACTIVE_PROCESS 的服务以交互方式运行。

Windows 7、Windows Server 2008 R2、Windows XP 和 Windows Server 2003:NoInteractiveServices 值默认为 0,这意味着允许具有SERVICE_INTERACTIVE_PROCESS的服务以交互方式运行。 当 NoInteractiveServices 设置为非零值时,无论它是否具有 SERVICE_INTERACTIVE_PROCESS,之后启动的服务都不允许以交互方式运行。

重要

所有服务都在终端服务会话 0 中运行。 因此,如果交互式服务显示用户界面,则它仅对连接到会话 0 的用户可见。 由于无法保证交互式用户连接到会话 0,因此不要将服务配置为在终端服务下或支持快速用户切换的系统上作为交互式服务运行, (使用终端服务) 实现快速用户切换。