IT Share you

.NET / C #에서 다른 프로세스의 명령 줄 인수를 가져올 수 있습니까?

shareyou 2020. 12. 8. 20:26
반응형

.NET / C #에서 다른 프로세스의 명령 줄 인수를 가져올 수 있습니까?


각기 다른 명령 줄 인수로 시작된 여러 앱 인스턴스가 실행중인 프로젝트가 있습니다. 해당 인스턴스 중 하나에서 버튼을 클릭 한 다음 모든 인스턴스를 종료하고 동일한 명령 줄 인수로 다시 시작하는 방법을 갖고 싶습니다.

을 통해 프로세스 자체를 쉽게 얻을 수 Process.GetProcessesByName()있지만, 할 때마다 StartInfo.Arguments속성은 항상 빈 문자열입니다. 해당 속성은 프로세스를 시작하기 전에 만 유효한 것 같습니다.

이 질문 에는 몇 가지 제안이 있었지만 모두 네이티브 코드로되어 있으며 .NET에서 직접 수행하고 싶습니다. 어떤 제안?


이것은 모든 관리되는 개체를 사용하지만 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();
    }

}

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은 여기에서 사용할 수 있습니다 .


Jesse C. Slicer의 탁월한 답변에 대한 AC # v6 + 적용 :

  • 어셈블리에 대한 참조를 추가하면 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;
    }
}

첫 번째 : 훌륭한 솔루션에 대해 Jesse에게 감사합니다. 내 변형은 다음과 같습니다. 참고 : 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();

    }

}

StartInfo.Arguments는 앱을 시작할 때만 사용되며 명령 줄 인수의 레코드가 아닙니다. 명령 줄 인수를 사용하여 응용 프로그램을 시작하는 경우 응용 프로그램에 인수가 들어올 때 저장하십시오. 가장 간단한 경우에는 텍스트 파일에 저장 한 다음 버튼을 누를 때 버튼 누르기 이벤트가있는 프로세스를 제외한 모든 프로세스를 종료 할 수 있습니다. 새 애플리케이션을 실행하고 새 명령 줄 인수에 해당 파일을 제공합니다. 이전 앱이 종료되는 동안 새 앱은 모든 새 프로세스 (파일의 각 줄에 하나씩)를 실행하고 종료합니다. 아래 Psuedocode :

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

Hope this helps. Good luck!

참고URL : https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c

반응형