
下面代码使用了异步读取(通过事件)来实时显示脚本输出,并在执行完毕后返回退出值,最后等待用户按键暂停控制台窗口:
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
int exitCode = RunBatchFile("test.bat");
Console.WriteLine($"Exit Code: {exitCode}");
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
static int RunBatchFile(string filePath)
{
try
{
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = filePath,
UseShellExecute = false, // 必须为false以支持重定向
RedirectStandardOutput = true, // 重定向标准输出
RedirectStandardError = true, // 重定向错误输出
CreateNoWindow = true // 不显示窗口
};
using (Process process = new Process { StartInfo = psi, EnableRaisingEvents = true })
{
// 异步处理标准输出
process.OutputDataReceived += (sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Console.WriteLine(args.Data);
}
};
// 异步处理错误输出
process.ErrorDataReceived += (sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Console.Error.WriteLine(args.Data);
}
};
process.Start();
// 开始异步读取输出
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit(); // 等待脚本执行结束
return process.ExitCode; // 返回退出码
}
}
catch (Exception ex)
{
Console.WriteLine($"Error running batch file: {ex.Message}");
return -1;
}
}
}
代码说明
-
异步读取输出
通过OutputDataReceived和ErrorDataReceived事件,在脚本执行过程中实时将输出打印到控制台。 -
重定向与不显示窗口
设置UseShellExecute = false、RedirectStandardOutput = true、RedirectStandardError = true和CreateNoWindow = true,确保可以重定向输出且不弹出额外窗口。 -
获取退出码并暂停
脚本执行完毕后返回退出码,并在控制台中显示,最后调用Console.ReadKey()暂停程序,等待用户按键后退出。
这样运行 RunBatchFile("test.bat") 后,控制台会实时显示脚本输出,最后显示退出值并等待用户按键。
用c++也写一个同样的
下面示例代码利用 Windows API(CreateProcess 和管道)实现运行批处理文件、实时输出内容、获取退出码,并在结束后暂停控制台窗口。该代码将标准输出和错误合并到同一个管道中,实时读取并输出到控制台。
#include <windows.h>
#include <iostream>
#include <string>
int RunBatchFile(const std::string& batchFile)
{
// 设置安全属性,允许管道句柄被子进程继承
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// 创建管道,用于重定向子进程的标准输出和错误(合并)
HANDLE hChildStd_Rd = NULL;
HANDLE hChildStd_Wr = NULL;
if (!CreatePipe(&hChildStd_Rd, &hChildStd_Wr, &saAttr, 0))
{
std::cerr << "创建管道失败" << std::endl;
return -1;
}
// 确保读句柄不被继承
if (!SetHandleInformation(hChildStd_Rd, HANDLE_FLAG_INHERIT, 0))
{
std::cerr << "设置句柄属性失败" << std::endl;
return -1;
}
// 设置启动信息,指定子进程的标准输出和错误句柄
PROCESS_INFORMATION piProcInfo;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
STARTUPINFOA siStartInfo;
ZeroMemory(&siStartInfo, sizeof(STARTUPINFOA));
siStartInfo.cb = sizeof(STARTUPINFOA);
siStartInfo.hStdOutput = hChildStd_Wr;
siStartInfo.hStdError = hChildStd_Wr; // 合并错误输出
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// 构造命令行:使用 cmd.exe /c 执行批处理文件
std::string cmdLine = "cmd.exe /c " + batchFile;
// CreateProcess 要求可写的字符串缓冲区
char* cmd = new char[cmdLine.size() + 1];
strcpy_s(cmd, cmdLine.size() + 1, cmdLine.c_str());
// 创建子进程
BOOL bSuccess = CreateProcessA(
NULL, // 应用程序名称
cmd, // 命令行
NULL, // 进程安全属性
NULL, // 线程安全属性
TRUE, // 句柄继承
0, // 创建标志
NULL, // 使用父进程的环境变量
NULL, // 使用父进程的当前目录
&siStartInfo, // STARTUPINFO 指针
&piProcInfo); // PROCESS_INFORMATION 指针
delete[] cmd; // 释放申请的内存
if (!bSuccess)
{
std::cerr << "CreateProcess 失败,错误码:" << GetLastError() << std::endl;
CloseHandle(hChildStd_Rd);
CloseHandle(hChildStd_Wr);
return -1;
}
else
{
// 子进程已经继承写句柄,父进程可以关闭写句柄
CloseHandle(hChildStd_Wr);
// 实时读取子进程输出
const DWORD BUFSIZE = 4096;
char buffer[BUFSIZE];
DWORD dwRead;
BOOL bReadSuccess = FALSE;
while (true)
{
bReadSuccess = ReadFile(hChildStd_Rd, buffer, BUFSIZE - 1, &dwRead, NULL);
if (!bReadSuccess || dwRead == 0)
break;
buffer[dwRead] = '\0'; // 添加字符串结束符
std::cout << buffer;
}
// 等待子进程结束
WaitForSingleObject(piProcInfo.hProcess, INFINITE);
// 获取退出码
DWORD exitCode;
if (!GetExitCodeProcess(piProcInfo.hProcess, &exitCode))
{
exitCode = -1;
}
// 关闭句柄
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
CloseHandle(hChildStd_Rd);
return static_cast<int>(exitCode);
}
}
int main()
{
int exitCode = RunBatchFile("test.bat");
std::cout << "\n退出码: " << exitCode << std::endl;
std::cout << "按任意键继续..." << std::endl;
system("pause");
return 0;
}
代码说明
-
管道设置
使用CreatePipe创建一个管道,并通过SetHandleInformation确保读取句柄不被子进程继承。 -
启动信息设置
在STARTUPINFOA中指定了hStdOutput和hStdError为管道写入端,使用cmd.exe /c形式执行批处理文件。 -
实时读取输出
通过ReadFile循环读取管道中的数据,将子进程输出实时显示到控制台。 -
获取退出码
使用WaitForSingleObject等待子进程结束,然后调用GetExitCodeProcess获取批处理文件的退出码。 -
暂停控制台
在main函数中调用system("pause")等待用户按键后退出。
这样运行后,控制台会实时显示 test.bat 的输出,结束后显示退出码,并等待用户按键。
4000

被折叠的 条评论
为什么被折叠?



