我有一个Windows服务,在系统帐户下运行,并不时执行一些程序(是的,是的,我知道这是一个不好的做法,但这不是我的决定).我需要设置"与桌面交互"检查,以便在安装服务后查看已执行程序的gui.我尝试了几种方法,将下面的代码放在我的服务安装程序的AfterInstall或OnCommited事件处理程序中:
ConnectionOptions coOptions = new ConnectionOptions();
coOptions.Impersonation = ImpersonationLevel.Impersonate;
ManagementScope mgmtScope = new System.Management.ManagementScope(@"root\CIMV2", coOptions);
mgmtScope.Connect();
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + ServiceMonitorInstaller.ServiceName + "'");
ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
InParam["DesktopInteract"] = true;
ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", InParam, null);
Run Code Online (Sandbox Code Playgroud)
要么
RegistryKey ckey = Registry.LocalMachine.OpenSubKey(
@"SYSTEM\CurrentControlSet\Services\WindowsService1", true);
if(ckey != null)
{
if(ckey.GetValue("Type") != null)
{
ckey.SetValue("Type", ((int)ckey.GetValue("Type") | 256));
}
}
Run Code Online (Sandbox Code Playgroud)
这两种方法都"有效".他们设置了支票,但是在我启动服务之后启动了exe - 并且没有显示gui!所以,如果我停止服务,重新检查并再次启动它 - 宾果游戏!一切都开始并显示出来.实现结果的第二种方法是重新启动 - 之后还会显示gui.
所以问题是:是否有正确的方法来设置"与桌面交互"检查,所以它将开始工作而无需重新检查和重新启动?
操作系统:Windows XP(尚未试过Vista和7 ......)
小智 5
private static void SetInterActWithDeskTop()
{
var service = new System.Management.ManagementObject(
String.Format("WIN32_Service.Name='{0}'", "YourServiceName"));
try
{
var paramList = new object[11];
paramList[5] = true;
service.InvokeMethod("Change", paramList);
}
finally
{
service.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
最后在互联网搜索一周后 - 我找到了一个很好的工作解决方案: http ://asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html
找到要启动的桌面。这可能看起来很滑稽,但事情并不像看起来那么简单。通过终端服务和快速用户切换,可以有多个交互式用户同时登录到计算机。如果您想要当前坐在物理控制台上的用户,那么您很幸运,终端服务 API 调用 WTSGetActiveConsoleSessionId 将为您提供所需的会话 ID。如果您的需求更复杂(即您需要与 TS 服务器上的特定用户交互,或者您需要非交互式会话中的窗口站名称),您需要使用 WTSEnumerateSessions 枚举终端服务器会话并检查使用 WTSGetSessionInformation 获取所需信息的会话。
现在您知道需要与哪个会话进行交互,并且您已经有了它的 ID。这是整个过程的关键,使用 WTSQueryUserToken 和会话 ID,您现在可以检索登录到目标会话的用户的令牌。这完全缓解了“与桌面交互”设置的安全问题,启动的进程不会使用本地系统凭据运行,而是使用与已登录到该会话的用户相同的凭据运行!没有特权提升。
使用 CreateProcessAsUser 和我们检索到的令牌,我们可以以正常方式启动该进程,它将使用目标用户的凭据在目标会话中运行。有一些注意事项,lpCurrentDirectory 和 lpEnvironment 都必须指向有效值 - 这些参数的正常默认解析方法不适用于跨会话启动。您可以使用 CreateEnvironmentBlock 为目标用户创建默认环境块。
附有工作项目的源代码。