2010-04-13 76 views
41

我有一個項目,我有一個應用程序正在運行的多個實例,每個實例都以不同的命令行參數啓動。我希望有一種方法可以從其中一個實例中單擊一個按鈕,然後關閉所有實例並使用相同的命令行參數重新啓動它們。我可以從.NET/C#獲得其他進程的命令行參數嗎?

我可以很容易地通過Process.GetProcessesByName()獲得進程本身,但每當我這樣做,StartInfo.Arguments屬性總是一個空字符串。它看起來也許該屬性只在開始一個進程之前有效。

This question有一些建議,但他們都在本地代碼,我想直接從.NET做到這一點。有什麼建議麼?

+1

您是否可以控制要重啓的應用程序? – 2010-04-13 22:30:35

+0

是的,我完全控制了我嘗試重新啓動的應用程序代碼 - 它總是會成爲我運行的同一應用程序的另一個實例。這是一個WPF應用程序,如果這有所作爲,但我認爲它不應該。 – 2010-04-13 22:36:25

+2

根據StartInfo上的MSDN文章(http://msdn.microsoft.com/en-us/library/system.diagnostics.process.startinfo.aspx),如果該進程是使用Process啓動的,則StartInfo對象僅包含信息。開始。它還表示在使用GetProcesses *函數時,StartInfo將爲空。 – Corin 2010-04-13 22:37:27

回答

60

這是使用的所有管理對象,但它確實浸下來到WMI境界:

private static void Main() 
{ 
    foreach (var process in Process.GetProcesses()) 
    { 
     try 
     { 
      Console.WriteLine(process.GetCommandLine()); 
     } 
     catch (Win32Exception ex) when ((uint)ex.ErrorCode == 0x80004005) 
     { 
      // Intentionally empty. 
     } 
    } 
} 

private static string GetCommandLine(this Process process) 
{ 
    var commandLine = new StringBuilder(process.MainModule.FileName); 

    commandLine.Append(" "); 
    using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) 
    { 
     foreach (var @object in searcher.Get()) 
     { 
      commandLine.Append(@object["CommandLine"]); 
      commandLine.Append(" "); 
     } 
    } 

    return commandLine.ToString(); 
} 
+1

唯一值得注意的是AccessDenied上的一些流程 – 2010-04-13 22:40:31

+2

小記;在我的計算機(Win 10)上,WMI返回的命令行包含正在運行的程序的名稱,因此不需要使用process.MainModule.FileName來初始化StringBuilder。 仍然是一個很好的代碼,它現在在我的項目中.. Thanx! – 2016-10-08 18:23:49

+0

什麼時候searcher.Get()返回一個包含多個元素的集合? 它什麼時候發生? – WawaBrother 2017-07-25 21:55:29

1

的StartInfo.Arguments當你啓動應用程序只使用,它不是命令行的記錄參數。如果您使用命令行參數啓動應用程序,那麼在參數進入應用程序時將其存儲。在最簡單的情況下,您可以將它們存儲在文本文件中,然後當您按下按鈕時,關閉除按鈕按下事件之外的所有進程。關閉一個新的應用程序,並將它提供給一個新的命令行arg。當舊應用程序關閉時,新應用程序將關閉所有新進程(文件中的每行一個)並關閉。僞代碼如下:

static void Main(string[] args) 
{ 
    if (args.Contains(StartProcessesSwitch)) 
     StartProcesses(GetFileWithArgs(args)) 
    else 
     WriteArgsToFile(); 
     //Run Program normally 
} 

void button_click(object sender, ButtonClickEventArgs e) 
{ 
    ShutDownAllMyProcesses() 
} 

void ShutDownAllMyProcesses() 
{ 
    List<Process> processes = GetMyProcesses(); 
    foreach (Process p in processes) 
    { 
     if (p != Process.GetCurrentProcess()) 
     p.Kill(); //or whatever you need to do to close 
    } 
    ProcessStartInfo psi = new ProcessStartInfo(); 
    psi.Arguments = CreateArgsWithFile(); 
    psi.FileName = "<your application here>"; 
    Process p = new Process(); 
    p.StartInfo = psi; 
    p.Start(); 
    CloseAppplication(); 
} 

希望這會有所幫助。祝你好運!

1

第一:謝謝傑西,爲您提供出色的解決方案。我的變化如下。注意:我喜歡關於C#的一件事是它是一種強類型語言。所以我不喜歡使用var類型。我覺得有一點清晰度值得幾個演員。

class Program 
{ 
    static void Main(string[] args) 
    { 


      Process[] processes = Process.GetProcessesByName("job Test"); 
      for (int p = 0; p < processes.Length; p++) 
      { 
       String[] arguments = CommandLineUtilities.getCommandLinesParsed(processes[p]); 
      } 
      System.Threading.Thread.Sleep(10000); 
    } 
} 



public abstract class CommandLineUtilities 
{ 
    public static String getCommandLines(Process processs) 
    { 
     ManagementObjectSearcher commandLineSearcher = new ManagementObjectSearcher(
      "SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + processs.Id); 
     String commandLine = ""; 
     foreach (ManagementObject commandLineObject in commandLineSearcher.Get()) 
     { 
      commandLine+= (String)commandLineObject["CommandLine"]; 
     } 

     return commandLine; 
    } 

    public static String[] getCommandLinesParsed(Process process) 
    { 
     return (parseCommandLine(getCommandLines(process))); 
    } 

    /// <summary> 
    /// This routine parses a command line to an array of strings 
    /// Element zero is the program name 
    /// Command line arguments fill the remainder of the array 
    /// In all cases the values are stripped of the enclosing quotation marks 
    /// </summary> 
    /// <param name="commandLine"></param> 
    /// <returns>String array</returns> 
    public static String[] parseCommandLine(String commandLine) 
    { 
     List<String> arguments = new List<String>(); 

     Boolean stringIsQuoted = false; 
     String argString = ""; 
     for (int c = 0; c < commandLine.Length; c++) //process string one character at a tie 
     { 
      if (commandLine.Substring(c, 1) == "\"") 
      { 
       if (stringIsQuoted) //end quote so populate next element of list with constructed argument 
       { 
        arguments.Add(argString); 
        argString = ""; 
       } 
       else 
       { 
        stringIsQuoted = true; //beginning quote so flag and scip 
       } 
      } 
      else if (commandLine.Substring(c, 1) == "".PadRight(1)) 
      { 
       if (stringIsQuoted) 
       { 
        argString += commandLine.Substring(c, 1); //blank is embedded in quotes, so preserve it 
       } 
       else if (argString.Length > 0) 
       { 
        arguments.Add(argString); //non-quoted blank so add to list if the first consecutive blank 
       } 
      } 
      else 
      { 
       argString += commandLine.Substring(c, 1); //non-blan character: add it to the element being constructed 
      } 
     } 

     return arguments.ToArray(); 

    } 

} 
+8

不要擔心「var」不那麼安全,這不是VB6或Javascript。 「var」只是意味着「讓編譯器從初始化中找出類型,而不是冗餘地提供一個類型以及一個初始值。從那裏開始,編譯器確保變量正確地使用它的'類型 – 2016-10-08 18:27:08

+0

任何原因'CommandLineUtilities'是'abstract'而不是'static'? – 2017-02-17 17:03:45

+1

同意,@GöranRoseen,然而有時當它不清楚什麼被返回到變量時增加了清晰度。 ',例如,沒有理由不使用'var'。 – 2017-07-17 21:17:47

4

A C#6的改編Jesse C. Slicer's excellent answer在於:

  • 完成並應該運行原來的樣子,一旦添加的引用組件System.Management.dll(所需WMI System.Management.ManagementSearcher類)。

  • 簡化了原代碼和修復了一些問題

  • 處理,如果被檢查的進程已經退出可能發生的額外異常。

using System.Management; 
using System.ComponentModel; 

// Note: The class must be static in order to be able to define an extension method. 
static class Progam 
{ 
    private static void Main() 
    { 
     foreach (var process in Process.GetProcesses()) 
     { 
      try 
      { 
       Console.WriteLine($"PID: {process.Id}; cmd: {process.GetCommandLine()}"); 
      } 
      // Catch and ignore "access denied" exceptions. 
      catch (Win32Exception ex) when (ex.HResult == -2147467259) {} 
      // Catch and ignore "Cannot process request because the process (<pid>) has 
      // exited." exceptions. 
      // These can happen if a process was initially included in 
      // Process.GetProcesses(), but has terminated before it can be 
      // examined below. 
      catch (InvalidOperationException ex) when (ex.HResult == -2146233079) {} 
     } 
    } 

    // Define an extension method for type System.Process that returns the command 
    // line via WMI. 
    private static string GetCommandLine(this Process process) 
    { 
     string cmdLine = null; 
     using (var searcher = new ManagementObjectSearcher(
      $"SELECT CommandLine FROM Win32_Process WHERE ProcessId = {process.Id}")) 
     { 
      // By definition, the query returns at most 1 match, because the process 
      // is looked up by ID (which is unique by definition). 
      var matchEnum = searcher.Get().GetEnumerator(); 
      if (matchEnum.MoveNext()) // Move to the 1st item. 
      { 
       cmdLine = matchEnum.Current["CommandLine"]?.ToString(); 
      } 
     } 
     if (cmdLine == null) 
     { 
      // Not having found a command line implies 1 of 2 exceptions, which the 
      // WMI query masked: 
      // An "Access denied" exception due to lack of privileges. 
      // A "Cannot process request because the process (<pid>) has exited." 
      // exception due to the process having terminated. 
      // We provoke the same exception again simply by accessing process.MainModule. 
      var dummy = process.MainModule; // Provoke exception. 
     } 
     return cmdLine; 
    } 
} 
2

如果你不想使用WMI,並寧願做這個土生土長的方式,我寫了基本要求NtQueryInformationProcess()和派生從返回的信息的命令行的DLL。

它是用C++編寫的,沒有依賴關係,因此它可以在任何Windows系統上工作。

要使用它,只需添加這些進口:

[DllImport("ProcCmdLine32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetProcCmdLine")] 
public extern static bool GetProcCmdLine32(uint nProcId, StringBuilder sb, uint dwSizeBuf); 

[DllImport("ProcCmdLine64.dll", CharSet = CharSet.Unicode, EntryPoint = "GetProcCmdLine")] 
public extern static bool GetProcCmdLine64(uint nProcId, StringBuilder sb, uint dwSizeBuf); 

然後調用它像這樣:

public static string GetCommandLineOfProcess(Process proc) 
{ 
    // max size of a command line is USHORT/sizeof(WCHAR), so we are going 
    // just allocate max USHORT for sanity's sake. 
    var sb = new StringBuilder(0xFFFF); 
    switch (IntPtr.Size) 
    { 
     case 4: GetProcCmdLine32((uint)proc.Id, sb, (uint)sb.Capacity); break; 
     case 8: GetProcCmdLine64((uint)proc.Id, sb, (uint)sb.Capacity); break; 
    } 
    return sb.ToString(); 
} 

的源代碼/ DLL是可用here

相關問題