2010-04-13 74 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

相关问题