2012-09-12 28 views
10

Tôi có ứng dụng C# tầm thường sau đây chỉ đơn giản là cố gắng để khởi động "jconsole.exe", mà trên máy tính của tôi nằm trong C: \ Programs \ jdk16 \ bin.Process.Start() và biến môi trường 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); 
     } 
    } 
    } 
} 

Nếu biến môi trường PATH của tôi được thiết lập để

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

nó hoạt động hoàn hảo. Tuy nhiên, nếu biến môi trường PATH được đặt thành

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

(lưu ý hai dấu gạch chéo ngược giữa "c:" và "chương trình"), nó không có ngoại lệ 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) 

Điều thú vị là, trong cửa sổ lệnh cùng một nơi tôi chạy chương trình .NET và nhận được ngoại lệ, tôi có thể chỉ cần gõ "jconsole.exe", và chương trình sẽ bắt đầu. Windows dường như không gặp khó khăn trong việc tìm kiếm tệp thực thi với dấu gạch chéo ngược kép trong PATH, nhưng Process.Start() thực hiện.

Tại sao dấu gạch chéo ngược phụ trong PATH gây ra sự cố và cách tôi có thể khắc phục sự cố? Tôi không biết nơi mà các tập tin thực thi tôi muốn gọi sẽ được đặt tại thời gian chạy, vì vậy tôi muốn dựa vào biến PATH.

+0

Có hai cách để bắt đầu một file EXE, bạn đang thử nghiệm cả hai cách. Ứng dụng của bạn sử dụng ShellExecuteEx(), trình thông dịch dòng lệnh sử dụng CreateProcess(). Bạn có thể chơi với thuộc tính ProcessStartInfo.UseShellExecute. Không có nhiều điểm đáng lo ngại về cách họ giải thích biến môi trường PATH một cách khác nhau, bạn biết cách khắc phục vấn đề. –

Trả lời

10

Không chắc chắn lý do tại sao sự cố xảy ra. Mặc dù, tôi có thể nghĩ ra một giải pháp hoạt động trên máy của tôi:

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

Tôi đã tìm thấy một đoạn cho tôi ý tưởng về giải pháp này. Từ documentation for Process.Start

Nếu bạn có một biến con đường khai báo trong hệ thống của bạn sử dụng dấu ngoặc kép, bạn phải hội đủ điều kiện đầy đủ con đường mà khi bắt đầu bất kỳ quá trình tìm thấy trong đó địa điểm. Nếu không, hệ thống sẽ không tìm thấy đường dẫn. Ví dụ: nếu c: \ mypath không nằm trong đường dẫn của bạn và bạn thêm nó bằng cách sử dụng báo giá marks: path =% path%; "c: \ mypath", bạn phải đủ điều kiện bất kỳ quy trình nào trong c: \ mypath khi bắt đầu nó.

Con đường tôi đọc nó, mặc dù biến PATH chứa đường dẫn hợp lệ rằng Windows có khả năng sử dụng, Process.Start là không thể sử dụng nó và cần đầy đủ đường dẫn .

+0

Cảm ơn bạn đã đánh dấu đoạn văn từ tài liệu Amith. Tôi đã giải thích nó như là chỉ ảnh hưởng đến các mục trong đường dẫn với dấu ngoặc kép, nhưng tôi thích sự khái quát hóa của bạn mà bạn không thể tin tưởng Process.Start() để sử dụng biến môi trường PATH đúng cách. Thật thú vị, tôi đã thử cài đặt PATH của mình thành 'c: \ windows \ system32; c: \ windows;" c: \ programs \ jdk16 \ bin "' và Process.Start() có thể tìm thấy jconsole.exe mà không cần bất kỳ trợ giúp thêm. Điều này có vẻ trái với những gì các tài liệu nói, về Process.Start() bằng cách sử dụng PATH, vì vậy tôi thực sự không tin tưởng nó ngay bây giờ. :) –

3

Bạn có thể giải quyết nó nếu trước tiên bạn tạo một ProcessStartInfo.

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

// Manipulate dictionary... 

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

Bạn sẽ phải tự tìm hiểu cách thao tác PATH để nó hoạt động cho bạn. Nhưng điều này sẽ giải quyết bất kỳ vấn đề bạn có thể có với biến PATH của bạn.

+1

Sau khi thay đổi thuộc tính [EnvironmentVariables] (http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.environmentvariables.aspx), bạn phải đặt [UseShellExecute property] (http : //msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.useshellexecute.aspx) thành sai. Tuy nhiên, nếu UseShellExecute là sai, tôi phải chỉ định đường dẫn đầy đủ đủ điều kiện cho thuộc tính [FileName] (http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.filename), loại lỗi nào mục đích của việc thay đổi con đường. –

+0

Tuy nhiên, trong ví dụ trên trang 'UseShellExecute' họ không đưa ra một' FileName' đầy đủ và sử dụng 'UseShellExecute = false;'. Tôi cũng đã cố gắng thiết lập nó thành false và chỉ cần gọi một exe mà chỉ có thể được tìm thấy trong PATH của tôi và nó chỉ mới bắt đầu. – Chrono

3

Câu trả lời được chấp nhận không chính xác.

cmd.exe sẽ tìm các ứng dụng có phần mở rộng có thể thực thi trước tiên.
Vì vậy, khi bạn có các tệp pumapuma.bat trong C:\Ruby\bin\, thì buma.bat sẽ được ưu tiên hơn puma.

Nếu bạn bắt đầu c:\ruby\bin\puma.bat từ c:\redmine, nó sẽ bắt đầu puma với thư mục làm việc hiện tại c:\ruby\bin và ứng dụng web của bạn sẽ hoạt động.
Nếu bạn bắt đầu c:\ruby\bin\puma trực tiếp, nó sẽ bắt đầu puma với thư mục làm việc hiện tại trong c:\redmine và sau đó sẽ thất bại.

Vì vậy, một phiên bản sửa trông nhiều hơn hoặc ít hơn như thế này:

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