如何通过进程ID获取应用程序的窗口标题

java标准api无法直接通过processhandle获取窗口标题,但可借助操作系统命令(如windows的tasklist或linux/macos的xwininfo/wmctrl)结合runtime执行shell命令来实现。

在Java中,ProcessHandle(自Java 9引入)提供了对进程基本信息(如用户、启动命令、命令行参数、启动时间等)的安全访问能力,但其ProcessHandle.Info接口不包含窗口标题(Window Title)字段。这是因为窗口标题属于GUI层面的属性,由操作系统窗口管理器维护,与底层进程本身无直接映射关系——同一进程可能拥有多个窗口、无窗口(如后台服务),或窗口标题动态变化,因此JVM未将其纳入跨平台进程抽象。

若需获取指定进程ID(PID)对应窗口的标题,必须依赖操作系统原生机制,并通过Java调用外部命令实现。以下是分平台的实用方案:

✅ Windows:使用 tasklist + 文本过滤

Windows系统可通过tasklist /v /fo list输出详细进程信息(含“Window Title”字段),再用find筛选:

public static void getWindowTitleByPidOnWindows(long pid) throws IOException, InterruptedException {
    String command = String.format("tasklist /v /fo list /fi \"pid eq %d\" | findstr /i \"Window Title\"", pid);
    Process process = Runtime.getRuntime().exec(command);
    process.waitFor();

    try (BufferedReader reader = new BufferedReader(
         

new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { if (line.trim().startsWith("Window Title:")) { String title = line.substring("Window Title:".length()).trim(); System.out.println("窗口标题: " + title); return; } } System.out.println("未找到PID=" + pid + "对应的窗口标题(可能无GUI窗口或权限不足)"); } }
⚠️ 注意:需以管理员权限运行Java程序才能获取其他用户进程的窗口信息;普通用户仅能看到自身进程。

✅ Linux(X11):使用 wmctrl 或 xwininfo

需先安装工具:sudo apt install wmctrl(Ubuntu/Debian)或 brew install wmctrl(macOS + XQuartz)。
wmctrl -lp 可列出所有窗口及其关联PID:

public static void getWindowTitleByPidOnLinux(long pid) throws IOException {
    Process process = Runtime.getRuntime().exec("wmctrl -lp");
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream()))) {
        String line;
        while ((line = reader.readLine()) != null) {
            // 输出格式示例: 0x04a00003  0 12345  user Desktop
            String[] parts = line.trim().split("\\s+");
            if (parts.length >= 3) {
                try {
                    long windowPid = Long.parseLong(parts[2]);
                    if (windowPid == pid) {
                        // 取出窗口标题(从第4列开始合并)
                        String title = String.join(" ", Arrays.copyOfRange(parts, 3, parts.length));
                        System.out.println("窗口标题: " + title);
                        return;
                    }
                } catch (NumberFormatException ignored) {}
            }
        }
        System.out.println("未找到PID=" + pid + "关联的X11窗口");
    }
}

✅ macOS:使用 AppleScript(推荐)

macOS无直接PID→窗口标题命令,但可通过AppleScript查询前台/所有应用窗口:

public static void getWindowTitleByPidOnMacOS(long pid) throws IOException, InterruptedException {
    // 获取进程名(用于后续匹配)
    String psCmd = "ps -o comm= -p " + pid;
    Process psProc = Runtime.getRuntime().exec(psCmd);
    psProc.waitFor();
    String appName = "";
    try (BufferedReader br = new BufferedReader(
            new InputStreamReader(psProc.getInputStream()))) {
        appName = br.readLine().trim().replaceAll("\\.app$", "");
    }

    // 用AppleScript遍历所有应用窗口,匹配进程名
    String script = String.format(
        "osascript -e 'use sys: \"System Events\"' " +
        "-e 'repeat with p in every application process whose unix id is %d' " +
        "-e 'repeat with w in windows of p' " +
        "-e 'if name of w is not missing value then log name of w' " +
        "-e 'end repeat' " +
        "-e 'end repeat'",
        pid
    );
    Process appScript = Runtime.getRuntime().exec(script);
    appScript.waitFor();

    try (BufferedReader br = new BufferedReader(
            new InputStreamReader(appScript.getInputStream()))) {
        String line;
        while ((line = br.readLine()) != null) {
            if (line.contains("log: ")) {
                System.out.println("窗口标题: " + line.substring("log: ".length()).trim());
                return;
            }
        }
    }
}

? 安全与兼容性提醒

  • 不要拼接不可信输入到命令字符串中,避免命令注入(应使用ProcessBuilder+参数列表替代字符串拼接);
  • 跨平台代码需做OS检测(System.getProperty("os.name"));
  • GUI进程可能因权限、沙盒(如macOS App Sandbox)、Wayland(Linux新显示协议)限制而无法被枚举;
  • Runtime.exec() 启动的进程默认不继承父进程环境变量,必要时需显式设置;
  • 建议添加超时控制(如process.waitFor(5, TimeUnit.SECONDS))防止挂起。

综上,获取窗口标题本质是平台特定任务,Java需作为“胶水语言”桥接OS能力。核心思路始终是:用PID定位进程 → 调用OS工具枚举其GUI窗口 → 解析输出提取标题