我有几个不同的建议:
您可以研究使用MSBuild而不是NMake
它更为复杂,但可以直接从.NET控制,并且是VS 2010及更早版本的C#/VB等项目的项目文件格式。
您可以使用一个小助手程序捕获环境并将其注入到您的进程中
这可能有点过度,但它是可行的。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();
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))
{
}
}
如果您使用此方法,请注意,如果缺少或失败vcvarsall.bat,则可能会出现严重问题,并且在其他语言环境下可能会遇到问题。