如何使用Java获取当前打开窗口/进程的列表?

110

有谁知道如何使用Java获取本地计算机的当前打开窗口或进程?

我想做的是:列出当前打开任务、窗口或进程,就像Windows任务管理器一样,但使用跨平台方法 - 如果可能的话只使用Java。

14个回答

115

这是另一种解析从命令"ps -e"获取进程列表的方法:

try {
    String line;
    Process p = Runtime.getRuntime().exec("ps -e");
    BufferedReader input =
            new BufferedReader(new InputStreamReader(p.getInputStream()));
    while ((line = input.readLine()) != null) {
        System.out.println(line); //<-- Parse data here.
    }
    input.close();
} catch (Exception err) {
    err.printStackTrace();
}

如果您正在使用Windows,则应更改该行代码:“Process p = Runtime.getRun...”(第三行),为以下代码:

Process p = Runtime.getRuntime().exec
    (System.getenv("windir") +"\\system32\\"+"tasklist.exe");

希望这些信息对你有所帮助!


如何获取进程的开始时间和结束时间。 - Bucks
36
在Windows上运行tasklist.exe /fo csv /nh,以CSV格式获取列表,这样更容易解析。 - Emmanuel Bourg
但它没有显示jar名称。我的可执行jar名称是helloDemo.jar,但它没有显示任何内容。 - Sumon Bappi
如果我添加一个 | grep java,为什么它不起作用?即 ps -elf | grep java 不会返回任何结果,但是 ps -elf 可以正常工作。 - Itay Moav -Malimovka
需要注意的是,底部的“Windows特定”部分似乎是不必要的。在Windows 10上,即使没有指定目录,在Clojure中使用Java-interop的(.exec (Runtime/getRuntime) "tasklist"))正确返回tasklist进程。 - Carcigenicate
1
对于任何使用Java 18的人来说,这个exec命令已经被弃用了,但你只需要简单地将它改为一个数组即可。Process p = Runtime.getRuntime().exec(new String[] {(System.getenv("windir") +"\\system32\\"+"tasklist.exe"} ); - Austin Whitelaw

60

最后,使用Java 9+可以通过ProcessHandle实现:

public static void main(String[] args) {
    ProcessHandle.allProcesses()
            .forEach(process -> System.out.println(processDetails(process)));
}

private static String processDetails(ProcessHandle process) {
    return String.format("%8d %8s %10s %26s %-40s",
            process.pid(),
            text(process.parent().map(ProcessHandle::pid)),
            text(process.info().user()),
            text(process.info().startInstant()),
            text(process.info().commandLine()));
}

private static String text(Optional<?> optional) {
    return optional.map(Object::toString).orElse("-");
}

输出:

    1        -       root   2017-11-19T18:01:13.100Z /sbin/init
  ...
  639     1325   www-data   2018-12-04T06:35:58.680Z /usr/sbin/apache2 -k start
  ...
23082    11054    huguesm   2018-12-04T10:24:22.100Z /.../java ProcessListDemo

1
需要添加哪些导入才能使用ProcessHandle? - Jordi
2
那个类在java.lang中,所以不需要导入。 - Hugues M.
1
@Jordi:你只需要 import java.util.Optional;。如果你使用一个不错的IDE(IntelliJ IDEA/NetBeans/Eclipse/...),当你将上述方法粘贴到现有类中时,必要的导入应该会自动添加。 - Eric Duminil

26
在Windows平台上,可以使用JNA作为替代方案:JNA
import com.sun.jna.Native;
import com.sun.jna.platform.win32.*;
import com.sun.jna.win32.W32APIOptions;

public class ProcessList {

    public static void main(String[] args) {
        WinNT winNT = (WinNT) Native.loadLibrary(WinNT.class, W32APIOptions.UNICODE_OPTIONS);

        WinNT.HANDLE snapshot = winNT.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0));

        Tlhelp32.PROCESSENTRY32.ByReference processEntry = new Tlhelp32.PROCESSENTRY32.ByReference();

        while (winNT.Process32Next(snapshot, processEntry)) {
            System.out.println(processEntry.th32ProcessID + "\t" + Native.toString(processEntry.szExeFile));
        }

        winNT.CloseHandle(snapshot);
    }
}

1
这只提供了命令名称,而不是整个命令行。有没有办法获取整个命令行? - Christopher Dancy
2
您可以通过调用 GetModuleFileName 来获取完整路径。请参见 http://stackoverflow.com/questions/7521693/converting-c-sharp-to-java-jna-getmodulefilename-from-hwnd 以获取示例。 - Emmanuel Bourg
1
唯一的问题是它只提供进程的路径,而不是整个命令行。有没有办法获取进程的完整命令行(即“ant.bat -f helloWorld.ext”)? - Christopher Dancy
@ChristopherDancy 不确定您是否还需要答案,但为了完整性,并且因为这里也没有提到:Windows管理信息命令行(或简称wmic.exe) 提供了一种检索正在运行的应用程序大量信息的方法 - WMIC PROCESS 或者限制特定进程输出的方式:WMIC PROCESS WHERE name='theName'。如果需要,您可以使用进一步的过滤器来限制输出。该命令行包含在返回的表格中的CommandLine列中(=第二列)。 - Roman Vottner
1
@ChristopherDancy 哦,忘了提到:您甚至可以通过使用“WMIC PROCESS WHERE“ name like '%nameOfApp%'” get Commandline”直接提取命令行。 - Roman Vottner
显示剩余2条评论

10

我所能想到的唯一方法是调用一个命令行应用程序来为你完成工作,然后屏幕抓取输出内容(例如类似于Linux 的 ps 和 Windows 的 tasklist)。

不幸的是,这意味着您必须编写一些解析例程来读取两个来源的数据。

Process proc = Runtime.getRuntime().exec ("tasklist.exe");
InputStream procOutput = proc.getInputStream ();
if (0 == proc.waitFor ()) {
    // TODO scan the procOutput for your data
}

1
是的,我也考虑过那个,但我认为它只能使用Java完成。 另外,我认为最好使用“ps -aux”而不是top。感谢快速回答! - ramayac
1
在Windows上,tasklist程序可以配置为输出CSV :) - MauganRa

7

YAJSW(Yet Another Java Service Wrapper)似乎具有基于JNA的org.rzo.yajsw.os.TaskList接口的实现,适用于win32、linux、bsd和solaris,并且受LGPL许可证保护。我没有直接调用这段代码的经验,但在过去使用YAJSW时效果非常好,因此您不必太担心。


显然v12+是Apache/LGPL双重许可证。 - harschware

6

您可以使用jProcesses轻松获取运行进程列表。

List<ProcessInfo> processesList = JProcesses.getProcessList();

for (final ProcessInfo processInfo : processesList) {
    System.out.println("Process PID: " + processInfo.getPid());
    System.out.println("Process Name: " + processInfo.getName());
    System.out.println("Process Used Time: " + processInfo.getTime());
    System.out.println("Full command: " + processInfo.getCommand());
    System.out.println("------------------");
}

这个库似乎非常慢...有什么原因吗(或者是一般的Java + WMI与VBS之间的交互?) - Gobliins
2
我猜你在谈论Windows实现。是的,通常WMI查询需要一些时间。就像往常一样,这取决于用例。如果你需要每10毫秒执行一次查询,那么它就太慢了。 - profesor_falken
是的,我在谈论Windows,而在Linux中我发现了使用“ps...<选项>”,所以我猜它应该会更快。 - Gobliins
好的,如果您愿意,可以在 Github 项目中创建一个问题,我最终会去看看,看看能否提高 Win 的性能。实际上,我在 Linux 上使用它,所以没有太关注 Win 的实现 :-S - profesor_falken
没问题,但是我想要的是:在你的process startTime中,你限制了给定时间为一天(hh:mm:ss),如果我没有记错的话。是否有选项可以获取日期+时间(yyyy-mm-dd-hh:mm:ss)? - Gobliins
这不太正常。startTime 是从 WIN32_PROCESS 类的 CreationDate 属性获取的(https://msdn.microsoft.com/en-us/library/windows/desktop/aa394372(v=vs.85).aspx),通常类似于“20160418081141.713311+120”。请创建一个问题,我会看一下。我现在没有访问 Windows 机器的权限。 - profesor_falken

4
这可能对带有捆绑JRE的应用程序有用:我会扫描正在运行应用程序的文件夹名称,因此如果您的应用程序正在执行以下操作:
C:\Dev\build\SomeJavaApp\jre-9.0.1\bin\javaw.exe

您可以通过以下方式找到J9中是否已经运行:

public static void main(String[] args) {
    AtomicBoolean isRunning = new AtomicBoolean(false);
    ProcessHandle.allProcesses()
            .filter(ph -> ph.info().command().isPresent() && ph.info().command().get().contains("SomeJavaApp"))
            .forEach((process) -> {
                isRunning.set(true);
            });
    if (isRunning.get()) System.out.println("SomeJavaApp is running already");
}

4

对于 Windows 系统,我使用以下方法:

Process process = new ProcessBuilder("tasklist.exe", "/fo", "csv", "/nh").start();
new Thread(() -> {
    Scanner sc = new Scanner(process.getInputStream());
    if (sc.hasNextLine()) sc.nextLine();
    while (sc.hasNextLine()) {
        String line = sc.nextLine();
        String[] parts = line.split(",");
        String unq = parts[0].substring(1).replaceFirst(".$", "");
        String pid = parts[1].substring(1).replaceFirst(".$", "");
        System.out.println(unq + " " + pid);
    }
}).start();
process.waitFor();
System.out.println("Done");

4
没有平台中立的方法来做这件事。在Java 1.6版本中,添加了一个“Desktop”类,它允许以可移植的方式浏览、编辑、邮件、打开和打印URI。可能会有一天这个类会被扩展来支持进程,但我表示怀疑。
如果你只对Java进程感兴趣,可以使用java.lang.management API获取JVM上的线程/内存信息。

3
使用代码来解析Linux中的ps aux和Windows中的tasklist是最好的选择,直到出现更通用的选项为止。
对于Windows,您可以参考:http://www.rgagnon.com/javadetails/java-0593.html Linux也可以通过管道将ps aux的结果传递给grep,这样处理/搜索会变得快速简便。我相信您也可以在Windows中找到类似的东西。

你可能也想更详细地查看任务列表:http://technet.microsoft.com/zh-cn/library/bb491010.aspx - James Oravec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接