redicting standard output

Z

Zenon

I have an application where I create a Plink process to communicate
with an HP Unix box. The problem I am having is that while reading the
redirected output, it seems to try to continue past the end and gets
caught in an endless loop. I am checking for the end of the output
like this:

while ((hj = puttyProcess.StandardOutput.ReadLine())!=null)

What am I doing wrong?

thanks,

Zenon
 
J

Jon Skeet [C# MVP]

Zenon said:
I have an application where I create a Plink process to communicate
with an HP Unix box. The problem I am having is that while reading the
redirected output, it seems to try to continue past the end and gets
caught in an endless loop. I am checking for the end of the output
like this:

while ((hj = puttyProcess.StandardOutput.ReadLine())!=null)

What am I doing wrong?

What do you mean by "continue past the end"?

Note that you should also be reading StandardError in another thread,
otherwise you could end up with plink blocking when it tries to write
to stderr.

Jon
 
Z

Zenon

I guess that I was assuming the thread was reading past the null which
signifies the end of the output and getting caught there. How do I
read StandardError into another thread?

thanks for your help

Z
 
J

Jon Skeet [C# MVP]

Zenon said:
I guess that I was assuming the thread was reading past the null which
signifies the end of the output and getting caught there.

What evidence did you have for that? Are you sure it isn't just
blocking because plink hasn't finished?
How do I
read StandardError into another thread?

Create a new thread which has access to the Process reference, and do
exactly the same as you're doing for StandardOutput, but for
StandardError.
 
Z

Zenon

Sorry to keep re-posting, but I just can't get it to work. Here is my
code

//////////////////////////////////////////
// Start Plink process
puttyInfo.RedirectStandardInput = true;
puttyInfo.RedirectStandardOutput = true;
puttyInfo.UseShellExecute = false;
puttyInfo.RedirectStandardError = true;
puttyInfo.CreateNoWindow = true;
puttyInfo.Arguments = userName;
puttyInfo.FileName = "Plink.exe";
puttyProcess = Process.Start(puttyInfo);
puttyProcess.StandardInput.WriteLine(password);
StreamReader myStreamReader = puttyProcess.StandardError;

string hj = "";
string err = "";
while ((hj =puttyProcess.StandardOutput.ReadLine())!= null)
{
while((err = myStreamReader.ReadLine()) != null)
{
setTextBoxStatus(err);
}

setTextBoxStatus(hj);
}

It is hanging on
while((err = myStreamReader.ReadLine()) != null)
 
J

Jon Skeet [C# MVP]

Zenon said:
Sorry to keep re-posting, but I just can't get it to work. Here is my
code

//////////////////////////////////////////
// Start Plink process
puttyInfo.RedirectStandardInput = true;
puttyInfo.RedirectStandardOutput = true;
puttyInfo.UseShellExecute = false;
puttyInfo.RedirectStandardError = true;
puttyInfo.CreateNoWindow = true;
puttyInfo.Arguments = userName;
puttyInfo.FileName = "Plink.exe";
puttyProcess = Process.Start(puttyInfo);
puttyProcess.StandardInput.WriteLine(password);
StreamReader myStreamReader = puttyProcess.StandardError;

string hj = "";
string err = "";
while ((hj =puttyProcess.StandardOutput.ReadLine())!= null)
{
while((err = myStreamReader.ReadLine()) != null)
{
setTextBoxStatus(err);
}

setTextBoxStatus(hj);
}

It is hanging on
while((err = myStreamReader.ReadLine()) != null)

That's doing it in the same thread though - so it will block until
there's a line from standard output, then it'll block until the error
stream has finished (completely - i.e. until the process has
terminated).

As I said before, you need to start a new thread to read in.
If you're unfamiliar with threading, see
http://www.pobox.com/~skeet/csharp/threads
 
Z

Zenon

I have read the c++ code as well as Johns and came up with this code.
I thought I had started a second thread for the error but it still
doesn't work.

//////////////////////////////////////////
// Start Plink process

ThreadStart errProc = new ThreadStart(readError);
Thread errThread = new Thread(errProc);
errThread.Start();

puttyInfo.RedirectStandardInput = true;
puttyInfo.RedirectStandardOutput = true;
puttyInfo.UseShellExecute = false;
puttyInfo.RedirectStandardError = true;
puttyInfo.CreateNoWindow = true;
puttyInfo.Arguments = userName;
puttyInfo.FileName = "Plink.exe";
puttyProcess = Process.Start(puttyInfo);
puttyProcess.StandardInput.WriteLine(password);

string hj = "";
while ((hj =puttyProcess.StandardOutput.ReadLine())!= null)
{
hj = puttyProcess.StandardOutput.ReadLine();
setTextBoxStatus(hj);
textBoxStatus.Select(textBoxStatus.MaxLength, 0);
textBoxStatus.ScrollToCaret();
}



public void readError()
{

string yy = "";
while((yy = puttyProcess.StandardError.ReadLine()) != null)
{
setTextBoxStatus(yy);
MessageBox.Show(yy);
textBoxStatus.Select(textBoxStatus.MaxLength, 0);
textBoxStatus.ScrollToCaret();

}
}

Thanks for your help, and patience.
 
Z

Zenon

I've checked out bot your example and John's article and came up with
this. I start a second thread to handle the redirected error but keep
the main plink process the same. Unfortunately, I still can't get this
to work. I do know that blocking is a problem because if I kill a
plink process in task manager, the code continues to execute. Thanks
for your help and patience.

//////////////////////////////////////////
// Start Plink process

ThreadStart errProc = new ThreadStart(readError);
Thread errThread = new Thread(errProc);
errThread.Start();

puttyInfo.RedirectStandardInput = true;
puttyInfo.RedirectStandardOutput = true;
puttyInfo.UseShellExecute = false;
puttyInfo.RedirectStandardError = true;
puttyInfo.CreateNoWindow = true;
puttyInfo.Arguments = userName;
puttyInfo.FileName = "Plink.exe";
puttyProcess = Process.Start(puttyInfo);
puttyProcess.StandardInput.WriteLine(password);

string hj = "";
while ((hj =puttyProcess.StandardOutput.ReadLine())!= null)
{
hj = puttyProcess.StandardOutput.ReadLine();
setTextBoxStatus(hj);
textBoxStatus.Select(textBoxStatus.MaxLength, 0);
textBoxStatus.ScrollToCaret();
}



public void readError()
{

string yy = "";
while((yy = puttyProcess.StandardError.ReadLine()) != null)
{
setTextBoxStatus(yy);
MessageBox.Show(yy);
textBoxStatus.Select(textBoxStatus.MaxLength, 0);
textBoxStatus.ScrollToCaret();
Thread.Sleep(1000);
Thread.CurrentThread.Abort();
}
}
 
J

Jon Skeet [C# MVP]

Zenon said:
I've checked out bot your example and John's article and came up with
this. I start a second thread to handle the redirected error but keep
the main plink process the same. Unfortunately, I still can't get this
to work. I do know that blocking is a problem because if I kill a
plink process in task manager, the code continues to execute.

So, it sounds like plink isn't exiting when you expect it to. What are
you doing which should make it exit?
 
Z

Zenon

Actually, I am not expecting it to exit, I just forced it to do so to
determine whether it was the cause of the "hang-up". At this point, I
have just started a plink process and logged in. I want to now begin
sending it commands (run executables resident on the UNIX machine).
Did I set up my second thread correctly? Is there anything I am
missing?
 
J

Jon Skeet [C# MVP]

Zenon said:
Actually, I am not expecting it to exit, I just forced it to do so to
determine whether it was the cause of the "hang-up". At this point, I
have just started a plink process and logged in. I want to now begin
sending it commands (run executables resident on the UNIX machine).
Did I set up my second thread correctly? Is there anything I am
missing?

If you're not expecting it to exit, then the two threads reading the
standard output and standard error will certainly block - they're meant
to. They will block until the program has finished, as otherwise they
won't know whether there's more data to come.
 
L

Lucian Wischik

Jon Skeet said:
So, it sounds like plink isn't exiting when you expect it to. What are
you doing which should make it exit?

As far as I remember, PLINK has two modes of execution:

(1) interactive shell. You start it, and it launches a remote shell
with redirected input/output. Once that remote shell has terminated,
then PLINK will also close its input+output handles. (How to close the
remote shell? -- normally by sending it CTRL-D, or typing "exit" or
"logout", but it depends on which remote shell is being used.)

(2) single-command. You pass a single command-string as an argument to
plink. It will open the connection and execute the command remotely,
redirecting input+output. If ever that command terminates, then the
PLINK's input+output handles will also close. (When will that remote
command terminate? -- it depeds entirely on the command.)
 
Z

Zenon

It looks to me that the problem is not with StandardError blocking
StandardOutput, but instead, StandardOutput's charBuffer is
overflowing. As I read the redirected output, everything works well
until the buffer gets to about 980 bytes. On the next pass it crashes.
Is there a way to clear the buffer? I tried DiscardBufferedData, but
that didn't seem to work.

thanks again
 
W

wfairl

No, you just need to read it. I looked at your code from the 19th and
you're still not launching a thread to read from the outputstream.

You need something like this:

Process p = new Process();
p.StartInfo.FileName = "MyProg.exe";
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute=false;

if(p.Start())
{
ManualResetEvent readerEvent = new ManualResetEvent(false);
StreamPoller errorPoller = new
StreamPoller(readerEvent,p.StandardOutput);
Thread outputThread = new Thread(new ThreadStart(outputPoller.Poll));
outputThread.Start();
p.WaitForExit();
readerEvent.Set();
...
}

public class StreamPoller
{

ManualResetEvent _readEvent;
StreamReader _reader;

public StreamPoller(ManualResetEvent readEvent,StreamReader reader)
{
_readEvent = readEvent;
_reader = reader;

}

public void Poll()
{

while(true)
{
if(_readEvent.WaitOne(1,true))
{
break;
}
//do whatever you want here with the redirected stdout
Console.Writeline(_reader.ReadLine());
}
}
}
 
Z

Zenon

Thanks very much for your help and code sample. I'm still working
through it and trying to completely understand it instead of pasting it
into my app without understanding it.

I learned an interesting thing with my code. If I add a line like
puttyProcess.StandardInput.WriteLine(something) in the read loop, the
code does not lock up. Does this WriteLine function empty some shared
read/write buffer? I realize that this doesn't make sense but....
Why would this writing affect the reading?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top