/// <summary>Run the AccuRev \e command synchronously (blocks) on the current thread.</summary> /// <param name="command">The AccuRev command to run, e.g. <tt>hist -fx -p AcTools -t 453</tt></param> /// <param name="validator">Use to change the [default logic](@ref AcUtils#CmdValidate#isValid) for determining /// if an AcUtilsException should be thrown based on AccuRev's program return value for \e command.</param> /// <returns>On success an AcResult object with AcResult.RetVal set to the AccuRev program return value and the command /// result (usually XML) in AcResult.CmdResult. Otherwise, AcResult.RetVal is <em>minus one (-1)</em> (default) on error. /// </returns> /// <exception cref="AcUtilsException">thrown on AccuRev program invocation failure or <tt>merge/diff</tt> /// program error (return value <em>two (2)</em>).</exception> /// <exception cref="Win32Exception">caught and [logged](@ref AcUtils#AcDebug#initAcLogging) /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on error spawning the AccuRev process that runs the command.</exception> /// <exception cref="InvalidOperationException">caught and logged in same on failure to handle a range of exceptions.</exception> /*! \attention Do not use this function for the <tt>ismember</tt> command. Instead, use AcGroups#isMember. */ public static AcResult run(string command, ICmdValidate validator = null) { AcResult result = new AcResult(); StringBuilder error = new StringBuilder(512); try { using (Process process = new Process()) { process.StartInfo.FileName = "accurev"; process.StartInfo.Arguments = command; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardInput = true; // fix for AccuRev defect 29059 process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.StandardOutputEncoding = Encoding.UTF8; process.StartInfo.StandardErrorEncoding = Encoding.UTF8; process.ErrorDataReceived += (sender, e) => { error.AppendLine(e.Data); }; StringBuilder output = new StringBuilder(); output.Capacity = 4096; process.OutputDataReceived += (sender, e) => { output.AppendLine(e.Data); }; process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.WaitForExit(); // blocks here. recommended to ensure output buffer has been flushed if (process.HasExited) { string err = error.ToString().Trim(); if (!String.IsNullOrEmpty(err) && !(String.Equals("You are not in a directory associated with a workspace", err))) { AcDebug.Log(err, false); } ICmdValidate validate = validator ?? new CmdValidate(); if (validate.isValid(command, process.ExitCode)) { result.RetVal = process.ExitCode; result.CmdResult = output.ToString(); } else { throw new AcUtilsException($"AccuRev program return: {process.ExitCode}{Environment.NewLine}accurev {command}"); // let calling method handle } } } } catch (Win32Exception ecx) { string msg = String.Format(@"Win32Exception caught and logged in AcCommand.run{0}{1}{0}""accurev {2}""{0}errorcode: {3}{0}native errorcode: {4}{0}{5}{0}{6}{0}{7}{0}{8}", Environment.NewLine, ecx.Message, command, ecx.ErrorCode.ToString(), ecx.NativeErrorCode.ToString(), ecx.StackTrace, ecx.Source, ecx.GetBaseException().Message, error.ToString()); AcDebug.Log(msg); } catch (InvalidOperationException ecx) { string msg = String.Format(@"InvalidOperationException caught and logged in AcCommand.run{0}{1}{0}""accurev {2}""{0}{3}", Environment.NewLine, ecx.Message, command, error.ToString()); AcDebug.Log(msg); } return(result); }
/// <summary>Run the AccuRev \e command asynchronously with non-blocking I/O.</summary> /// <remarks>To reduce the risk of the AccuRev server becoming unresponsive due to an excess of commands, /// the maximum number of commands that will run simultaneously for a client application is eight (8). /// Other commands [are queued](@ref System#Threading#Tasks#Schedulers#LimitedConcurrencyLevelTaskScheduler) /// until space is available. You can override this default value by creating the environment variable /// \b ACUTILS_MAXCONCURRENT and specifying a different number, e.g. \c ACUTILS_MAXCONCURRENT=12.</remarks> /// <param name="command">The AccuRev command to run, e.g. <tt>hist -fx -p AcTools -t 453</tt></param> /// <param name="validator">Use to change the [default logic](@ref AcUtils#CmdValidate#isValid) for determining /// if an AcUtilsException should be thrown based on AccuRev's program return value for \e command.</param> /// <returns>An AcResult object with AcResult.RetVal set to the AccuRev program return value and the command result /// (usually XML) in AcResult.CmdResult. Returns \e null if an exception occurs. /// </returns> /// <exception cref="AcUtilsException">thrown on AccuRev program invocation failure or <tt>merge/diff</tt> /// program error (return value <em>two (2)</em>).</exception> /// <exception cref="Win32Exception">caught and [logged](@ref AcUtils#AcDebug#initAcLogging) /// in <tt>\%LOCALAPPDATA\%\\AcTools\\Logs\\<prog_name\>-YYYY-MM-DD.log</tt> on error spawning the AccuRev process that runs the command.</exception> /// <exception cref="InvalidOperationException">caught and logged in same on failure to handle a range of exceptions.</exception> /*! \attention Do not use this function for the <tt>ismember</tt> command. Instead, use AcGroups#isMember. */ public static async Task <AcResult> runAsync(string command, ICmdValidate validator = null) { TaskCompletionSource <AcResult> tcs = new TaskCompletionSource <AcResult>(); StringBuilder error = new StringBuilder(512); try { await _taskFactory.StartNew(() => { using (Process process = new Process()) { process.StartInfo.FileName = "accurev"; process.StartInfo.Arguments = command; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardInput = true; // fix for AccuRev defect 29059 process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.StandardOutputEncoding = Encoding.UTF8; process.StartInfo.StandardErrorEncoding = Encoding.UTF8; process.ErrorDataReceived += (sender, e) => { error.AppendLine(e.Data); }; process.Start(); process.BeginErrorReadLine(); Task <string> output = process.StandardOutput.ReadToEndAsync(); process.WaitForExit(); if (process.HasExited) { string err = error.ToString().Trim(); if (!String.IsNullOrEmpty(err) && !(String.Equals("You are not in a directory associated with a workspace", err))) { AcDebug.Log(err, false); } ICmdValidate validate = validator ?? new CmdValidate(); if (validate.isValid(command, process.ExitCode)) { tcs.SetResult(new AcResult(process.ExitCode, output.Result)); } else { tcs.SetException(new AcUtilsException($"AccuRev program return: {process.ExitCode}{Environment.NewLine}accurev {command}")); // let calling method handle } } } }).ConfigureAwait(false); } catch (Win32Exception ecx) { string msg = String.Format(@"Win32Exception caught and logged in AcCommand.runAsync{0}{1}{0}""accurev {2}""{0}errorcode: {3}{0}native errorcode: {4}{0}{5}{0}{6}{0}{7}{0}{8}", Environment.NewLine, ecx.Message, command, ecx.ErrorCode.ToString(), ecx.NativeErrorCode.ToString(), ecx.StackTrace, ecx.Source, ecx.GetBaseException().Message, error.ToString()); AcDebug.Log(msg); tcs.SetException(ecx); } catch (InvalidOperationException ecx) { string msg = String.Format(@"InvalidOperationException caught and logged in AcCommand.runAsync{0}{1}{0}""accurev {2}""{0}{3}", Environment.NewLine, ecx.Message, command, error.ToString()); AcDebug.Log(msg); tcs.SetException(ecx); } return(await tcs.Task.ConfigureAwait(false)); }