Bug in .NET's Process model?

W

William Stacey [MVP]

This works fine in debug or release mode. I suspect your issue is Xcopy is
posting a message asking if target is a Dir name or a file - as it does if
target dir does not exist:
"C:\>xcopy dir1 dir5 /e/y
Does dir3 specify a file name
or directory name on the target
(F = file, D = directory)?"

So you never see any stdOut and process seems to hang, but is actually
waiting on input from you. As your blocking on stdOut, you never get the
point of waiting for process to exit. So your stuck. You need to read std
out and std err in seperate threads and start those before you block waiting
for exit. Then you capture the error. Here is example. Could use some
more work, but shows the msg xcopy gives you asking if dir5 is a directory:

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace XcopyTest
{
class Program
{
static void Main(string[] args)
{
ProcessStartInfo s = new ProcessStartInfo("cmd.exe");
s.Arguments = @"/c xcopy c:\dir1 c:\dir5 /e /y";
s.CreateNoWindow = true;
s.RedirectStandardOutput = true;
s.RedirectStandardError = true;
s.UseShellExecute = false;
s.WindowStyle = ProcessWindowStyle.Hidden;
int exitCode;

Process p = new Process();
p.StartInfo = s;
p.Start();

StringBuilder sbErr = new StringBuilder();
Thread errThread = new Thread(delegate()
{
string line = null;
try
{
while ( (line = p.StandardError.ReadLine()) != null )
{
sbErr.AppendLine(line);
}
}
catch ( Exception ex )
{
Console.WriteLine("******** " + ex.Message);
}
});
errThread.Start();

StringBuilder sbOut = new StringBuilder();
Thread stdThread = new Thread(delegate()
{
string line = null;
try
{
while ( (line = p.StandardOutput.ReadLine()) != null )
{
sbOut.AppendLine(line);
}
}
catch ( Exception ex )
{
Console.WriteLine("******** " + ex.Message);
}
});
stdThread.Start();

if ( !p.WaitForExit(5 * 1000) )
{
p.Kill();
stdThread.Interrupt();
errThread.Interrupt();
}
stdThread.Join();
errThread.Join();
exitCode = p.ExitCode;
p.Close();

Console.WriteLine("ExitCode:" + exitCode);
Console.WriteLine("Std out:"+ sbOut.ToString());
Console.WriteLine("Std Err:"+ sbErr.ToString());
Console.ReadLine();
}
}
}
 
G

Guest

I had thought that was the issue as well, but the behavior I'm seeing is a
valid return code and no stdout, not a hung process (I'm using the /I swith
to avoid the dir issue, I'm also starting xcopy directly, not as an argument
to cmd). The process DOES return with a 0 exit code, it just hasn't copied
files or spit anything out to stdout or stderr. Also, that doesn't explain
why it works when run inside .NETs debugger yet fails when it is run by
windows outside of the IDE. Attaching the debugger after the process has
already been started outside of the IDE also shows that the process does not
hang. Thanks for the suggestions!
 
G

Guest

OK, turns out I wasn't using the /I switch. Adding this switch seems to fix
the immediate problem, yet raises even more questions.

1) Why did this work when run in the context of the .NET IDE debugger
without the /I switch?
2) Why did the debugger, when attached to this process running outside of
the IDE, step past the p.WaitForExit()?

Thanks!
 
W

William Stacey [MVP]

It is difficult for us to keep guessing without seeing any code. Please
post the sample code that shows this problem.
 
G

Guest

Yeah, i shoulda put this up to begin with, but I have to manually type it in
so I was tryng to avoid it...


/*************************************************

System.String output = "";

System.Diagnostics.Process p = new System.Diagnostics.Process();
//searches PATH for the executable
p.StartInfo.Filename = "xcopy.exe";
p.Arguments = "C:\\dir1 C:\\dir2 /E /Y /V /I";
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();

//read stdout and stderr before waiting for exit
output += p.StandardOutput.ReadToEnd();
output += p.StandardError.ReadToEnd();

p.WaitForExit();

long l = p.ExitCode;
System.Diagnostics.Debug.WriteLine("returned: " + l.ToString() + " :: " +
output);
 
W

William Stacey [MVP]

Debug.WriteLine does not output anything in Release mode - only in debug
mode. So the below code works in debug and release:
System.String output = "";

System.Diagnostics.Process p = new System.Diagnostics.Process();
//searches PATH for the executable
p.StartInfo.FileName = "xcopy.exe";
p.StartInfo.Arguments = "C:\\dir1 C:\\dir5 /E /Y /V /I";
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();

//read stdout and stderr before waiting for exit
output += p.StandardOutput.ReadToEnd();
output += p.StandardError.ReadToEnd();

p.WaitForExit();

long l = p.ExitCode;
Console.WriteLine("returned: " + l.ToString() + " :: " +
output);
Console.WriteLine("Done.");
Console.ReadLine();

Couple other points.
1) For console programs, I turn off the redirect to Quick Console to help
me avoid some of these issues.
2) If this is a general method, I would read both std out and std err in
seperate threads so you don't block forever if you have some program waiting
for input for some unknown reason.
HTH
 
G

Guest

this is a general method. After looking into it more, I get

"The path is not of a legal form"

from either stdout or stderr.... I don't think the xcopy binary even
contans that string. But xcopy seems to work fine if I just use
System.Diagnostics.Process.Strat("xcopy", args); Again, I only get this
strange behavior when I run the app outside the debugger.
 

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