2012-09-12 14 views
10

Ho la seguente applicazione C# banale che tenta semplicemente di avviare "jconsole.exe", che sulla mia macchina si trova in C: \ Programmi \ jdk16 \ bin.Variabile di ambiente Process.Start() e PATH

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); 
     } 
    } 
    } 
} 

Se la mia variabile d'ambiente PATH è impostata su

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

funziona perfettamente. Tuttavia, se la variabile PATH viene impostato

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

(notare le due barre rovesciate tra "c:" e "programmi"), si riesce con un'eccezione 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) 

È interessante notare che, nello stesso prompt dei comandi in cui ho eseguito il programma .NET e ottenere l'eccezione, posso semplicemente digitare "jconsole.exe", e il programma si avvierà. Sembra che Windows non abbia problemi a trovare l'eseguibile con il doppio backslash nel PERCORSO, ma Process.Start().

Perché il backsback aggiuntivo nel PERCORSO causa problemi e come posso risolvere il problema? Non so dove si troverà l'eseguibile che voglio chiamare al runtime, quindi preferirei fare affidamento sulla variabile PATH.

+0

Ci sono due modi per avviare un file EXE, si sta testando in entrambe le direzioni. La tua app utilizza ShellExecuteEx(), l'interprete della riga di comando utilizza CreateProcess(). Puoi giocare con la proprietà ProcessStartInfo.UseShellExecute. Non ha molto senso preoccuparsi di come interpretano la variabile d'ambiente PATH in modo diverso, sapete come risolvere il problema. –

risposta

10

Non so bene perché si verifica il problema. Anche se, mi viene in mente una soluzione che funziona sulla mia macchina:

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); 
} 

ho trovato uno para che mi ha dato l'idea per questa soluzione. Dal documentation for Process.Start

Se si dispone di una variabile percorso dichiarata nel vostro sistema utilizzando le virgolette, è necessario qualificare completamente quel percorso quando si inizia qualsiasi processo trovato in quella posizione . Altrimenti, il sistema non troverà il percorso. Ad esempio, se c: \ mypath non è nel tuo percorso e lo aggiungi utilizzando la quotazione marchi: percorso =% percorso%; "c: \ mypath", devi qualificare completamente qualsiasi processo in c: \ mypath quando avviarlo.

Il modo in cui l'ho letto, anche se la variabile PATH conteneva un percorso valido che Windows è in grado di utilizzare, Process.Start è in grado di usarlo e ha bisogno il percorso completo .

+0

Grazie per aver evidenziato il paragrafo del documento Amith. L'ho interpretato come interessando solo le voci nel percorso con le virgolette, ma mi piace la tua generalizzazione che non puoi fidarti di Process.Start() per usare correttamente la variabile d'ambiente PATH. Per inciso, ho provato a impostare il mio PERCORSO in 'c: \ windows \ system32; c: \ windows;" c: \ programs \ jdk16 \ bin "', e Process.Start() è riuscito a trovare jconsole.exe senza qualsiasi aiuto extra. Questo sembra contrario a ciò che dicono i documenti, su Process.Start() usando il PATH, quindi non mi fido davvero di adesso. :) –

3

È possibile risolverlo se si crea prima un ProcessStartInfo.

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

// Manipulate dictionary... 

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

Dovrete scoprire voi stessi come manipolare il PERCORSO per farlo funzionare per voi. Ma questo dovrebbe risolvere tutti i problemi che potresti avere con la tua variabile PATH.

+1

Dopo aver modificato la [proprietà EnvironmentVariables] (http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.environmentvariables.aspx), è necessario impostare la proprietà [UseShellExecute] (http : //msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.useshellexecute.aspx) su false. Tuttavia, se UseShellExecute è falso, devo specificare il percorso completo per la [proprietà FileName] (http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.filename), che tipo di sconfitte lo scopo di cambiare il percorso. –

+0

Tuttavia nell'esempio stesso sulla pagina 'UseShellExecute' non forniscono un' FileName 'completo e utilizzano 'UseShellExecute = false;'. Ho anche provato a impostarlo su false e ho appena chiamato un exe che può essere trovato solo nel mio PATH e inizia appena. – Chrono

3

La risposta accettata non è corretta.

cmd.exe trova prima le applicazioni con estensioni eseguibili.
Quindi, quando i file sono puma e puma.bat in C:\Ruby\bin\, quindi buma.bat avrà la precedenza su puma.

Se si avvia c:\ruby\bin\puma.bat da c:\redmine, verrà avviato puma con la directory di lavoro corrente c:\ruby\bin e l'applicazione Web funzionerà.
Se si avvia direttamente c:\ruby\bin\puma, si avvierà puma con la directory di lavoro corrente in c:\redmine e in seguito avrà esito negativo.

Quindi una versione corretta sembra più o meno così:

// 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 
Problemi correlati