¿Puedo obtener argumentos de línea de comando de otros procesos desde .NET / C #?

Tengo un proyecto en el que tengo varias instancias de una aplicación en ejecución, cada una de las cuales se inició con diferentes argumentos de línea de comando. Me gustaría tener una forma de hacer clic en un botón de una de esas instancias que luego cierra todas las instancias y las inicia una copia de seguridad nuevamente con los mismos argumentos de la línea de comando.

Puedo obtener los procesos con mayor facilidad a través de Process.GetProcessesByName() , pero cada vez que lo hago, la propiedad StartInfo.Arguments siempre es una cadena vacía. Parece que tal vez la propiedad solo es válida antes de comenzar un proceso.

Esta pregunta tenía algunas sugerencias, pero todas están en código nativo, y me gustaría hacer esto directamente desde .NET. ¿Alguna sugerencia?

Esto está usando todos los objetos administrados, pero se sumerge en el reino de 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 - no security access to the process. } catch (InvalidOperationException) { // Intentionally empty - the process exited before getting details. } } } private static string GetCommandLine(this Process process) { using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) using (ManagementObjectCollection objects = searcher.Get()) { return objects.Cast().SingleOrDefault()?["CommandLine"]?.ToString(); } } 

AC # 6 adaptación de la excelente respuesta de Jesse C. Slicer que:

  • está completo y debe ejecutarse tal como está, una vez que agrega una referencia al ensamblado System.Management.dll (necesario para la clase WMI System.Management.ManagementSearcher ).

  • optimiza el código original y soluciona algunos problemas

  • maneja una excepción adicional que puede ocurrir si un proceso que se está examinando ya ha salido.

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

Si no desea usar WMI y prefiere tener una forma nativa de hacerlo, escribí una DLL que básicamente llama a NtQueryInformationProcess() y deriva la línea de comando de la información devuelta.

Está escrito en C ++ y no tiene dependencias, por lo que debería funcionar en cualquier sistema de Windows.

Para usarlo, solo agregue estas importaciones:

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

Luego llámalo así:

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

El código fuente / DLL están disponibles aquí .

Primero: gracias Jesse, por su excelente solución. Mi variación está abajo. Nota: Una de las cosas que me gusta de C # es que es un lenguaje fuertemente tipado. Por lo tanto, evito el uso de var type. Siento que un poco de claridad vale unos moldes.

 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))); } ///  /// 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 ///  ///  /// String array public static String[] parseCommandLine(String commandLine) { List arguments = new List(); 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(); } } 

StartInfo.Arguments solo se usa cuando inicia la aplicación, no es un registro de los argumentos de la línea de comando. Si inicia las aplicaciones con argumentos de línea de comando, entonces almacene los argumentos cuando entren en su aplicación. En el caso más simple, podría almacenarlos en un archivo de texto, luego, cuando presione el botón, apague todos los procesos excepto el que tiene el evento de presionar un botón. Encienda una nueva aplicación y aliméntela en una nueva línea de comando arg. Mientras la aplicación anterior se apaga, la nueva aplicación desconecta todos los procesos nuevos (uno para cada línea en el archivo) y se apaga. Psuedocode a continuación:

 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 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 = ""; Process p = new Process(); p.StartInfo = psi; p.Start(); CloseAppplication(); } 

Espero que esto ayude. ¡Buena suerte!