从作为可以与用户交互的SYSTEM运行的服务启动应用程序

我目前有一个单一的应用程序,需要从我在.net 3.5编码的Windows服务启动。 此应用程序当前以运行该服务的用户身份运行,在我的情况下是SYSTEM用户。 如果以SYSTEM用户身份运行,则不会将应用程序显示给用户桌面。 思考? build议?

//constructor private Process ETCHNotify = new Process(); //StartService() ETCHNotify.StartInfo.FileName = baseDir + "\\EtchNotify.exe"; ETCHNotify.StartInfo.UseShellExecute = false; //BackgroundWorkerThread_DoWork() if (!systemData.GetUserName().Equals("")) { // start ETCHNotify try { ETCHNotify.Start(); } catch (Exception ex) { systemData.Run("ERR: Notify can't start: " + ex.Message); } } 

我只执行try / catch如果函数我已经写了GetUserName()(它决定运行explorer.exe的用户的用户名)不是null

再次重申:所需的function是,这启动了ETCHNotify的状态,允许它与当前login的用户进行交互,由GetUserName()

一些发现的贴子( 这个和这个 )

请注意,从Windows Vista开始,服务严禁与用户直接交互

重要提示:从Windows Vista开始,服务不能直接与用户交互。 因此,标题为“使用交互式服务”一节中提到的技术不应在新代码中使用。

这个“特征”已经被打破了,而传统的观点认为你不应该依赖它。 服务并不意味着提供一个UI或允许任何类型的直接用户交互。 微软一直警告说,从Windows NT的早期开始就可以避免这个功能,因为可能存在安全风险。

有一些可能的解决方法,但是,如果你绝对必须有这个功能。 但我强烈建议您仔细考虑其必要性,并为您的服务探索替代设计。

使用WTSEnumerateSessions找到正确的桌面,然后CreateProcessAsUser在该桌面上启动应用程序(将其作为STARTUPINFO结构的一部分传递给桌面的句柄)是正确的。

不过,我强烈建议不要这样做。 在一些环境中,比如终端服务器主机上有许多活跃的用户,确定哪个桌面是“主动”的是不容易的,甚至可能是不可能的。

更传统的方法是在全球启动组中为您的服务添加一个小型客户端应用程序的快捷方式。 这个应用程序随后将随每个用户会话一起启动,并可用于启动其他应用程序(如果需要的话),而不需要任何用户证书,会话和/或桌面的杂耍。

我不打算回答这个问题,因为你已经回答了这个问题,(现在是二十五年了,现在呢?)但是总有那些人在寻找同样的话题,并且阅读答案。 。

为了让我的服务与桌面互动,无论是桌面,还是多台桌面,甚至服务甚至在同一台计算机上运行的桌面应用程序! 没有关系到我在这里得到的东西…我不会在细节上让你感到厌烦,我只是把肉和土豆给你,如果你想看更多的东西,请告诉我。

好。 我做的第一件事就是创建一个广告服务 。 这是服务运行的线程,打开一个UDP套接字来监听网络上的广播。 然后,使用同一段代码,我将它与客户端应用程序共享,但它调用Advertise.CLIENT ,而不是Advertise.SERVER … CLIENT打开端口,我期望该服务在,并广播消息,“你好…有没有人在那里?”,询问他们是否有任何服务器在监听,如果是的话,回复到这个IP地址,你的电脑名称,IP地址和端口#,我可以找到.NET Remoting Services …“,然后等待一小段超时时间,收集它所得到的响应,如果它超过一个,则向用户显示一个对话框和一个响应的服务列表。然后客户端选择一个,或者,如果只有一个响应,它将调用Connect( (TserverResponse) res);这样,连接起来。此时,服务器正在使用远程服务,WellKnownClientType和WellKnownserverType把自己放在那里…

我不觉得你对我的“自动定位器”太感兴趣了,因为很多人都对UDP皱眉,甚至当你的应用在大型网络上开始播放的时候更是如此。 所以,我假设你会对我的RemotingHelper更感兴趣,让客户端连接到服务器。 它看起来像这样:

  public static Object GetObject(Type type) { try { if(_wellKnownTypes == null) { InitTypeCache(); } WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry)_wellKnownTypes[type]; if(entr == null) { throw new RemotingException("Type not found!"); } return System.Activator.GetObject(entr.ObjectType, entr.ObjectUrl); } catch(System.Net.Sockets.SocketException sex) { DebugHelper.Debug.OutputDebugString("SocketException occured in RemotingHelper::GetObject(). Error: {0}.", sex.Message); Disconnect(); if(Connect()) { return GetObject(type); } } return null; } private static void InitTypeCache() { if(m_Advertiseserver == null) { throw new RemotingException("Advertisementserver cannot be null when connecting to a server."); } _wellKnownTypes = new Dictionary<Type, WellKnownClientTypeEntry>(); Dictionary<string, object> channelProperties = new Dictionary<string, object>(); channelProperties["port"] = 0; channelProperties["name"] = m_Advertiseserver.ChannelName; Dictionary<string, object> binFormatterProperties = new Dictionary<string, object>(); binFormatterProperties["typeFilterLevel"] = "Full"; if(Environment.UserInteractive) { BinaryserverFormatterSinkProvider binFormatterProvider = new BinaryserverFormatterSinkProvider(binFormatterProperties, null); _serverChannel = new TcpserverChannel(channelProperties, binFormatterProvider); // LEF: Only if we are coming form OUTSIDE the SERVICE do we want to register the channel, since the SERVICE already has this // channel registered in this AppDomain. ChannelServices.RegisterChannel(_serverChannel, false); } System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatServiceStatus))); RegisterType(typeof(IPawnStatServiceStatus),m_Advertiseserver.RunningStatusURL.ToString()); System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatService))); RegisterType(typeof(IPawnStatService), m_Advertiseserver.RunningserverURL.ToString()); System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IServiceConfiguration))); RegisterType(typeof(IServiceConfiguration), m_Advertiseserver.RunningConfigURL.ToString()); } [SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration, RemotingConfiguration=true)] public static void RegisterType(Type type, string serviceUrl) { WellKnownClientTypeEntry clientType = new WellKnownClientTypeEntry(type, serviceUrl); if(clientType != RemotingConfiguration.IsWellKnownClientType(type)) { RemotingConfiguration.RegisterWellKnownClientType(clientType); } _wellKnownTypes[type] = clientType; } public static bool Connect() { // Init the Advertisement Service, and Locate any listening services out there... m_Advertiseserver.InitClient(); if(m_Advertiseserver.LocateServices(iTimeout)) { if(!Connected) { bConnected = true; } } else { bConnected = false; } return Connected; } public static void Disconnect() { if(_wellKnownTypes != null) { _wellKnownTypes.Clear(); } _wellKnownTypes = null; if(_serverChannel != null) { if(Environment.UserInteractive) { // LEF: Don't unregister the channel, because we are running from the service, and we don't want to unregister the channel... ChannelServices.UnregisterChannel(_serverChannel); // LEF: If we are coming from the SERVICE, we do *NOT* want to unregister the channel, since it is already registered! _serverChannel = null; } } bConnected = false; } } 

所以,这是我的远程代码的肉,让我写一个客户端,不必知道服务的安装位置,或在网络上运行多少服务。 这使我可以通过网络或本地机器与它进行通信。 让两个或两个以上的人运行应用程序并不是一个问题,但是,你的可能。 现在,我有一些复杂的回调代码,我在那里注册事件,通过远程通道,所以我必须有代码来检查客户端是否连接,然后才向客户端发送事件。 此外,如果您正在运行多个用户,则可能不想使用Singleton对象。 这对我来说很好,因为服务器拥有这些对象,并且它们是服务器的所有东西。 所以,我的STATS对象,例如,是一个单身人士。 没有理由为每个连接创建一个实例,当每个人都看到相同的数据,对不对?

如有必要,我可以提供更多的代码块。 当然,这是什么使得这项工作的总体情况的一小部分…更不用说订阅提供者,以及所有这些。

为了完整起见,我将包含代码块,以便在过程的整个过程中保持您的服务连接。

 public override object InitializeLifetimeService() { ILease lease = (ILease)base.InitializeLifetimeService(); if(lease.CurrentState == LeaseState.Initial) { lease.InitialLeaseTime = TimeSpan.FromHours(24); lease.SponsorshipTimeout = TimeSpan.FromSeconds(30); lease.RenewOnCallTime = TimeSpan.FromHours(1); } return lease; } #region ISponsor Members [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)] public TimeSpan Renewal(ILease lease) { return TimeSpan.FromHours(12); } #endregion 

如果将ISponsor接口作为服务器对象的一部分包含在内,则可以实现上述代码。

希望这有些有用。

当你注册你的服务时,你可以告诉它允许与桌面交互。 你可以阅读这个oldie链接http://www.codeproject.com/KB/install/cswindowsservicedesktop.aspx

此外,不要忘记,你可以有多个用户同时登录。

显然,在Windows Vista和更新的与桌面交互已经变得更加困难。 阅读这个潜在的解决方案: http : //www.codeproject.com/KB/cs/ServiceDesktopInteraction.aspx

最终,为了解决这个问题,我接受了@marco的建议和他提到的帖子。 我创建的服务完全独立于与用户交互的托盘应用程序。 但是,我通过注册表“启动”方法安装了托盘应用程序。 Service安装程序现在将安装与用户交互的应用程序…这是最安全和最完整的方法。

谢谢大家的帮助。