You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux环境下用C++获取活动窗口可执行文件名及监听焦点变化

C++实现活动窗口进程检测方案

方案1:X11原生实现(通用跨桌面)

这个方案直接基于X11协议,无需依赖外部命令,兼容性覆盖绝大多数X11桌面环境(GNOME、KDE、Xfce等)。

步骤1:获取活动窗口的PID

通过X11 API读取根窗口的_NET_ACTIVE_WINDOW属性拿到当前焦点窗口ID,再读取该窗口的_NET_WM_PID属性获取进程ID:

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

pid_t get_active_window_pid(Display *display) {
    Window root = DefaultRootWindow(display);
    Atom net_active_window = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);
    Atom net_wm_pid = XInternAtom(display, "_NET_WM_PID", False);
    
    // 获取活动窗口ID
    Atom actual_type;
    int format;
    unsigned long nitems, bytes_after;
    unsigned char *data = NULL;
    Window active_win = None;

    if (XGetWindowProperty(display, root, net_active_window, 0, 1, False, AnyPropertyType,
                          &actual_type, &format, &nitems, &bytes_after, &data) == Success && data) {
        active_win = *(Window*)data;
        XFree(data);
    }
    if (active_win == None) return -1;

    // 获取窗口对应的PID
    pid_t pid = -1;
    if (XGetWindowProperty(display, active_win, net_wm_pid, 0, 1, False, XA_CARDINAL,
                          &actual_type, &format, &nitems, &bytes_after, &data) == Success && data) {
        pid = *(pid_t*)data;
        XFree(data);
    }
    return pid;
}

步骤2:通过PID读取可执行文件名

利用Linux的/proc文件系统,直接读取/proc/[pid]/comm(短进程名)或/proc/[pid]/exe(完整可执行路径,符号链接):

bool get_process_name(pid_t pid, char *buf, size_t buf_size) {
    char comm_path[64];
    snprintf(comm_path, sizeof(comm_path), "/proc/%d/comm", pid);
    
    FILE *fp = fopen(comm_path, "r");
    if (!fp) return false;
    
    if (fgets(buf, buf_size, fp)) {
        // 去掉换行符
        buf[strcspn(buf, "\n")] = '\0';
        fclose(fp);
        return true;
    }
    fclose(fp);
    return false;
}

步骤3:监听窗口焦点变化

通过X11的事件监听机制,订阅根窗口的PropertyChange事件,当_NET_ACTIVE_WINDOW属性变化时触发检测:

int main() {
    Display *display = XOpenDisplay(NULL);
    if (!display) {
        fprintf(stderr, "Failed to open X display\n");
        return 1;
    }
    Window root = DefaultRootWindow(display);
    Atom net_active_window = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);

    // 订阅根窗口的属性变化事件
    XSelectInput(display, root, PropertyChangeMask);

    XEvent event;
    char proc_name[256];
    pid_t last_pid = -1;

    while (1) {
        XNextEvent(display, &event);
        if (event.type == PropertyNotify && event.xproperty.atom == net_active_window) {
            pid_t current_pid = get_active_window_pid(display);
            if (current_pid != -1 && current_pid != last_pid) {
                if (get_process_name(current_pid, proc_name, sizeof(proc_name))) {
                    printf("Active window process changed: %s (PID: %d)\n", proc_name, current_pid);
                    // 在这里执行你的自定义功能
                    // custom_function(proc_name);
                }
                last_pid = current_pid;
            }
        }
    }

    XCloseDisplay(display);
    return 0;
}

编译时需要链接X11库:g++ your_code.cpp -o window_monitor -lX11

方案2:D-Bus实现(桌面环境特定)

如果目标环境是GNOME/KDE等遵循Freedesktop规范的桌面,D-Bus方案代码更简洁,但接口依赖具体桌面环境:

获取活动窗口PID(GNOME示例)

#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>

pid_t get_gnome_active_pid() {
    DBusError err;
    dbus_error_init(&err);
    DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "DBus connection error: %s\n", err.message);
        dbus_error_free(&err);
        return -1;
    }

    // 调用GNOME Shell接口获取活动窗口关联的应用信息
    DBusMessage *msg = dbus_message_new_method_call(
        "org.gnome.Shell", "/org/gnome/Shell/WindowTracker",
        "org.gnome.Shell.WindowTracker", "GetWindowApp"
    );
    if (!msg) {
        fprintf(stderr, "Failed to create DBus message\n");
        dbus_connection_unref(conn);
        return -1;
    }

    // 先获取活动窗口ID(可复用X11逻辑或调用GNOME接口)
    Display *display = XOpenDisplay(NULL);
    Window active_win = DefaultRootWindow(display);
    // 此处省略X11获取活动窗口ID的代码,直接传入示例值
    dbus_message_append_args(msg, DBUS_TYPE_UINT32, &active_win, DBUS_TYPE_INVALID);
    XCloseDisplay(display);

    DBusMessage *reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
    dbus_message_unref(msg);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "DBus reply error: %s\n", err.message);
        dbus_error_free(&err);
        dbus_connection_unref(conn);
        return -1;
    }

    // 解析回复获取应用PID
    pid_t pid = -1;
    DBusMessageIter iter;
    dbus_message_iter_init(reply, &iter);
    dbus_message_iter_recurse(&iter, &iter);
    dbus_message_iter_get_basic(&iter, &pid);

    dbus_message_unref(reply);
    dbus_connection_unref(conn);
    return pid;
}

监听焦点变化

通过订阅D-Bus的PropertiesChanged信号实现,例如监听org.gnome.Shell.WindowTracker的属性变化,触发重新检测。

方案对比

  • X11方案:通用性强,几乎支持所有X11桌面,无需额外依赖(仅需X11开发库),代码稍繁琐但稳定。
  • D-Bus方案:代码更简洁,但依赖具体桌面环境的D-Bus接口,兼容性不如X11方案。

内容的提问来源于stack exchange,提问作者InnerBushman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 23:44:57