6

I am trying to execute a shell command from within a C# service that I created. However, this command does not seem to execute. As a standard console application, it works perfectly though, so I know there is no issue with the command itself or how it is being executed form within the code. Can anyone tell me why this would not work? Please keep in mind I am pretty new to C#, so this may just be a matter of my inexperience. Below is the code from the service itself:

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
        }

    }
}
iCantSeeSharp
  • 3,880
  • 4
  • 42
  • 65
Kevin Mills
  • 408
  • 10
  • 15
  • 1
    Is the process you are launching interactive (i.e. does it have a UI?). It's bad news if so - check this similar thread out which has some excellent information linked within it http://stackoverflow.com/questions/4147821/start-a-windows-service-and-launch-cmd – Martin Aug 09 '13 at 16:25
  • Incidentally if it is an interactive process you can still launch it from a service, but it requires using the Win32 API as opposed to using the built in `Process` functionality in .Net. – Martin Aug 09 '13 at 16:32
  • No, there is no GUI with the application that I am attempting to run (VirtualBox itself does have a GUI, but this utility is purely cli driven). I am not entirely sure what you mean by an interactive service, but I can tell you that the application takes arguments, executes them immediately, and then self-terminates after doing so, all from within the command prompt. I am assuming this is what you were needing to determine? – Kevin Mills Aug 09 '13 at 16:43
  • It may be worth adding a working directory to the `StartInfo` of the process as the starting folder behaviour is different when launching processes from a service - try setting `StartInfo.WorkingDirectory = @"C:\Program Files\Oracle\VirtualBox"` and see what happens. – Martin Aug 09 '13 at 21:16
  • Good thought, but unfortunately that didn't work either. – Kevin Mills Aug 12 '13 at 12:20

2 Answers2

0

If it's a windows app that requires the desktop, you're pretty much SOL. If it's a standard console application, you need to redirect standard input, standard output and standard error. You also need to set the ProcessStartInfo's UseShellExecute property to false: you service can't start the OS shell (cmd.exe) as that requires access to the desktop...and further, you can't redirect standard input with that property set to true.

Standard input is normally wired up to the keyboard. Your service does not have access to the keyboard. Standard out and standard error are normally wired up to the cmd.exe console window.

Once you have standard output and standard error redirected, you'll need to wire up handler to these Process object events:

  • Exited. Raised when the process ends.
  • OutputDataReceived. Raised when data is written to standard output by the process (in reality, occurs when any buffers on the output stream are flushed, so it may lag from the actual write operation).
  • ErrorDataReceived. Raised when data is written to standard error.

I tend to redirect standard output and standard error to the same stream and make sure that that stream is unbuffered. It's the equivalent of the cmd.exe incantation

some-command 2>&1

to redirect stdout and stderr into the same file handle.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • This particular utility does not require the desktop. It is just a standard console app which is executed using a few arguments that I had specified using myProcess.StartInfo.Arguments = "controlvm test setlinkstate1 off". I'm sorry but your explanation is a bit over my head. Could you break this down a bit for me? – Kevin Mills Aug 09 '13 at 17:13
  • That's what I'm saying. Unless you specify `UseShellExecute = false ;`, your executable process is going to started under an instance of CMD.EXE. That requires a desktop and your service doesn't have access to it. – Nicholas Carey Aug 09 '13 at 17:19
  • This has already been set on the line: myProcess.StartInfo.UseShellExecute = false – Kevin Mills Aug 09 '13 at 17:20
0

As it turns out, the reason why this was not working was due to Application Compatibility - Session 0 Isolation. Essentially, the service launched the process as intended, but it was being launched inside of session 0, so it was hidden from the user, and as such, the user could not interact with it.

In order to get around this constraint, I was able to follow the guide Subverting Vista UAC in Both 32 and 64 bit Architectures written by Pero Matić. This brief guide allowed me to spawn a process from a service inside the currently logged on user's session, rather than inside of session 0. Sadly, I no longer have the completed code that I used to get this to work as the project I was using it for was cancelled.

Kevin Mills
  • 408
  • 10
  • 15