通过句柄或pid获取窗口的子窗口

通过句柄或pid获取窗口的子窗口

问题描述:

我有主进程/窗口的句柄和pid.
我想获取进程/窗口的子级 - 只有带有标题的可视窗口.

I have handle and pid of main process / window.
I want to get the children of the process / window - only the visual windows with title.

为了实现我想要的,我使用了以下内容:

to achieve what I want, I used the following:

[DllImport("user32.dll")] private static extern int EnumWindows(EnumWindowsProc ewp, int lParam);
[DllImport("user32.dll", SetLastError = true)] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")] private static extern bool IsWindowVisible(int hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowText(int hWnd, StringBuilder title, int size);


public delegate bool EnumWindowsProc(int hWnd, int lParam); //Delegate used for EnumWindows() callback function.


public void GetChildren()
{
    //Declare a callback delegate for EnumWindows() API call.
    EnumWindowsProc ewp = new EnumWindowsProc(EvalWindow);
    //Enumerate all Windows.
    EnumWindows(ewp, 0);
}

//EnumWindows CALLBACK function
private bool EvalWindow(int hWnd, int lParam)
{
    //Get the ThreadProcessId of the Window.
    IntPtr handle = (IntPtr)hWnd;
    uint currentThreadPid;
    GetWindowThreadProcessId(handle, out currentThreadPid);

    //Check if the Window is children of the current Window (if it is, it will have the same pid).
    if (currentThreadPid != _threadPid)
        return true;

    //Check if Window is Visible or not.
    if (!IsWindowVisible(hWnd))
        return true;

    //Get the Window's Title.
    StringBuilder title = new StringBuilder(256);
    GetWindowText(hWnd, title, 256);

    //Check if Window has Title.
    if (title.Length == 0)
        return true;

    //Add new Window to the _childrenhWnd.
    _childrenhWnd.Add(handle);

    return true;
}

其中 _threadPid 是父进程的 pid.

where _threadPid is the pid of the parent.

效果很好,但我觉得有一个更好的方法,而不是浏览所有打开的窗口并过滤它们.

It works great but I feel there's a better way of doing it instead of going through all the opened windows and filter them.

我说得对吗?我怎样才能做得更好?

Am I right? how can I do it better?

我看到了一些关于 EnumChildWindows 的东西,但我也能做到.

I saw something about EnumChildWindows but I could achieve the same.

基于 RaymondChen 的评论,我想澄清我所说的孩子"的意思:
1.如果您打开便签并添加便笺 - 便笺是儿童.
2. 如果你打开 Paint.NET 并按 (f5, f6, f7, f8) 打开面板- 这些是子窗口.
程序的每个窗口不是主窗口(也是对话框),我称之为子窗口.

Based on RaymondChen comment, I want to clarify what I mean by saying "children":
1. If you open sticky notes and add notes - the notes are children.
2. If you open Paint.NET and press (f5, f6, f7, f8) to open the panels - these are child windows.
Every window of the program which is not the main (dialogs also) I call children.

编辑 2:

这是我应用汉斯方法后的代码:

Here's my code after applying Hans's method:

[DllImport("user32.dll")] static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll")] private static extern bool IsWindowVisible(int hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowText(int hWnd, StringBuilder title, int size);


delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);


public void GetChildren()
{
   foreach (ProcessThread processThread in _process.Threads)
   {
       EnumThreadWindows(processThread.Id,
        (hWnd, lParam) => 
        {
            //Check if Window is Visible or not.
            if (!IsWindowVisible((int)hWnd))
                return true;

            //Get the Window's Title.
            StringBuilder title = new StringBuilder(256);
            GetWindowText((int)hWnd, title, 256);

            //Check if Window has Title.
            if (title.Length == 0)
                return true;

            _childrenhWnd.Add(hWnd);

            return true;
        }, IntPtr.Zero);
   }
}

不需要使用 GetWindowLongPtr 因为所有拥有的窗口都必须有标题并且是可见的,否则我真的不在乎所以这些条件已经过滤了拥有的窗口.

didnt need to use GetWindowLongPtr because all the owned windows must have title and be visible else I dont really care so these conditions already filter the owned windows.

很明显,您实际上并不是在谈论子窗口,EnumWindows() 不会枚举它们.这些肯定是拥有的窗口,它们保持在所有者的顶部,并在所有者最小化时最小化.用于工具窗口和对话框,如 Paint.NET 使用.没有专用的 winapi 函数来枚举拥有的窗口.

Clearly you are not actually talking about child windows, EnumWindows() doesn't enumerate them. These are surely owned windows, they stay on top of their owner and get minimized when the owner is minimized. Used for tool windows and dialogs, like Paint.NET uses. There is no dedicated winapi function to enumerate owned windows.

你可以让它更有效率.枚举进程中的线程,使用Process.Threads.然后对于每个线程使用 EnumThreadWindows().带有 GWLP_HWNDPARENT 的 GetWindowLongPtr() 返回所有者的句柄,如果它是一个拥有的窗口则非零.

You can make it more efficient. Enumerate the threads in the process, use Process.Threads. Then for each thread use EnumThreadWindows(). GetWindowLongPtr() with GWLP_HWNDPARENT returns the handle of the owner, non-zero if it is an owned window.