【问题标题】:Process.Start() and PATH environment variableProcess.Start() 和 PATH 环境变量
【发布时间】:2012-09-05 18:04:24
【问题描述】:

我有以下简单的 C# 应用程序,它只是尝试启动“jconsole.exe”,它在我的机器上位于 C:\Programs\jdk16\bin。

using System;
using System.Diagnostics;

namespace dnet {
  public class dnet {
    static void Main( string[] args ) {
      try {
        Process.Start("jconsole.exe");
        Console.WriteLine("Success!");
      } catch (Exception e) {
        Console.WriteLine("{0} Exception caught.", e);
      }
    }
  }
}

如果我的 PATH 环境变量设置为

c:\windows;c:\windows\sytem32;c:\programs\jdk16\bin

完美运行。但是,如果 PATH 环境变量设置为

c:\windows;c:\windows\sytem32;c:\\programs\jdk16\bin

(注意 "c:" 和 "programs" 之间的两个反斜杠),它会因 win32 异常而失败。

System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
at dnet.dnet.Main(String[] args)

有趣的是,在我运行 .NET 程序并获得异常的同一命令提示符下,我只需键入“jconsole.exe”,程序就会启动。 Windows 似乎可以轻松地在 PATH 中找到带有双反斜杠的可执行文件,但 Process.Start() 可以。

为什么 PATH 中的额外反斜杠会导致问题,我该如何解决这个问题?我不知道我要调用的可执行文件在运行时会位于何处,所以我宁愿依赖 PATH 变量。

【问题讨论】:

  • 启动 EXE 有两种方式,您正在测试两种方式。您的应用程序使用 ShellExecuteEx(),命令行解释器使用 CreateProcess()。您可以使用 ProcessStartInfo.UseShellExecute 属性。不必担心他们如何以不同方式解释 PATH 环境变量,您知道如何解决问题。

标签: c# path process.start


【解决方案1】:

不太清楚为什么会出现问题。不过,我可以想到一种适用于我的机器的解决方案:

var enviromentPath = System.Environment.GetEnvironmentVariable("PATH");

Console.WriteLine(enviromentPath);
var paths = enviromentPath.Split(';');
var exePath = paths.Select(x => Path.Combine(x, "mongo.exe"))
                   .Where(x => File.Exists(x))
                   .FirstOrDefault();

Console.WriteLine(exePath);

if (string.IsNullOrWhiteSpace(exePath) == false)
{
    Process.Start(exePath);
}

我确实找到了一个段落,它给了我这个解决方案的想法。来自documentation for Process.Start

如果您在系统中使用引号声明了路径变量,您 在启动在该路径中找到的任何进程时,必须完全限定该路径 地点。否则系统将找不到路径。例如, 如果 c:\mypath 不在您的路径中,并且您使用引号添加它 标记:path = %path%;"c:\mypath",您必须完全限定任何进程 启动时在 c:\mypath 中。

按照我的阅读方式,即使PATH 变量包含Windows 能够使用的有效路径,Process.Start 也无法使用它,需要完全限定路径

【讨论】:

  • 感谢您突出显示文档 Amith 中的段落。我将其解释为仅影响带引号的路径中的条目,但我喜欢您不能相信 Process.Start() 正确使用 PATH 环境变量的概括。有趣的是,我尝试将 PATH 设置为 c:\windows\system32;c:\windows;"c:\programs\jdk16\bin",并且 Process.Start() 无需任何额外帮助即可找到 jconsole.exe。这似乎与文档所说的相反,关于 Process.Start() 使用 PATH,所以我现在真的不相信它。 :)
  • 我推荐使用System.IO.Path.PathSeparator来获得多平台支持。在 macos 上,路径分隔符是“:”。
【解决方案2】:

先创建ProcessStartInfo就可以解决。

ProcessStartInfo psi = new ProcessStartInfo("jconsole.exe");
StringDictionary dictionary = psi.EnvironmentVariables;

// Manipulate dictionary...

psi.EnvironmentVariables["PATH"] = dictionary.Replace(@"\\", @"\");
Process.Start(psi);

您必须自己了解如何操作 PATH 才能让它为您工作。但这应该可以解决您的 PATH 变量可能遇到的任何问题。

【讨论】:

  • 更改 EnvironmentVariables property 后,您必须将 UseShellExecute property 设置为 false。但是,如果 UseShellExecute 为 false,我必须为 FileName property 指定完全限定的路径,这有点违背更改路径的目的。
  • 然而,在UseShellExecute 页面上的示例中,他们没有给出完全限定的FileName 并使用UseShellExecute = false;。我也尝试将其设置为 false 并调用只能在我的 PATH 中找到的 exe 并启动。
  • 我还建议将您的Process.StartInfo.WorkingDirectory 设置为您的 .exe 应使用的环境路径
【解决方案3】:

接受的答案不正确。

cmd.exe 将首先查找具有可执行扩展名的应用程序。
因此,当您在C:\Ruby\bin\ 中拥有文件pumapuma.bat 时,puma.bat 将优先于puma

如果您从c:\redmine 启动c:\ruby\bin\puma.bat,它将使用当前工作目录c:\ruby\bin 启动puma,并且您的Web 应用程序将运行。
但是,如果直接启动c:\ruby\bin\puma,它将以c:\redmine中的当前工作目录启动puma,随后会失败。

所以修正后的版本或多或少是这样的:

// FindAppInPathDirectories("ruby.exe");
public string FindAppInPathDirectories(string app)
{
    string enviromentPath = System.Environment.GetEnvironmentVariable("PATH");
    string[] paths = enviromentPath.Split(';');

    foreach (string thisPath in paths)
    {
        string thisFile = System.IO.Path.Combine(thisPath, app);
        string[] executableExtensions = new string[] { ".exe", ".com", ".bat", ".sh", ".vbs", ".vbscript", ".vbe", ".js", ".rb", ".cmd", ".cpl", ".ws", ".wsf", ".msc", ".gadget" };

        foreach (string extension in executableExtensions)
        {
            string fullFile = thisFile + extension;

            try
            {
                if (System.IO.File.Exists(fullFile))
                    return fullFile;
            }
            catch (System.Exception ex)
            {
                Log("{0}:\r\n{1}",
                     System.DateTime.Now.ToString(m_Configuration.DateTimeFormat, System.Globalization.CultureInfo.InvariantCulture)
                    , "Error trying to check existence of file \"" + fullFile + "\""
                );

                Log("Exception details:");
                Log(" - Exception type: {0}", ex.GetType().FullName);
                Log(" - Exception Message:");
                Log(ex.Message);
                Log(" - Exception Stacktrace:");
                Log(ex.StackTrace);
            } // End Catch

        } // Next extension

    } // Next thisPath


    foreach (string thisPath in paths)
    {
        string thisFile = System.IO.Path.Combine(thisPath, app);

        try
        {
            if (System.IO.File.Exists(thisFile))
                return thisFile;
        }
        catch (System.Exception ex)
        {
            Log("{0}:\r\n{1}",
                 System.DateTime.Now.ToString(m_Configuration.DateTimeFormat, System.Globalization.CultureInfo.InvariantCulture)
                , "Error trying to check existence of file \"" + thisFile + "\""
            );

            Log("Exception details:");
            Log(" - Exception type: {0}", ex.GetType().FullName);
            Log(" - Exception Message:");
            Log(ex.Message);
            Log(" - Exception Stacktrace:");
            Log(ex.StackTrace);
        } // End Catch

    } // Next thisPath

    return app;
} // End Function FindAppInPathDirectories

【讨论】:

    猜你喜欢
    • 2014-04-15
    • 2019-06-30
    • 1970-01-01
    • 2019-05-15
    • 1970-01-01
    • 2013-08-04
    • 1970-01-01
    • 1970-01-01
    • 2020-03-07
    相关资源
    最近更新 更多