交互式服务

通常,服务是控制台应用程序,旨在在没有图形用户界面(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 函数来显示消息框。 建议用于显示简单状态消息。 请勿在服务初始化期间或从 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,因此不要将服务配置为在终端服务下或支持快速用户切换的系统上作为交互式服务运行(使用终端服务实现快速用户切换)。