views:

2307

answers:

6

I'm currently writing myself a little C# back up program. I'm using a standard windows form for the interface, and am calling cmd.exe as a new process, and then using XCOPY from within this new process. Every thing's working great, except for this last feature I want to add in, which is the ability to break the operation.

From a native command prompt, I can do this cleanly with ctrl+c, but try as I might, I can't replicate this functionality using the winforms and process approach. I've tried redirecting the standardinput and using that to send consolespecialkeys.ControlC to the process, I've also tried sending 0x03 and "/x03", both of which I've read on other forum posts are hex code for ctrl+c. Nothing I'm sending is registered though, and exiting the process kills the user interface, but leaves the xcopy.exe working in the background. Killing xcopy.exe manually results in it leaving the file it was copying half copied and corrupted, not something that happens using the ctrl+c in a command prompt.

Am I missing something blindingly obvious? I'm new-ish to C#, so I'll hold my hands up and admit this is most likely me being slow, or misunderstanding how the process is working with cmd.exe. However, since processes support standard input redirection, it seems like something that should work... to me at least. I've put the basic outline of my code below, in case it helps identify where I'm messing up.

string XCopyArguments = "\"" + dir.FullName + "\" \"" + destination + "\" /D /S /I /E";  
Process XCopyProcess = new Process();  
ProcessStartInfo XCopyStartInfo = new ProcessStartInfo(); 
XCopyStartInfo.FileName = "CMD.exe ";  
XCopyStartInfo.RedirectStandardError = true;
XCopyStartInfo.RedirectStandardOutput = true;
XCopyStartInfo.RedirectStandardInput = true;
XCopyStartInfo.UseShellExecute = false;
XCopyStartInfo.CreateNoWindow = true;
XCopyStartInfo.Arguments = " /D /c XCOPY " + XCopyArguments;
XCopyProcess.EnableRaisingEvents = true;
XCopyProcess.StartInfo = XCopyStartInfo;
XCopyProcess.Start();                
XCopyProcess.WaitForExit(15000);
int ExitCode = XCopyProcess.ExitCode;
if (ExitCode > 0 & !XCopyProcess.HasExited)
{
XCopyProcess.Kill();
}
XCopyProcess.Dispose();

Many thanks in advance for any help anyone can offer.

+13  A: 

Hi.

I don't want to be a besserwisser, but I think you'd be much better off doing the copying inside your program. Using File, Directory and the other classes in the System.IO namespace, it's really simple, and leaves you in full control to report progress, cancel operations etc.

Tor Haugen
Many thanks for the suggestion.To be honest, I'd probably gotten a little blinkered in my determination to use XCOPY. I'd started out using that for pure simplicity (rather than writing recursive foreach loops), but as I started to add more layers to my project, I think XCOPY has become a less and less efficient way to handle the job. I've somewhat started over on the actual copy engine now, using .Net's System.IO. We'll see how I get on with that.Again, many thanks for the input and speedy reply.
Dante Invidia
+1  A: 

It would require fewer code lines to just loop over the subdirectories and files and copy them one by one and then you wouldn't have to worry about controling another process...

danbystrom
As I said to Tor Haugen above, I think you're right that using XCOPY is probably a little counter intuitive at this stage in the project. Seemed like a good idea at the time, though.Thanks for your input and suggestion for an alternate approach, going to try out that approach instead now.
Dante Invidia
A: 

You'll have to manually kill the process if you want to handle the copying this way. In your code above, you are calling XCopyProcess.WaitForExit(...). This is a blocking call so the parent C# process will halt at that point until the child process has finished or the time-interval has elapsed.

What you could do is instead of blocking, you can sleep in a loop routinely checking if the user has requested to kill the process via your C# UI. If you receive this event, you explicitly kill the process. Otherwise, you wait for another interval until the process is finished.

EDIT: I do agree with the other comments, though. Copy directly from the .NET framework instead of using xcopy.

j0rd4n
I had wondered if that waitforexit was giving me problems, but between it and doggedly sticking to trying to send ctrl+c to my process, I never really gave the solution you suggested a shot. In the end, and in light of the community feedback here, I think I'm going to ditch XCOPY anyhow and go with.Net's native abilities instead, but still, thank you very much for the advice, and for taking time to answer. It's much appreciated.
Dante Invidia
+1  A: 

Yes, doing the operation in .NET would be easier. BUT, I need to send ctrl-c to a process also and I don't have that option.

So can we please get an answer to this question?

EDIT: Do I have to post a duplicate to get an answer? And no, @j0rd4n didn't answer the question.

Nick Whaley
@j0rd4n posted your answer.
runako
A: 

Sorry it's in VB.NET.

Declare Function GenerateConsoleCtrlEvent Lib "kernel32" ( _
                    ByVal dwCtrlEvent As Integer, _
                    ByVal dwProcessGroupId As Integer _
                    ) As Integer

Private Const CTRL_C_EVENT As Integer = 0

Private Sub SendCtrlC()
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)

    ' send a Ctrl-C to this process
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, currentpid)

    ' send a Ctrl-C to the cmd process
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, cmdpid)
End Sub
pyrokin5
This doesn't work if the process is started with "CreateNoWindow=true".
David Lively
+1  A: 

Like the others said, there are better ways to accomplish that particular task. However, that doesn't answer your question. I use a similar technique to what you have shown here for automating various tasks and find it quite useful. Sometimes things go very badly though and you want the process to bail out before things get worse. ;p

Here is the problem with your example:

XCopyStartInfo.CreateNoWindow = true;

Set it to false and it will then process XCopyProcess.CloseMainWindow() and XCopyProcess.Close(). Much cleaner than using Kill().

ruthy