gpt4 book ai didi

c# - 从 C# 在同一环境中执行多个命令

转载 作者:可可西里 更新时间:2023-11-01 13:41:21 25 4
gpt4 key购买 nike

我正在开发一个小型 C# GUI 工具,它应该获取一些 C++ 代码并在通过一些向导后对其进行编译。如果我在运行著名的 vcvarsall.bat 后从命令提示符运行它,这一切都很好。现在我希望用户不要先进入命令提示符,而是让程序调用 vcvars,然后调用 nmake 和我需要的其他工具。为此,显然应该保留 vcvars 设置的环境变量。

我该怎么做?

我能找到的最佳解决方案是创建一个临时的 cmd/bat 脚本来调用其他工具,但我想知道是否有更好的方法。


更新:我同时尝试了批处理文件和 cmd。使用批处理文件时,vcvars 将终止完整的批处理执行,因此不会执行我的第二个命令(即 nmake)。我目前的解决方法是这样的(缩短):

string command = "nmake";
string args = "";
string vcvars = "...vcvarsall.bat";
ProcessStartInfo info = new ProcessStartInfo();
info.WorkingDirectory = workingdir;
info.FileName = "cmd";
info.Arguments = "/c \"" + vcvars + " x86 && " + command + " " + args + "\"";
info.CreateNoWindow = true;
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
Process p = Process.Start(info);

这有效,但未捕获 cmd 调用的输出。仍在寻找更好的东西

最佳答案

我有几个不同的建议

  1. 您可能想研究使用 MSBuild 而不是 NMake

    比较复杂,但是可以直接从.Net控制,是VS 2010开始的所有项目,C#/VB等的VS项目文件的格式。早于此的项目

  2. 您可以使用一个小的帮助程序捕获环境并将其注入(inject)您的进程

    这可能有点矫枉过正,但它会起作用。 vsvarsall.bat 没有做比设置一些环境变量更神奇的事情,所以您所要做的就是记录运行它的结果,然后将其重播到您创建的进程的环境中。

帮助程序 (envcapture.exe) 很简单。它只是列出其环境中的所有变量并将它们打印到标准输出。这是整个程序代码;将其粘贴到 Main() 中:

XElement documentElement = new XElement("Environment");
foreach (DictionaryEntry envVariable in Environment.GetEnvironmentVariables())
{
documentElement.Add(new XElement(
"Variable",
new XAttribute("Name", envVariable.Key),
envVariable.Value
));
}

Console.WriteLine(documentElement);

您可能只需调用 set 而不是此程序并解析该输出,但如果任何环境变量包含换行符,这可能会中断。

在您的主程序中:

首先,必须抓取vcvarsall.bat初始化的环境。为此,我们将使用类似于 cmd.exe/s/c ""...\vcvarsall.bat"x86 && "...\envcapture.exe"" 的命令行. vcvarsall.bat修改环境,然后envcapture.exe打印出来。然后,主程序捕获该输出并将其解析为字典。 (注意:此处的 vsVersion 可能是 90、100 或 110)

private static Dictionary<string, string> CaptureBuildEnvironment(
int vsVersion,
string architectureName
)
{
// assume the helper is in the same directory as this exe
string myExeDir = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location
);
string envCaptureExe = Path.Combine(myExeDir, "envcapture.exe");
string vsToolsVariableName = String.Format("VS{0}COMNTOOLS", vsVersion);
string envSetupScript = Path.Combine(
Environment.GetEnvironmentVariable(vsToolsVariableName),
@"..\..\VC\vcvarsall.bat"
);

using (Process envCaptureProcess = new Process())
{
envCaptureProcess.StartInfo.FileName = "cmd.exe";
// the /s and the extra quotes make sure that paths with
// spaces in the names are handled properly
envCaptureProcess.StartInfo.Arguments = String.Format(
"/s /c \" \"{0}\" {1} && \"{2}\" \"",
envSetupScript,
architectureName,
envCaptureExe
);
envCaptureProcess.StartInfo.RedirectStandardOutput = true;
envCaptureProcess.StartInfo.RedirectStandardError = true;
envCaptureProcess.StartInfo.UseShellExecute = false;
envCaptureProcess.StartInfo.CreateNoWindow = true;

envCaptureProcess.Start();

// read and discard standard error, or else we won't get output from
// envcapture.exe at all
envCaptureProcess.ErrorDataReceived += (sender, e) => { };
envCaptureProcess.BeginErrorReadLine();

string outputString = envCaptureProcess.StandardOutput.ReadToEnd();

// vsVersion < 110 prints out a line in vcvars*.bat. Ignore
// everything before the first '<'.
int xmlStartIndex = outputString.IndexOf('<');
if (xmlStartIndex == -1)
{
throw new Exception("No environment block was captured");
}
XElement documentElement = XElement.Parse(
outputString.Substring(xmlStartIndex)
);

Dictionary<string, string> capturedVars
= new Dictionary<string, string>();

foreach (XElement variable in documentElement.Elements("Variable"))
{
capturedVars.Add(
(string)variable.Attribute("Name"),
(string)variable
);
}
return capturedVars;
}
}

之后,当你想在构建环境中运行命令时,你只需要将新进程中的环境变量替换为之前捕获的环境变量即可。每次运行程序时,您只需要为每个参数组合调用一次 CaptureBuildEnvironment。不要试图在运行之间保存它,否则它会变得陈旧。

static void Main()
{
string command = "nmake";
string args = "";

Dictionary<string, string> buildEnvironment =
CaptureBuildEnvironment(100, "x86");

ProcessStartInfo info = new ProcessStartInfo();
// the search path from the adjusted environment doesn't seem
// to get used in Process.Start, but cmd will use it.
info.FileName = "cmd.exe";
info.Arguments = String.Format(
"/s /c \" \"{0}\" {1} \"",
command,
args
);
info.CreateNoWindow = true;
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
foreach (var i in buildEnvironment)
{
info.EnvironmentVariables[(string)i.Key] = (string)i.Value;
}

using (Process p = Process.Start(info))
{
// do something with your process. If you're capturing standard output,
// you'll also need to capture standard error. Be careful to avoid the
// deadlock bug mentioned in the docs for
// ProcessStartInfo.RedirectStandardOutput.
}
}

如果您使用它,请注意,如果 vcvarsall.bat 丢失或失败,它可能会死得很惨,并且使用非 en-US 语言环境的系统可能会出现问题。

关于c# - 从 C# 在同一环境中执行多个命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14006897/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com