c# - 运行命令提示符 .exe 应用程序时,将超时定义为 C# WPF 应用程序中的特定行

标签 c# .net wpf software-design

在我的主要 WPF 应用程序代码中,我需要使用命令提示符运行 .exe 应用程序。此操作在 backgroundworker 中执行 我有以下代码。代码在命令提示符下运行 readlines.exe 应用程序,并将输出行读入字符串 (str)。

string str;
ProcessStartInfo proc = new ProcessStartInfo();
proc.WindowStyle = ProcessWindowStyle.Hidden;
proc.UseShellExecute = true;
proc.FileName = @"readlines.exe";
proc.Arguments = @"";
proc.UseShellExecute = false;
proc.RedirectStandardOutput = true;
proc.CreateNoWindow = true;
proc.RedirectStandardInput = true;

Process proc1 = Process.Start(proc);
proc1.StandardInput.WriteLine("");

str = proc1.StandardOutput.ReadToEnd();

我想在下面的行中添加超时,这样当超时结束时,过程将被取消(如 CTR+C),“str”将获得输出文本,直到这一点。

str = proc1.StandardOutput.ReadToEnd();

这可能吗?

最佳答案

虽然之前的答案已经被接受,但这里有一个可能更有用、更安全和更高效的解决方案。此外,它不使用 ReadLine() 方法,该方法会阻塞直到写入一行(这可能永远不会发生)。它使用 StringBuilder 的实例并以指定数据 block 的形式从流中读取(默认大小为 128 个字符)。此外,它支持基于事件的读取数据通知。

类的用法保持不变。

ProcessOutputReader por = new ProcessOutputReader(proc1);
por.StartReading();

// Do whatever you want here
// (e.g. sleep or whatever)

por.StopReading();

// Now you have everything that has been read in por.Data

不过,我添加了 OnDataRead 事件,每次读取新数据时都会触发该事件。您可以使用例如访问数据以下代码:

...
// Subscribe to the event
por.OnDataRead += OnDataReadEventHandler;
...

回调方法/事件处理程序看起来像这样:

private void OnDataReadEventHandler(object sender, ProcessOutputReaderEventArgs e)
{
    // e.IntermediateDataStore points to the StringBuilder instance which holds
    // all the data that has been received until now.
    string completeData = e.IntermediateDataStore.ToString();

    // e.NewData points to a string which contains the data that has been received
    // since the last triggered event (because the event is triggered on each read).
    string newData = e.NewData;
}

修改后的 ProcessOutputReader 类如下所示:

/// <summary>
/// Represents the ProcessOutputReader class.
/// </summary>
public class ProcessOutputReader
{
    /// <summary>
    /// Represents the instance of the thread arguments class.
    /// </summary>
    private ProcessOutputReaderWorkerThreadArguments threadArguments;

    /// <summary>
    /// Initializes a new instance of the <see cref="ProcessOutputReader"/> class.
    /// </summary>
    /// <param name="process">The process which's output shall be read.</param>
    /// <exception cref="System.ArgumentOutOfRangeException">Is thrown if the specified process reference is null.</exception>
    public ProcessOutputReader(Process process)
    {
        if (process == null)
        {
            throw new ArgumentOutOfRangeException("process", "The parameter \"process\" must not be null");
        }

        this.Process = process;
        this.IntermediateDataStore = new StringBuilder();
        this.threadArguments = new ProcessOutputReaderWorkerThreadArguments(this.Process, this.IntermediateDataStore);
    }

    /// <summary>
    /// Is fired whenever data has been read from the process output.
    /// </summary>
    public event EventHandler<ProcessOutputReaderEventArgs> OnDataRead;

    /// <summary>
    /// Gets or sets the worker thread.
    /// </summary>
    private Thread ReaderThread
    {
        get;
        set;
    }

    /// <summary>
    /// Gets or sets the intermediate data store.
    /// </summary>
    private StringBuilder IntermediateDataStore
    {
        get;
        set;
    }

    /// <summary>
    /// Gets the data collected from the process output.
    /// </summary>
    public string Data
    {
        get
        {
            return this.IntermediateDataStore.ToString();
        }
    }

    /// <summary>
    /// Gets the process.
    /// </summary>
    public Process Process
    {
        get;
        private set;
    }

    /// <summary>
    /// Stars reading from the process output.
    /// </summary>
    public void StartReading()
    {
        if (this.ReaderThread != null)
        {
            if (this.ReaderThread.IsAlive)
            {
                return;
            }
        }

        this.ReaderThread = new Thread(new ParameterizedThreadStart(ReaderWorker));
        this.threadArguments.Exit = false;
        this.ReaderThread.Start(this.threadArguments);
    }

    /// <summary>
    /// Stops reading from the process output.
    /// </summary>
    public void StopReading()
    {
        if (this.ReaderThread != null)
        {
            if (this.ReaderThread.IsAlive)
            {
                this.threadArguments.Exit = true;
                this.ReaderThread.Join();
            }
        }
    }

    /// <summary>
    /// Fires the OnDataRead event.
    /// </summary>
    /// <param name="newData">The new data that has been read.</param>
    protected void FireOnDataRead(string newData)
    {
        if (this.OnDataRead != null)
        {
            this.OnDataRead(this, new ProcessOutputReaderEventArgs(this.IntermediateDataStore, newData));
        }
    }

    /// <summary>
    /// Represents the worker method.
    /// </summary>
    /// <param name="data">The thread arguments, must be an instance of the <see cref="ProcessOutputReaderWorkerThreadArguments"/> class.</param>
    private void ReaderWorker(object data)
    {
        ProcessOutputReaderWorkerThreadArguments args;

        try
        {
            args = (ProcessOutputReaderWorkerThreadArguments)data;
        }
        catch
        {
            return;
        }

        try
        {
            char[] readBuffer = new char[args.ReadBufferSize];

            while (!args.Exit)
            {
                if (args.Process == null)
                {
                    return;
                }

                if (args.Process.HasExited)
                {
                    return;
                }

                if (args.Process.StandardOutput.EndOfStream)
                {
                    return;
                }

                int readBytes = this.Process.StandardOutput.Read(readBuffer, 0, readBuffer.Length);
                args.IntermediateDataStore.Append(readBuffer, 0, readBytes);

                this.FireOnDataRead(new String(readBuffer, 0, readBytes));
            }
        }
        catch (ThreadAbortException)
        {
            if (!args.Process.HasExited)
            {
                args.Process.Kill();
            }
        }
    }
}

此外,您还需要如下所示的 ProcessOutputReaderWorkerThreadArguments 类:

/// <summary>
/// Represents the ProcessOutputReaderWorkerThreadArguments class.
/// </summary>
public class ProcessOutputReaderWorkerThreadArguments
{
    /// <summary>
    /// Represents the read buffer size,
    /// </summary>
    private int readBufferSize;

    /// <summary>
    /// Initializes a new instance of the <see cref="ProcessOutputReaderWorkerThreadArguments"/> class.
    /// </summary>
    /// <param name="process">The process.</param>
    /// <param name="intermediateDataStore">The intermediate data store.</param>
    public ProcessOutputReaderWorkerThreadArguments(Process process, StringBuilder intermediateDataStore)
    {
        this.ReadBufferSize = 128;
        this.Exit = false;
        this.Process = process;
        this.IntermediateDataStore = intermediateDataStore;
    }

    /// <summary>
    /// Gets or sets a value indicating whether the thread shall exit or not.
    /// </summary>
    public bool Exit
    {
        get;
        set;
    }

    /// <summary>
    /// Gets or sets the read buffer size in bytes.
    /// </summary>
    /// <exception cref="System.ArgumentOutOfRangeException">Is thrown if the specified value is not greather than 0.</exception>
    public int ReadBufferSize
    {
        get
        {
            return this.readBufferSize;
        }
        set
        {
            if (value <= 0)
            {
                throw new ArgumentOutOfRangeException("value", "The specified value for \"ReadBufferSize\" must be greater than 0.");
            }

            this.readBufferSize = value;
        }
    }

    /// <summary>
    /// Gets the process.
    /// </summary>
    public Process Process
    {
        get;
        private set;
    }

    /// <summary>
    /// Gets the intermediate data store.
    /// </summary>
    public StringBuilder IntermediateDataStore
    {
        get;
        private set;
    }
}

ProcessOutputReaderEventArgs 类如下所示:

/// <summary>
/// Represents the ProcessOutputReaderEventArgs class.
/// </summary>
public class ProcessOutputReaderEventArgs : EventArgs 
{
    /// <summary>
    /// Initializes a new instance of the <see cref="ProcessOutputReaderEventArgs"/> class.
    /// </summary>
    /// <param name="intermediateDataStore">The reference to the intermediate data store.</param>
    /// <param name="newData">The new data that has been read.</param>
    public ProcessOutputReaderEventArgs(StringBuilder intermediateDataStore, string newData)
    {
        this.IntermediateDataStore = intermediateDataStore;
        this.NewData = newData;
    }

    /// <summary>
    /// Gets the reference to the intermediate data store.
    /// </summary>
    public StringBuilder IntermediateDataStore
    {
        get;
        private set;
    }

    /// <summary>
    /// Gets the new data that has been read.
    /// </summary>
    public string NewData
    {
        get;
        private set;
    }
}

关于c# - 运行命令提示符 .exe 应用程序时,将超时定义为 C# WPF 应用程序中的特定行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24104789/

相关文章:

c# - ASP .NET 应用程序启动了多少次

c# - 发出一个类覆盖两个具有相同名称的接口(interface)方法

.net - Paypal REST API .net SDK - 400 个错误请求

wpf - 通知 DevExpress 数据网格中 View 上的模型属性发生更改

c# - 如何以编程方式访问 ContentTemplate 中定义的元素?

c# - 通过 xmlns 引用访问另一个项目

c# - 订阅/取消订阅(添加/删除)扩展方法内的事件

c# - 错误 : a SafeHandle or CriticalHandle of type ZLibStreamHandle failed to properly release

c# - 如何使用 Task 对象初始化 List

.net - Databound后如何更改Gridview的标题文本?