在C#中向cmd.exe进程发送ctrl+c命令

3

我正在编写一个小的C#备份程序。我使用标准的Windows表单作为界面,并调用cmd.exe作为新进程,然后在这个新进程中使用XCOPY。一切都很顺利,除了我想添加的最后一个功能——中断操作。

从本地命令提示符中,我可以通过ctrl+c干净地完成此操作,但是尝试了很多次后,我无法使用winforms和进程方法来复制此功能。我尝试重定向standardinput并使用它将consolespecialkeys.ControlC发送到进程,我还尝试发送0x03和“/x03”,这两个我在其他论坛帖子上读到的是ctrl+c的十六进制代码。但是我发送的任何内容都没有被注册,退出进程会杀死用户界面,但会让xcopy.exe在后台运行。手动终止xcopy.exe会导致复制的文件只有一半,且已损坏,这不是在命令提示符中使用ctrl+c时发生的情况。

我是否遗漏了一些显而易见的东西?我对C#还比较陌生,所以我承认这很可能是我的问题,或者我对进程如何与cmd.exe一起工作存在误解。但是,由于进程支持标准输入重定向,因此似乎应该可以解决...至少对我来说是这样。如果有帮助的话,我在下面放了我的代码基本大纲,以便确定我出错的地方。

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();

非常感谢您提前提供的任何帮助。
7个回答

14

我不想自以为是,但我认为你最好在程序内部进行复制。使用System.IO命名空间中的File、Directory和其他类非常简单,可以让你完全掌控进度报告、取消操作等。


非常感谢您的建议。说实话,我可能在使用XCOPY时有点过于固执己见了。一开始我选择使用它是因为它非常简单(而不是编写递归foreach循环),但随着我在项目中添加更多层次,我认为XCOPY已经成为处理任务的越来越低效的方式。现在我已经重新开始使用实际的复制引擎,使用了.Net的System.IO。我们将看看我在这方面的表现如何。再次感谢您的意见和快速回复。 - Dante Invidia

3

是的,在.NET中进行操作会更容易。但是,我还需要发送ctrl-c到一个进程,但我没有这个选项。

所以我们能否请您回答这个问题呢?

编辑:我必须发布重复问题才能得到答案吗?不,@j0rd4n没有回答这个问题。


2
像其他人说的那样,有更好的方法来完成特定的任务。但是,这并没有回答你的问题。我使用类似于你在这里展示的技术来自动化各种任务,并发现它非常有用。有时候事情会非常糟糕,你希望在事情变得更糟之前退出进程。
以下是你的示例存在的问题:
XCopyStartInfo.CreateNoWindow = true;
将其设置为false,然后它将处理XCopyProcess.CloseMainWindow()和XCopyProcess.Close()。比使用Kill()更加简洁。

1

只需循环遍历子目录和文件并逐个复制它们,这将需要更少的代码行,然后您就不必担心控制另一个进程了...


正如我之前对Tor Haugen所说的,我认为在项目的这个阶段使用XCOPY可能有点违反直觉。虽然当时看起来是个好主意。感谢您的建议和替代方法,现在将尝试使用该方法。 - Dante Invidia

1

抱歉,它是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

如果使用“CreateNoWindow=true”启动进程,则此方法无效。 - 3Dave

1

我已经成功地向一个使用SW_HIDE创建的cmd.exe进程发送了CTRL-C组合键,即隐藏的cmd.exe窗口。

技术上,我使用EnumWindows来识别该进程并获取其窗口句柄(即使它不可见,它仍然具有处理消息的句柄)。

接下来,我使用PostMessage将ctrl-c组合键发布到该进程。这与用户在窗口处于活动状态时按下“ctrl-c”具有相同的效果。

要从C#中执行此操作,您可能需要访问http://pinvoke.net/ - 在编写C#中的Win32 API函数原型时,这是一个救星。


0

如果您想以这种方式处理复制,那么您将不得不手动终止进程。在您上面的代码中,您正在调用XCopyProcess.WaitForExit(...)。这是一个阻塞调用,因此父C#进程将在该点停止,直到子进程完成或时间间隔已过。

您可以做的是,而不是阻塞,您可以在循环中睡眠,定期检查用户是否通过您的C# UI请求终止进程。如果收到此事件,则显式终止该进程。否则,您将等待另一个间隔,直到进程完成。

编辑:我确实同意其他评论。直接从.NET框架复制,而不是使用xcopy。


我曾经想过是waitforexit给了我问题,但在它和坚定地尝试向我的进程发送ctrl+c之间,我从未真正尝试过你建议的解决方案。最终,在这里社区的反馈下,我认为我将放弃XCOPY并转而使用.Net的本机能力,但仍然非常感谢您的建议和抽出时间来回答我的问题。非常感谢。 - Dante Invidia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接