locked
How to run a non-interactive process as another user in a Windows Service using C#? RRS feed

  • Question

  • Hi,

    I need to run a process in a non-interactive way as another user in a Windows Service using C#. My code looks like this:

    var process = new Process
    {
        EnableRaisingEvents = true,
        StartInfo =
        {
            FileName = /* process name */,
            Arguments = /* arguments */,
            Domain = /* domain */,
            UserName = /* username */,
            Password = /* password */,
            CreateNoWindow = true,
            RedirectStandardError = true,
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            UseShellExecute = false
        }
    };
    
    var error = new StringBuilder();
    var errorWaitHandle = new ManualResetEventSlim();
    process.ErrorDataReceived += (s, e) =>
    {
        if (e.Data != null)
        {
            error.AppendLine(e.Data);
        }
        else
        {
            errorWaitHandle.Set();
        }
    };
    
    var output = new StringBuilder();
    var outputWaitHandle = new ManualResetEventSlim();
    process.OutputDataReceived += (s, e) =>
    {
        if (e.Data == /* detect that it's time to write to the input */)
        {
            process.StandardInput.Write(/* write to the input something that will give an output and exit the process */);
            process.StandardInput.Close();
        }
        else if (e.Data != null)
        {
            output.AppendLine(e.Data);
        }
        else
        {
            outputWaitHandle.Set();
        }
    };
    
    process.Exited += (s, e) =>
    {
        try
        {
            errorWaitHandle.Wait();
            outputWaitHandle.Wait();
    
            if (error.Length == 0)
            {
                // Do something with 'output.ToString()'.
            }
            else
            {
                // Do something with 'error.ToString()'.
            }
        }
        finally
        {
            process.Dispose();
        }
    };
    
    process.Start();
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();

    When I run this from a console application it runs without any problems. However, when I run it from a Windows Service:

    If the Windows Service is running as LOCAL_SYSTEM

    • Process.Start() method throws an exception.

    If the Windows Service is running as another account:

    • ErrorDataReceived event receives null data immediately.
    • OutputDataReceived event receives null data immediately.
    • The process does not really run.

    If I remove the lines:

            Domain = /* domain */,
            UserName = /* username */,
            Password = /* password */,

    The code runs without any problems on a Windows Service with both LOCAL_SYSTEM other accounts.

    After a lot of digging around I learned the following:

    • Since Windows Vista, Windows Services are not allowed to be interactive, in other words, they cannot create interactive processes (e.g. create windows). The only processes that are allowed are those that are not interactive (e.g. don't create windows).
    • When Process.Start() is called without credentials, the Win32 CreateProcess method is called with the CREATE_NO_WINDOW process creation flag. Since no window is created, the process is able to run under a Windows Service.
    • When Process.Start() is called with credentials, the Win32 CreateProcessWithLogonW method is called. Since this method does not support the CREATE_NO_WINDOW process creation flag, a window is always created, hence the process is unable to run under a Windows Service.

    Theoretically, there's a Win32 CreateProcessAsUser method that supports the Win32 CreateProcess method features with the addition of running it as another user.

    • Is the Win32 CreateProcessAsUser method the way to actually solve this?
    • How can I use this method from C# with the same features that the System.Diagnostics.Process class provides (e.g. standard input, output, error redirection, etc).

    • Edited by Marco GR Wednesday, June 1, 2016 12:37 AM
    Wednesday, June 1, 2016 12:36 AM

Answers