我有一个项目,我有一个应用程序的多个实例在运行,每个实例都是用不同的命令行参数启动的。我希望有一种方法可以从其中一个实例中单击一个按钮,然后关闭所有实例并使用相同的命令行参数重新启动它们。
我可以通过Process.GetProcessesByName()
轻松地自行获取进程,但每当我这样做时,StartInfo.Arguments
属性始终为空字符串。看起来这个属性可能只在开始一个过程之前有效。
This question有一些建议,但它们都是本机代码,我想直接从.NET执行此操作。有什么建议吗?
答案 0 :(得分:68)
这是使用所有托管对象,但它确实深入到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();
}
}
答案 1 :(得分:8)
C#v6 + 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).
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;
}
}
答案 2 :(得分:4)
如果您不想使用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。
答案 3 :(得分:1)
首先:谢谢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();
}
}
答案 4 :(得分:1)
如果要在没有WMI或ProcCmdLine32.dll的Linux上执行相同的操作,则代码如下:
from django.contrib import admin
from myapp.models import Foo, Bar, Baz
admin.site.register(Foo)
admin.site.register(Bar)
admin.site.register(Baz)
答案 5 :(得分:0)
StartInfo.Arguments仅在启动应用程序时使用,它不是命令行参数的记录。如果使用命令行参数启动应用程序,则在参数进入应用程序时存储参数。在最简单的情况下,您可以将它们存储在文本文件中,然后当您按下按钮时,关闭除按钮按下事件之外的所有进程。触发新应用程序,并将该文件以新命令行arg提供。当旧应用程序关闭时,新应用程序会触发所有新进程(文件中每行一个)并关闭。 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();
}
希望这会有所帮助。祝你好运!