none
Problems with Process class in Windows 7 RRS feed

  • Question

  • Hi everybody,

    I wrote a program that copys a directory from on PC into all other PCs in this LAN. Since the files are huge, I wrote it with a different thread. This is the most crucial part of the program (copy to one PC only):

            private void StartCommand(string cmd, string cmdArgs = "")
            {
                output.Clear();
                error.Clear();

                using (var p = new Process())
                {

                    p.StartInfo.FileName = "cmd.exe";
     
                    p.StartInfo.Arguments = @"/c xcopy D:\SourceDir \\pc-name\D$\DestName /k/r/e/i/s/c/h/f/o/x/y";

                    p.StartInfo.RedirectStandardError = true;
                    // P.StartInfo.RedirectStandardInput = true;
                    p.StartInfo.RedirectStandardOutput = true;
                    p.StartInfo.CreateNoWindow = true;
                    p.StartInfo.UseShellExecute = false;

                    using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false))
                    using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false))
                    {
                        p.ErrorDataReceived += (sender, e) =>
                        {
                            if (e.Data == null)
                            {
                                errorWaitHandle.Set();
                            }
                            else
                            {
                                error.AppendLine(e.Data);
                                // UpdateRtbMethod(Color.Red, e.Data);
                            }
                        };

                        p.OutputDataReceived += (sender, e) =>
                        {
                            if (e.Data == null)
                            {
                                outputWaitHandle.Set();
                            }
                            else
                            {
                                output.AppendLine(e.Data);
                                // UpdateRtbMethod(Color.Green, e.Data);
                            }
                        };


                        p.Start();

                        p.BeginErrorReadLine();
                        p.BeginOutputReadLine();

                        int timeout = 1000000;

                        if ((p.WaitForExit(timeout)) && (outputWaitHandle.WaitOne(/*timeout*/)) && errorWaitHandle.WaitOne(/*timeout*/))
                        {
                            // Process completed. Check process.ExitCode here.
                            if (output.Length > 0)
                                OnCommandFinished(true, output.ToString());
                            else if (error.Length > 0)
                            {
                                OnCommandFinished(false, error.ToString());
                            }
                            else
                            {
                                OnCommandFinished(false, "Unspecified error");
                            }
                        }
                        else
                        {
                            // Timed out.
                            OnCommandFinished(false, "The Command process timed out");
                        }

    ....

    Unfortunately, I never got this to work. Also, I don't even get an error message since the Data part in the DataErrorReceived event is always null. Of couse, it works if I execute the xcopy command in a console. I guess it is because of some text formatting/escaping in the Arguments property. I have actually tried to integrate the xcopy command and the options in a batch file, calling the batch file from the above code, but all get's worse: I don't get any message anymore. It took me days to write this program, but then I tried it with Python where I used subprocess:

    ...

    #
    DestLanPaths = [r'\\' + x + r'\d$\DestDir' for x in AllTargetComputerNames]

    # print(SourceLanPath)

    # print(DestLanPaths)

    for destLanPath in DestLanPaths :
       subprocess.call(['xcopy', SourceLanPath, destLanPath, args])

    And the Python program worked more or less at once! I guess that the Python subprocess.call method handles the different arguments of xcopy much better than the C# version. Any ideas why the c# version doesn't work? Did I do something wrong in the C# code?

    Best regards

    Johannes

    Monday, September 10, 2018 1:09 PM

All replies

  • What is your purpose? Cause your program isn't using any threading at all.

    I would look into ROBOCOPY first, cause it can do multi-threaded copying (/MT:x).


    Monday, September 10, 2018 1:34 PM
  • As Stefan mentions, if I were to want to do this then I'd use robocopy instead of xcopy.

    But ignoring that I question why you'd bother calling out to xcopy. Just use the framework and copy the files yourself. MSDN has an example under common IO on how to do exactly this. Assuming you want to do this across multiple machines at the same time then you can just use async/await or similar code.

    //Assuming MSDN code mentioned earlier and that it isn't async
    public void CopyDirectory ( string sourceDirectory, string targetDirectory, bool copySubDirs ) { … }
    
    public async Task CopyDirectory ( string sourceDirectory, IEnumerable<string> targetDirectories )
    {
       var tasks = new List<Task>();
    
       foreach (var target in targetDirectories)
          tasks.Add(Task.Run(() => CopyDirectory(source, target, true));
       
       //Using await here but you could use Task.WaitAll too
       foreach (var task in tasks)
          await task.ConfigureAwait(false);
    }

    Note - above code not tested.

    As for the error you're seeing I'd learn toward a couple of possibilities. Firstly you're using the admin share (e.g. D$). That means the program must be running with admin rights. If you have UAC enabled this isn't going to be true by default so I'd expect an error. Your handling of the process output doesn't seem correct to me but I didn't dig into it too much because, again, I don't think you need any of it. So I suspect that (barring permissions) just using the framework class directly will solve your issue.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, September 10, 2018 1:50 PM
    Moderator
  • Hello Stefan,

    thanks for your answer. I didn't show the threading because it would just enlarge the shown code snippet for no reason. Let's say the code snippet is called by another thread. The problem is that it just doesn't copy the directories without returning any error. But I know that the problem is not because of the threading, I had the problem without threading as well (+ the GUI blocking which is my purpose of using threading). I probably shouldn't have mentioned threading. I know about ROBOCOPY, but it allegedly had/has some of its own problems/bugs: https://stackoverflow.com/questions/40744335/how-do-i-force-robocopy-to-overwrite-files/40750265#40750265. Finally, I know that my xcopy command *does* work when I execute it directly in a console (cmd). Of couse, I use an admin account to do this, so the rights shouldn't be a problem either.

    But I will give ROBOCOPY a try.

    Best regards

    Johannes

    Tuesday, September 11, 2018 6:44 AM
  • Hello Michael,

    thanks for your answer and your code. Good idea, I will try that out. Probably 5 years ago, I would have done it with the framework. Since then I haven't programmed in C# much, unfortunately. Instead, I did C++/Linux/bash/Python and there it is so common to use system commands in larger programs. About using xcopy, see my answer to Stefan. I certainly use an admin account. Doesn't that mean that my c# (Winforms) program runs with admin account as well?

    I guess that there is a problem with stuff like back slash escaping, quotes, xcopy arguments, going from the Process class into the cmd.exe. In Python I had similar problems with os.system() instead of subprocess.call().

    Best regards

    Johannes

    Tuesday, September 11, 2018 7:03 AM
  • Well, the other OT-question: why not doing it in PowerShell?

    Tuesday, September 11, 2018 9:01 AM
  • "Doesn't that mean that my c# (Winforms) program runs with admin account as well?"

    Depends. having an account as admin doesn't mean you are running as admin because of UAC. To run as admin you have to elevate the user token. That happens when you start the app and get the UAC prompt. If your app has a manifest saying it must run as admin then it'll trigger automatically otherwise the shortcut has to be set to do that or you have to right click "run as administrator".

    "I guess that there is a problem with stuff like back slash escaping"

    Shouldn't be a problem in C#. Verbatim strings (like you're using for the xcopy line) solves this. 

    My gut instinct is that the issue you're seeing is around all that wait logic you have. I didn't put much thought into it because I don't think calling out to the OS is the correct solution here but if I were try to debug this issue I'd start there. Your wait logic seems "off" to me.


    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, September 11, 2018 1:37 PM
    Moderator

  • I guess that there is a problem with stuff like back slash escaping, quotes, xcopy arguments, going from the Process class into the cmd.exe. 

    FWIW, I created this small repro of a portion of your code and it worked
    as expected - it copied all files and subdirs from source to destination.

    static void Main(string[] args)
    {
        var p = new Process())
    
        p.StartInfo.FileName = "cmd.exe";
    
        //p.StartInfo.Arguments = @"/c xcopy D:\SourceDir \\pc-name\D$\DestName /k/r/e/i/s/c/h/f/o/x/y";
        //p.StartInfo.Arguments = @"/c xcopy C:\Temp C:\Temp2 /k/r/e/i/s/c/h/f/o/x/y";
        p.StartInfo.Arguments = @"/c xcopy C:\Temp C:\Temp2 /k/r/e/i/s/c/h/f/y";
    
        p.StartInfo.RedirectStandardError = true;
        // P.StartInfo.RedirectStandardInput = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.UseShellExecute = false;
    
        p.Start();
    }
    
    

    So I think you can dismiss the theory that the argument string is not 
    being parsed correctly. Try a similar small sample doing xcopy on a local
    drive to confirm this yourself.

    Note that I removed the /x and /o switches as they will trigger an 
    "Access denied" unless running as Administrator. As noted by Michael,
    I'm running Win7 with UAC enabled so I would have to *explicitly* run
    as Administrator to avoid these denials.

    With the /x and /o switches added I must right-click on the exe and choose
    "Run As Administrator" for it to work. This is so even though I'm the only
    user registered on this PC and obviously have Admin rights.

    - Wayne

    Tuesday, September 11, 2018 4:24 PM