我可以从.NET / C#获得其他进程的命令行参数吗?

我有一个项目,我有一个应用程序运行的多个实例,每个实例都是用不同的命令行参数启动的。 我想有一种方法来从这些实例中的一个单击button,然后closures所有的实例,并使用相同的命令行参数重新启动它们。

我可以很容易地通过Process.GetProcessesByName()获取进程本身,但每当我这样做, StartInfo.Arguments属性总是一个空string。 看起来也许这个属性在启动一个进程之前是唯一有效的。

这个问题有一些build议,但他们都在本机代码,我想直接从.NET做到这一点。 有什么build议么?

这是使用所有的托pipe对象,但它确实渗入到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(); } 

AC#6适应Jesse C. Slicer的出色答案 :

  • 是完整的,并且应该按原样运行,一旦添加了对Assembly的引用System.Management.dll (对于WMI System.Management.ManagementSearcher类是必需的)。

  • 简化原始代码并修复一些问题

  • 处理正在检查的进程已经退出时可能发生的额外exception。

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

首先:谢谢Jesse,您的出色解决scheme。 我的变化如下。 注意:我喜欢关于C#的一件事是它是一种强types语言。 所以我不使用vartypes。 我觉得有一点清晰是值得的几个演员。

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

如果您不想使用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在这里可用。

StartInfo.Arguments仅在启动应用程序时使用,它不是命令行参数的logging。 如果使用命令行参数启动应用程序,则在参数进入应用程序时将其存储。 在最简单的情况下,您可以将它们存储在文本文件中,然后当您按下button时,closures除button按下事件之外的所有进程。 启动一个新的应用程序,然后将该文件提供给一个新的命令行arg。 当旧应用程序closures时,新应用程序将closures所有新进程(文件中的每行一个)并closures。 下面的伪码:

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

希望这可以帮助。 祝你好运!