XLib 为 XQueryTree() 返回损坏的结果?

XLib returning corrupt results for XQueryTree()?

在 Linux / X-Windows 下,我一直在尝试获取 windows 的递归列表。我的最终目标是生成一个包含 ID、位置、尺寸和标题的 windows 列表。一些 windows(例如 xeyes)在 top-level window 上没有 window 标题。必须找到第一个 child window 才能得到 human-readable window 名称。

所以我最终编写了代码以递归地获取每个 window。然而,XQueryTree() 返回的 window 列表似乎有损坏的数据 - 有些 child window-IDs 为零。所以看起来 XQueryTree() 返回的 children 数量不正确(应该与分配的 children_list 参数的长度匹配)。

最终代码 大部分 失败并出现 BadWindow 错误。 运行 它通过 valgrindXQueryTree().

分配的数据中显示 read-past-end
// COMPILE: gcc -Wall -g recursive_window_count.c -o recursive_window_count  -lX11


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

#define MAX_INDENT 100

unsigned int getWindowCount( Display *display, Window parent_window, int depth )
{
    Window  root_return;
    Window  parent_return;
    Window *children_list = NULL;
    unsigned int list_length = 0;
    char    indent[MAX_INDENT+1];

    // Make some indentation space, depending on the depth
    memset( indent, '[=10=]', MAX_INDENT+1 );
    for ( int i=0; i<depth*4 && i<MAX_INDENT; i++ )
    {
        indent[i] = ' ';
    }

    // query the window list recursively, until each window reports no sub-windows
    printf( "getWindowCount() - %sWindow %lu\n", indent, parent_window );
    if ( 0 != XQueryTree( display, parent_window, &root_return, &parent_return, &children_list, &list_length ) )
    {
        printf( "getWindowCount() - %s    %d window handle returned\n", indent, list_length );
        if ( list_length > 0 && children_list != NULL )
        {
            for ( int i=0; i<list_length; i++)
            {
                // But typically the "top-level" window is not what the user sees, so query any children
                // Only the odd window has child-windows.  XEyes does.
                if ( children_list[i] != 0 )
                {
                    unsigned int child_length = getWindowCount( display, children_list[i], depth+1 );
                    list_length += child_length;  
                }
                else
                {
                    // There's some weirdness with the returned list
                    // We should not have to be doing this at all
                    printf( "%sHuh? child window handle at index #%d (of %d) is zero?\n", indent, i, list_length );
                    break;  
                }
            }

            XFree( children_list ); // cleanup
        }
    }

    return list_length; 
}


int main( int argc, char **argv )
{
    Display *display;
    Window   root_window;

    display = XOpenDisplay( NULL );
    if ( display ) 
    {
        // Get the number of screens, it's not always one!
        int screen_count = XScreenCount( display );

        // Each screen has a root window
        printf( "There are %d screens available on this X Display\n", screen_count );

        for ( int i=0; i < screen_count; i++ )
        {
            root_window = XRootWindow( display, i );
            printf( "Screen %d - %u windows\n", i, getWindowCount( display, root_window, 0 ) );
        }
        XCloseDisplay( display );
    }

    return 0;
}

valgrind 日志的头部:

prompt> valgrind ./recursive_window_count 
==5117== Memcheck, a memory error detector
==5117== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5117== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==5117== Command: ./recursive_window_count
==5117== 
There are 1 screen available on this X Display
getWindowCount() - Window 625
getWindowCount() -     105 window handles returned
getWindowCount() -     Window 48234747
getWindowCount() -         1 window handle returned
getWindowCount() -         Window 75497478
getWindowCount() -             1 window handle returned
getWindowCount() -             Window 75497479
getWindowCount() -                 0 window handles returned
==5117== Invalid read of size 8
==5117==    at 0x1093CD: getWindowCount (recursive_window_count.c:38)
==5117==    by 0x109406: getWindowCount (recursive_window_count.c:40)
==5117==    by 0x109530: main (recursive_window_count.c:77)
==5117==  Address 0x4bff508 is 0 bytes after a block of size 8 alloc'd
==5117==    at 0x483A723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==5117==    by 0x483D017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==5117==    by 0x489A53B: XQueryTree (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==5117==    by 0x10934D: getWindowCount (recursive_window_count.c:29)
==5117==    by 0x109406: getWindowCount (recursive_window_count.c:40)
==5117==    by 0x109530: main (recursive_window_count.c:77)
==5117== 
    Huh? child window handle at index #1 (of 2) is zero?
getWindowCount() -     Window 48236534
getWindowCount() -         1 window handle returned
getWindowCount() -         Window 96468999
getWindowCount() -             1 window handle returned
getWindowCount() -             Window 96469000
getWindowCount() -                 0 window handles returned
    Huh? child window handle at index #1 (of 2) is zero?
... truncated

不,XQueryTree 没有返回垃圾数据。问题出在您的代码上。递归调用 getWindowCount 之后的 list_length += child_length 没有任何意义,并且会触发未定义的行为。只需删除该行。

一些观察:

    char    indent[MAX_INDENT+1];

    // Make some indentation space, depending on the depth
    memset( indent, '[=10=]', MAX_INDENT+1 );
    for ( int i=0; i<depth*4 && i<MAX_INDENT; i++ )
    {
        indent[i] = ' ';
    }

    // query the window list recursively, until each window reports no sub-windows
    printf( "getWindowCount() - %sWindow %lu\n", indent, parent_window );

只使用 printf("... %*s%lu\n", depth * 4, "", parent_window) 而不是所有这些,您将不必关心任何 MAX_INDENT.