Подтвердить что ты не робот

Могу ли я получить аргументы командной строки других процессов из .NET/С#?

У меня есть проект, в котором у меня есть несколько экземпляров приложения, каждый из которых был запущен с различными аргументами командной строки. Я хотел бы иметь возможность щелкнуть кнопку из одного из этих экземпляров, который затем выключит все экземпляры и снова запустит их обратно с теми же аргументами командной строки.

Я могу легко получить процессы через Process.GetProcessesByName(), но всякий раз, когда я это делаю, свойство StartInfo.Arguments всегда является пустой строкой. Похоже, что это свойство действительно только до начала процесса.

В этом вопросе были некоторые предложения, но они все в собственном коде, и я хотел бы сделать это прямо из .NET. Любые предложения?

4b9b3361

Ответ 1

Это использует все управляемые объекты, но это действительно углубляется в область 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<ManagementBaseObject>().SingleOrDefault()?["CommandLine"]?.ToString();
    }

}

Ответ 2

Адаптация AС# 6 Jesse C. Slicer - отличный ответ, который:

  • завершена и должна запускаться как есть после добавления ссылки на сборку 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).
            using (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;
    }
}

Ответ 3

Если вы не хотите использовать WMI и, скорее, имеете собственный способ сделать это, я написал DLL, которая в основном вызывает NtQueryInformationProcess() и выводит командную строку из возвращаемой информации.

Он написан на С++ и не имеет зависимостей, поэтому он должен работать в любой системе 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 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 доступны здесь.

Ответ 4

Во-первых: Спасибо, Джесси, за отличное решение. Мое изменение ниже. Примечание. Одна из вещей, которые мне нравятся в С#, - это строго типизированный язык. Поэтому я избегаю использования типа 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();

    }

}

Ответ 5

StartInfo.Arguments используется только при запуске приложения, это не запись аргументов командной строки. Если вы запускаете приложения с аргументами командной строки, то храните аргументы, когда они входят в ваше приложение. В простейшем случае вы можете сохранить их в текстовом файле, а затем, когда вы нажмете кнопку, закройте все процессы, кроме тех, которые были нажаты на кнопку. Запустите новое приложение и отправьте его этому файлу в новой командной строке arg. Пока старое приложение отключается, новое приложение запускает все новые процессы (по одному для каждой строки в файле) и выключается. Psedocode ниже:

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

Надеюсь, это поможет. Удачи!