private void StartProcess(Process processToStart, string arguments, String executable, LogHandler logHandler, bool redirectStdin) { // Define handler of the completed process ProcessCompletionCallback processCompletionCallback = delegate(Process proc) { string msg = processToStart.Id + " - " + processToStart.StartInfo.FileName + " " + processToStart.StartInfo.Arguments; try { if (_orderlyShutdown) { LogEvent("Child process [" + msg + "] terminated with " + proc.ExitCode, EventLogEntryType.Information); } else { LogEvent("Child process [" + msg + "] finished with " + proc.ExitCode, EventLogEntryType.Warning); // if we finished orderly, report that to SCM. // by not reporting unclean shutdown, we let Windows SCM to decide if it wants to // restart the service automatically if (proc.ExitCode == 0) { SignalShutdownComplete(); } Environment.Exit(proc.ExitCode); } } catch (InvalidOperationException ioe) { LogEvent("WaitForExit " + ioe.Message); } try { proc.Dispose(); } catch (InvalidOperationException ioe) { LogEvent("Dispose " + ioe.Message); } }; // Invoke process and exit ProcessHelper.StartProcessAndCallbackForExit( processToStart: processToStart, executable: executable, arguments: arguments, envVars: _envs, workingDirectory: _descriptor.WorkingDirectory, priority: _descriptor.Priority, callback: processCompletionCallback, logHandler: logHandler, redirectStdin: redirectStdin, hideWindow: _descriptor.HideWindow); }
/// <summary> /// Starts a process and asynchronosly waits for its termination. /// Once the process exits, the callback will be invoked. /// </summary> /// <param name="processToStart">Process object to be used</param> /// <param name="arguments">Arguments to be passed</param> /// <param name="executable">Executable, which should be invoked</param> /// <param name="envVars">Additional environment variables</param> /// <param name="workingDirectory">Working directory</param> /// <param name="priority">Priority</param> /// <param name="callback">Completion callback. If null, the completion won't be monitored</param> /// <param name="logHandler">Log handler. If enabled, logs will be redirected to the process and then reported</param> /// <param name="redirectStdin">Redirect standard input</param> public static void StartProcessAndCallbackForExit(Process processToStart, String executable = null, string arguments = null, Dictionary <string, string> envVars = null, string workingDirectory = null, ProcessPriorityClass?priority = null, ProcessCompletionCallback callback = null, bool redirectStdin = true, LogHandler logHandler = null) { var ps = processToStart.StartInfo; ps.FileName = executable ?? ps.FileName; ps.Arguments = arguments ?? ps.Arguments; ps.WorkingDirectory = workingDirectory ?? ps.WorkingDirectory; ps.CreateNoWindow = false; ps.UseShellExecute = false; ps.RedirectStandardInput = redirectStdin; ps.RedirectStandardOutput = logHandler != null; ps.RedirectStandardError = logHandler != null; if (envVars != null) { foreach (string key in envVars.Keys) { Environment.SetEnvironmentVariable(key, envVars[key]); // DONTDO: ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163) } } processToStart.Start(); Logger.Info("Started process " + processToStart.Id); if (priority != null && priority.Value != ProcessPriorityClass.Normal) { processToStart.PriorityClass = priority.Value; } // Redirect logs if required if (logHandler != null) { Logger.Debug("Forwarding logs of the process " + processToStart + " to " + logHandler); logHandler.log(processToStart.StandardOutput.BaseStream, processToStart.StandardError.BaseStream); } // monitor the completion of the process if (callback != null) { StartThread(delegate { processToStart.WaitForExit(); callback(processToStart); }); } }
/// <summary> /// Starts a process and asynchronosly waits for its termination. /// Once the process exits, the callback will be invoked. /// </summary> /// <param name="processToStart">Process object to be used</param> /// <param name="arguments">Arguments to be passed</param> /// <param name="executable">Executable, which should be invoked</param> /// <param name="envVars">Additional environment variables</param> /// <param name="workingDirectory">Working directory</param> /// <param name="priority">Priority</param> /// <param name="callback">Completion callback. If null, the completion won't be monitored</param> public static void StartProcessAndCallbackForExit(Process processToStart, String executable = null, string arguments = null, Dictionary <string, string> envVars = null, string workingDirectory = null, ProcessPriorityClass?priority = null, ProcessCompletionCallback callback = null) { var ps = processToStart.StartInfo; ps.FileName = executable ?? ps.FileName; ps.Arguments = arguments ?? ps.Arguments; ps.WorkingDirectory = workingDirectory ?? ps.WorkingDirectory; ps.CreateNoWindow = false; ps.UseShellExecute = false; ps.RedirectStandardInput = true; // this creates a pipe for stdin to the new process, instead of having it inherit our stdin. ps.RedirectStandardOutput = true; ps.RedirectStandardError = true; if (envVars != null) { foreach (string key in envVars.Keys) { Environment.SetEnvironmentVariable(key, envVars[key]); // DONTDO: ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163) } } processToStart.Start(); Logger.Info("Started process " + processToStart.Id); if (priority != null && priority.Value != ProcessPriorityClass.Normal) { processToStart.PriorityClass = priority.Value; } // monitor the completion of the process if (callback != null) { StartThread(delegate { processToStart.WaitForExit(); callback(processToStart); }); } }