Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从C在同一环境中执行多个命令# 我正在开发一个小的C++ gui工具,它可以在获取一些向导代码之后,通过一些向导编译它。如果我在运行著名的vcvvarsall.bat之后从命令提示符下运行它,这一切都很好。现在,我希望用户不要首先进入命令提示符,而是让程序调用vcvars,然后是nmake和我需要的其他工具。为此,显然应保留vcvars设置的环境变量_C#_Windows_Visual Studio_Process - Fatal编程技术网

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

C# 从C在同一环境中执行多个命令# 我正在开发一个小的C++ gui工具,它可以在获取一些向导代码之后,通过一些向导编译它。如果我在运行著名的vcvvarsall.bat之后从命令提示符下运行它,这一切都很好。现在,我希望用户不要首先进入命令提示符,而是让程序调用vcvars,然后是nmake和我需要的其他工具。为此,显然应保留vcvars设置的环境变量,c#,windows,visual-studio,process,C#,Windows,Visual Studio,Process,我该怎么做 我能找到的最佳解决方案是创建一个临时的cmd/bat脚本,它将调用其他工具,但我想知道是否有更好的方法 更新:我同时尝试了批处理文件和cmd。使用批处理文件时,vcvars将终止完整的批处理执行,因此不会执行第二个命令(即nmake)。我当前的解决方法如下(缩短): 这是可行的,但是没有捕获cmd调用的输出。仍然在寻找更好的可能没有比收集所有需要的数据、生成bat文件并使用Process类运行它更好的方法了。 在编写时,您正在重定向输出,这意味着您必须设置UseShellExecu

我该怎么做

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


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


这是可行的,但是没有捕获cmd调用的输出。仍然在寻找更好的

可能没有比收集所有需要的数据、生成bat文件并使用
Process
类运行它更好的方法了。
在编写时,您正在重定向输出,这意味着您必须设置
UseShellExecute=false因此,我认为除了从bat文件调用set之外,没有办法设置变量。

我有几个不同的建议

  • 您可能希望使用MSBuild而不是NMake进行研究

    它更复杂,但可以直接从.Net进行控制,对于从VS 2010开始的所有项目以及更早的C#/VB/等项目,它都是VS项目文件的格式

  • 您可以使用一个小型助手程序捕获环境,并将其注入到您的流程中

    这可能有点过分,但它会起作用。bat没有比设置几个环境变量更神奇的功能,所以您所要做的就是记录运行它的结果,然后将其重放到您创建的进程环境中

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

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

    在您的主程序中:

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


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

    编辑:添加nmake调用的特定用例

    在过去,我需要得到各种“构建路径的东西”,这就是我所使用的-你可能需要在这里或那里调整东西以适应,但基本上,vcvars所做的唯一事情就是设置一组路径;这些helper方法用于获取这些路径名,您只需将它们传递到开始信息:

    public static string GetFrameworkPath()
    {
        var frameworkVersion = string.Format("v{0}.{1}.{2}", Environment.Version.Major, Environment.Version.Minor, Environment.Version.Build);
        var is64BitProcess = Environment.Is64BitProcess;
        var windowsPath = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
        return Path.Combine(windowsPath, "Microsoft.NET", is64BitProcess ? "Framework64" : "Framework", frameworkVersion);  
    }
    
    public static string GetPathToVisualStudio(string version)
    {   
        var is64BitProcess = Environment.Is64BitProcess;
        var registryKeyName = string.Format(@"Software\{0}Microsoft\VisualStudio\SxS\VC7", is64BitProcess ? @"Wow6432Node\" : string.Empty);
        var vsKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKeyName);
        var versionExists = vsKey.GetValueNames().Any(valueName => valueName.Equals(version));
        if(versionExists)
        {
            return vsKey.GetValue(version).ToString();
        }
        else
        {
            return null;
        }
    }
    
    你可以通过以下方式利用这些信息:

    var paths = new[]
        { 
            GetFrameworkPath(), 
            GetPathToVisualStudio("10.0"),
            Path.Combine(GetPathToVisualStudio("10.0"), "bin"),
        };  
    
    var previousPaths = Environment.GetEnvironmentVariable("PATH").ToString();
    var newPaths = string.Join(";", previousPaths.Split(';').Concat(paths));
    Environment.SetEnvironmentVariable("PATH", newPaths);
    
    var startInfo = new ProcessStartInfo()
    {
        FileName = "nmake",
        Arguments = "whatever you'd pass in here",
    };
    var process = Process.Start(startInfo);
    

    目前我打开一个VC命令提示符,在这里我启动我的程序,这个程序创建了一个ProcessStartInfo对象,具有正确的WorkingDir,Filename=“namke”,并重定向了我们的输出。。。这里有一个Make继承了我的程序环境,它继承了vc环境好奇的是:Downvoter,你能解释一下downvote吗?谢谢。我想我对以下问题的回答可能会对你有所帮助。是的,正如我所说的,这是我目前的解决办法。。。虽然这有太多的缺陷(很难看到哪一步出错,很难分割输出日志文件,…),所以我在寻找更好的方法:-)@johannes其实没那么难,只需处理
    process.ErrorDataReceived
    事件,并在进程之间插入一些
    echo
    。是的,然后解析输出,希望echo序列是真正唯一的,并且。。。如前所述:这是可能的,但很烦人。@johannes,但为什么不在每个命令之前添加**ECHO************命令名******?选项一是没有选择的,因为它的系统更复杂,我无法控制。在vcvars调用之后,我如何获得环境?我添加了一些更清晰的注释,说明了这三个代码段实际上在做什么。您尝试过吗我现在手头上没有Windows框,但是在运行问题代码时,我无法捕获cmd输出,因为这会创建一个不可见的窗口(由于CreateNoWIndow),并将输出粘贴到那里,使其无法通过进程的标准输出流
    CreateNoWIndow
    访问不是问题所在。它实际上并没有创建一个不可见的控制台窗口;它只是说不要创建一个新的。问题似乎是cmd.exe不会在标准输出句柄上转发到它启动的进程,除非它还有一个有效的标准错误句柄。我在答案中添加了一个解决方法。哈!process.RedirectStandardError=true;这是我真正需要的。我仍然认为这个解决方案是一个黑客,但我会遵循这个方法。谢谢
    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;
        }
    }
    
    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. 
        }
    }
    
    public static string GetFrameworkPath()
    {
        var frameworkVersion = string.Format("v{0}.{1}.{2}", Environment.Version.Major, Environment.Version.Minor, Environment.Version.Build);
        var is64BitProcess = Environment.Is64BitProcess;
        var windowsPath = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
        return Path.Combine(windowsPath, "Microsoft.NET", is64BitProcess ? "Framework64" : "Framework", frameworkVersion);  
    }
    
    public static string GetPathToVisualStudio(string version)
    {   
        var is64BitProcess = Environment.Is64BitProcess;
        var registryKeyName = string.Format(@"Software\{0}Microsoft\VisualStudio\SxS\VC7", is64BitProcess ? @"Wow6432Node\" : string.Empty);
        var vsKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKeyName);
        var versionExists = vsKey.GetValueNames().Any(valueName => valueName.Equals(version));
        if(versionExists)
        {
            return vsKey.GetValue(version).ToString();
        }
        else
        {
            return null;
        }
    }
    
    var paths = new[]
        { 
            GetFrameworkPath(), 
            GetPathToVisualStudio("10.0"),
            Path.Combine(GetPathToVisualStudio("10.0"), "bin"),
        };  
    
    var previousPaths = Environment.GetEnvironmentVariable("PATH").ToString();
    var newPaths = string.Join(";", previousPaths.Split(';').Concat(paths));
    Environment.SetEnvironmentVariable("PATH", newPaths);
    
    var startInfo = new ProcessStartInfo()
    {
        FileName = "nmake",
        Arguments = "whatever you'd pass in here",
    };
    var process = Process.Start(startInfo);