Pregunta

I am launching rsync from inside my app. Right now it works fine, it opens a console window, prompts me for my password, and shows file progress as the files copy.

public static int SyncFolder(string sourceFolder, string destFolder)
{

    Process rsync = new Process();
    rsync.StartInfo.FileName = Path.Combine(RsyncPath, "rsync.exe");
    rsync.StartInfo.UseShellExecute = true;
    rsync.StartInfo.Arguments = String.Join(" ", new[] { "-rltzh --progress --chmod=a=rw,Da+x", FileUtils.EncodeParameterArgument(sourceFolder), FileUtils.EncodeParameterArgument(destFolder) });
    rsync.Start();
    rsync.WaitForExit();

    return rsync.ExitCode;
}

The problem is I don't want a separate console window to be opened. I would like to display the text progress inside a control of some type and respond to any prompts (like entering the password) from inside my program itself.

public int SyncFolder(string sourceFolder, string destFolder)
{

    Process rsync = new Process();
    rsync.StartInfo.FileName = Path.Combine(RsyncPath, "rsync.exe");
    rsync.StartInfo.UseShellExecute = false;
    rsync.StartInfo.RedirectStandardInput = true;
    rsync.StartInfo.RedirectStandardOutput = true;
    rsync.StartInfo.RedirectStandardError = true;
    rsync.StartInfo.Arguments = String.Join(" ", new[] { "-rltzh --progress --chmod=a=rw,Da+x", FileUtils.EncodeParameterArgument(sourceFolder), FileUtils.EncodeParameterArgument(destFolder) });

    rsync.ErrorDataReceived += rsync_ErrorDataReceived;
    rsync.OutputDataReceived += rsync_OutputDataReceived;

    rsync.Start();
    BindToUiConrol(rsync.StandardInput);

    rsync.WaitForExit();

    return rsync.ExitCode;
}

private void rsync_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    //Magic!
}

private void rsync_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
    //Even more Magic!
}

private void BindToUiConrol(StreamWriter standardInput)
{
   if(this.InvokeRequired)
   {
       this.BeginInvoke(new Action<StreamWriter>(BindToUiConrol), standardInput);
       return;
   }

   //Hook up events here so a single line text box dumps it's text in when you hit enter.
}

And this is where I am stuck. If I did not have the %'s that kept updating on the same line I would just have it keep dumping new lines as they come in, but how do I handle the line being re-used with a new value put in to it?

My dream solution would be to just have kind of a console window embedded inside the form itself, similar to what HyperTerminal did in older versions of windows, but I have no idea how do do that in .NET.


Here is a example program to show the issue

public class Program
{

    private static void Main(string[] args)
    {
        Console.Write("1");
        Console.WriteLine();

        Console.Write("2");
        Console.CursorLeft = 0;
        Console.Write("3");
        Console.WriteLine();

        Console.Write("4");
        Console.WriteLine();
    }
}

How do I write a Forms application that will display

1
3
4

in a control after it runs the process.

¿Fue útil?

Solución

This is not a trivial problem to solve in general. It has to do with the fact that Windows/Console applications don't really write to Standard Out and Standard Error, they write to the "Console". There are various hacks and things that make Standard Out and Standard Error work ok when you just care about all of the text and all of the error text at once, but nothing works well interactively, because the way things are buffered.

Follow this discussion for more information:

Writing a console wrapper in C#?

Otros consejos

What you are looking to do is append the terminal text to a multiline textbox or a RichTextBox.

You will also need to capture the keyboard events on that control.

One concern you might run into is the program input and the GUI operating on separate threads.

How to update textbox on GUI from another thread in C#

Fast Append Text to text box

After original question was clarified What you are looking for is to detect the '\r' character which is carriage return and then you will need to move back to the last '\n' line feed in your textbox content.

You could do this in several ways.

  1. First keep a List that would contain each line and just delete the last line. And append the text after the previous line.

  2. You could use a Regex that would match the textbox content to the final linefeed in the textbox. I believe the Regex would be "^.*\n" http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx

  3. Simply truncate the textbox contents to the String.LastIndexOf('\n') http://msdn.microsoft.com/en-us/library/system.string.lastindexof.aspx

Edit #2:

The question seems directed at determining the triggers for cursor movement.
Microsoft has their reference at the following link: http://msdn.microsoft.com/en-us/library/h21280bw.aspx

The second example you gave with Console.Left = 0 seems to throw an exception. Since it is a contrived answer for this solution I am assuming oyu really only care about rsync which I would venture to guess only uses '\r', '\n', and '\b'. The only other issue would be if it uses ANSI escape codes http://en.wikipedia.org/wiki/ANSI_escape_code

Finally a loop must read the input and parse the appropriate characters. I would recommend a BackgroundWorker as the GUI will block waiting on the loop.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top