如何在Windows服务安装程序中设置“与桌面交互”

时间:2010-07-28 09:28:15

标签: c# service

我有一个在系统帐户下运行的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); 

 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));
    }
  }

这两种方法都“有效”。他们设置了支票,但是在我启动服务之后启动了exe - 并且没有显示gui!所以,如果我停止服务,重新检查并再次启动它 - 宾果游戏!一切都开始并显示出来。实现结果的第二种方法是重新启动 - 之后也会显示gui。

所以问题是:是否有正确的方法来设置“与桌面交互”检查,所以它会在没有重新检查和重新启动的情况下开始工作?

操作系统:Windows XP(尚未尝试Vista和7 ......)

3 个答案:

答案 0 :(得分: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();
            }


        }

答案 1 :(得分:2)

最后在网上搜索了一周之后 - 我找到了一个很棒的解决方案: http://asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html

  

找到要启动的桌面。这个   可能看起来很滑稽但不是那样   看似简单。有终端   服务和快速用户切换   可以是多个交互式用户   同时登录到计算机   时间。如果你想要那个用户   目前坐在身体上   控制台然后你很幸运,   终端服务API调用   WTSGetActiveConsoleSessionId会得到   你需要的会话ID。如果你的   需求更复杂(即你需要   与...上的特定用户进行交互   TS服务器或您需要的名称   窗口站在非交互式   会话)你需要枚举   终端服务器会话   WTSEnumerateSessions并检查   会话以获取您需要的信息   使用WTSGetSessionInformation。

     

现在你知道你需要什么课程   与您交互并拥有其ID。   这是整个过程的关键,   使用WTSQueryUserToken和   您现在可以检索的会话ID   登录到的用户的令牌   目标会话。这完全是   减轻了安全问题   “与桌面交互”设置,   推出的流程不会   与LOCAL SYSTEM一起运行   凭证,但相同   凭证作为用户   已登录该会话!没有   特权提升。

     

使用CreateProcessAsUser和   我们已检索到令牌,我们可以启动   正常的过程和它的过程   将在目标会话中运行   目标用户的凭据。那里   两者都有几点需要注意   lpCurrentDirectory和lpEnvironment   必须指向有效值 -   正常的默认解析方法   这些参数不起作用   跨会议发布。您可以使用   CreateEnvironmentBlock创建一个   默认环境块为   目标用户。

附上了工作项目的源代码。

答案 2 :(得分:2)

与Heisa相同,但与WMI相同。 (代码是Powershell,但可以轻松移植到C#)

if ($svc = gwmi win32_service|?{$_.name -eq $svcname})
{
    try {
        $null = $svc.change($svc.displayname,$svc.pathname,16,1,`
        "Manual",$false,$svc.startname,$null,$null,$null,$null)
        write-host "Change made"
    catch { throw "Error: $_" }
} else
{ throw "Service $svcname not installed" }

有关参数说明,请参阅MSDN: Service Change() method