【问题标题】:Execute shell command from service从服务执行 shell 命令
【发布时间】:2013-08-09 16:21:39
【问题描述】:

我正在尝试从我创建的 C# 服务中执行 shell 命令。但是,这个命令似乎没有执行。作为一个标准的控制台应用程序,它可以完美地运行,所以我知道命令本身或者它是如何在代码中执行的形式没有问题。谁能告诉我为什么这行不通?请记住,我对 C# 还很陌生,所以这可能只是我缺乏经验的问题。以下是服务本身的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;

namespace AdapterDisableTest
{
    class Program : ServiceBase
    {
        //private static Timer workTimer;

        static void Main(string[] args)
        {
            ServiceBase.Run(new Program());
        }

        public Program()
        {
            this.ServiceName = "AdapterDisableTest";
        }

        protected override void OnStart(string[] args)
        {
            base.OnStart(args);

            Process myProcess = new Process();

            myProcess.StartInfo.FileName = @"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe";
            myProcess.StartInfo.Arguments = "controlvm test setlinkstate1 off";
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.CreateNoWindow = true;
            myProcess.Start();

        }

        protected override void OnStop()
        {
            base.OnStop();

            //TODO: clean up any variables and stop any threads
        }

    }
}

【问题讨论】:

  • 您正在启动的进程是交互式的(即它是否有 UI?)。如果是这样,那就是个坏消息 - 检查这个类似的线程,其中链接了一些很好的信息stackoverflow.com/questions/4147821/…
  • 顺便说一句,如果它是一个交互式进程,您仍然可以从服务启动它,但它需要使用 Win32 API,而不是使用 .Net 中内置的 Process 功能。
  • 不,我尝试运行的应用程序没有 GUI(VirtualBox 本身确实有一个 GUI,但这个实用程序是纯粹的 cli 驱动的)。我不完全确定您所说的交互式服务是什么意思,但我可以告诉您,该应用程序接受参数,立即执行它们,然后在这样做之后自行终止,所有这些都在命令提示符下进行。我假设这是您需要确定的?
  • 可能值得将工作目录添加到进程的StartInfo,因为从服务启动进程时起始文件夹的行为不同 - 尝试设置 StartInfo.WorkingDirectory = @"C:\Program Files\Oracle\VirtualBox" 看看会发生什么。跨度>
  • 好主意,但不幸的是也没有用。

标签: c# shell service


【解决方案1】:

如果它是一个需要桌面的 Windows 应用程序,那么你就是 SOL。如果是标准控制台应用程序,则需要重定向标准输入、标准输出和标准错误。您还需要将ProcessStartInfoUseShellExecute 属性设置为false:您的服务无法启动OS shell (cmd.exe),因为这需要访问桌面...此外,您可以' t 将该属性设置为true 的标准输入重定向。

标准输入通常连接到键盘。您的服务无权访问键盘。标准输出和标准错误通常连接到cmd.exe 控制台窗口。

一旦您重定向了标准输出和标准错误,您需要将处理程序连接到这些Process 对象事件:

  • Exited. 进程结束时引发。
  • OutputDataReceived. 在进程将数据写入标准输出时引发(实际上,在刷新输出流上的任何缓冲区时发生,因此它可能会滞后于实际的写入操作)。李>
  • ErrorDataReceived. 当数据写入标准错误时引发。

我倾向于将标准输出和标准错误重定向到同一个流,并确保该流没有缓冲。这相当于cmd.exe 咒语

some-command 2>&1

将 stdout 和 stderr 重定向到同一个文件句柄中。

【讨论】:

  • 此特定实用程序不需要桌面。它只是一个标准控制台应用程序,它使用我使用 myProcess.StartInfo.Arguments = "controlvm test setlinkstate1 off" 指定的几个参数执行。我很抱歉,但你的解释有点过头了。你能帮我解释一下吗?
  • 这就是我要说的。除非您指定UseShellExecute = false ;,否则您的可执行进程将在 CMD.EXE 实例下启动。这需要桌面,而您的服务无权访问它。
  • 这个已经设置好就行了:myProcess.StartInfo.UseShellExecute = false
【解决方案2】:

事实证明,这不起作用的原因是Application Compatibility - Session 0 Isolation。本质上,服务按预期启动了进程,但它是在会话 0 内启动的,因此对用户隐藏,因此用户无法与之交互。

为了绕过这个限制,我能够遵循 Pero Matić 编写的指南 Subverting Vista UAC in Both 32 and 64 bit Architectures。这个简短的指南允许我从当前登录的用户会话中的服务生成一个进程,而不是在会话 0 中。遗憾的是,我不再拥有完整的代码,我曾经使用它作为我正在使用的项目工作它被取消了。

【讨论】:

    猜你喜欢
    • 2011-06-15
    • 2014-04-21
    • 2014-01-22
    • 2011-03-04
    • 2013-08-25
    • 1970-01-01
    • 2016-09-14
    相关资源
    最近更新 更多