我编写了一个Windows服务,允许我远程运行和停止应用程序.这些应用程序使用CreateProcess运行,这对我有用,因为它们中的大多数只执行后端处理.最近,我需要运行将GUI呈现给当前登录用户的应用程序.如何在C++中编码以允许我的服务找到当前活动的桌面并在其上运行GUI?
Roger Lipscombe的回答是,使用WTSEnumerateSessions找到合适的桌面,然后使用CreateProcessAsUser在该桌面上启动应用程序(将桌面句柄作为STARTUPINFO结构的一部分传递)是正确的.
但是,我强烈建议不要这样做.在某些环境中,例如具有许多活动用户的终端服务器主机,确定哪个桌面是"活动"桌面并不容易,甚至可能无法实现.
但最重要的是,如果一个应用程序突然出现在用户的桌面上,这很可能会在一个糟糕的时间发生(或者因为用户根本没想到它,或者因为你试图在会话时启动应用程序尚未完全初始化,在关闭过程中,或其他什么).
更传统的方法是在全局启动组中为您的服务添加一个小客户端应用程序的快捷方式.然后,此应用程序将与每个用户会话一起启动,并且可以在不使用任何用户凭据,会话和/或桌面的情况下启动其他应用程序(如果需要).
此外,管理员可以根据需要移动/禁用此快捷方式,这将使您的应用程序部署更加容易,因为它不会偏离其他Windows应用程序使用的标准...
简短的回答是"你没有",因为打开在另一个用户环境下运行的GUI程序是一个安全漏洞,通常称为粉碎攻击.
看一下这篇MSDN文章:Interactive Services.它为服务与用户交互提供了一些选项.
简而言之,您有以下选择:
使用WTSSendMessage函数在用户会话中显示一个对话框.
创建单独的隐藏GUI应用程序,并使用CreateProcessAsUser函数在交互式用户的上下文中运行应用程序.设计GUI应用程序以通过进程间通信(IPC)的某种方法(例如命名管道)与服务进行通信.该服务与GUI应用程序通信,以告知它何时显示GUI.应用程序将用户交互的结果传回服务,以便服务可以采取适当的操作.请注意,除非使用适当的访问控制列表(ACL),否则IPC可以通过网络公开您的服务接口.
如果此服务在多用户系统上运行,请将应用程序添加到以下密钥,以便在每个会话中运行:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run.如果应用程序使用IPC的命名管道,则服务器可以通过根据会话ID为每个管道提供唯一名称来区分多个用户进程.
WTSEnumerateSessions和CreateProcessAsUser.
有几个人建议使用WTSEnumerateSessions和CreateProcessAsUser.我想知道为什么没有人建议WTSGetActiveConsoleSessionId,因为你说你只想定位一个登录用户.
有些人确实建议使用CreateProcessAsUser.如果按照您所说的方式调用普通的旧CreateProcess,那么应用程序的GUI将使用您的服务权限而不是用户的权限运行.