如何通过窗口的HWND获取其子窗口?

44

我有一个给定窗口的句柄。如何枚举它的子窗口?


1
通常情况下,我可以从要枚举的窗口中获取HWND。 - Yuriy Faktorovich
非常好 - 我已经更新了您的问题以使其更加清晰。 - Shog9
1
假设您已经了解 Spy++。这是处理这些内容非常有用的工具。 - David
1
如果你喜欢Spy++,那么你可能想尝试一下Winspector Spy。我发现它更易于使用,并且提供了更多的选项。 - Yuriy Faktorovich
5个回答

43

这里提供了一个可行的解决方案:

public class WindowHandleInfo
{
    private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);

    [DllImport("user32")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam);

    private IntPtr _MainHandle;

    public WindowHandleInfo(IntPtr handle)
    {
        this._MainHandle = handle;
    }

    public List<IntPtr> GetAllChildHandles()
    {
        List<IntPtr> childHandles = new List<IntPtr>();

        GCHandle gcChildhandlesList = GCHandle.Alloc(childHandles);
        IntPtr pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);

        try
        {
            EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
            EnumChildWindows(this._MainHandle, childProc, pointerChildHandlesList);
        }
        finally
        {
            gcChildhandlesList.Free();
        }

        return childHandles;
    }

    private bool EnumWindow(IntPtr hWnd, IntPtr lParam)
    {
        GCHandle gcChildhandlesList = GCHandle.FromIntPtr(lParam);

        if (gcChildhandlesList == null || gcChildhandlesList.Target == null)
        {
            return false;
        }

        List<IntPtr> childHandles = gcChildhandlesList.Target as List<IntPtr>;
        childHandles.Add(hWnd);

        return true;
    }
}

如何使用:

class Program
{
    [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

    static void Main(string[] args)
    {
        Process[] anotherApps = Process.GetProcessesByName("AnotherApp");
        if (anotherApps.Length == 0) return;
        if (anotherApps[0] != null)
        {
            var allChildWindows = new WindowHandleInfo(anotherApps[0].MainWindowHandle).GetAllChildHandles();
        }
    }
}

我该如何获取 user32.dll 文件? - jai
1
@jai 这是一个Windows库,它已经存在并在您的计算机上注册。那段代码应该可以正常工作,无需额外的引用。 - Caffé
谢谢@caffe...但是如果我使用user32.dll,应用程序会要求某种权限...我无法运行应用程序...我该如何解决这个问题... - jai
1
相信 GCHandle.FromIntPtr 不会返回 null。 - Drew Noakes
pinvoke.net 是一个很好的参考网站,可以帮助你使用 Windows API。 - Josh Gust

12

使用:

internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);

[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);

您将会在传递给函数中的回调函数上收到回调。


8
我发现最好的解决方案是Managed WindowsAPI。它有一个CrossHair控件,可以用于选择窗口(不是问题的一部分),以及一个AllChildWindows方法,可以获取所有子窗口,这可能包装了EnumChildWindows函数。最好不要重复造轮子。

7

3

这里有一个管理的替代方案可以取代EnumWindows,但你仍然需要使用EnumChildWindows来查找子窗口的句柄。

foreach (Process process in Process.GetProcesses())
{
   if (process.MainWindowTitle == "Title to find")
   {
      IntPtr handle = process.MainWindowHandle;

      // Use EnumChildWindows on handle ...
   }
}

我正在尝试做这件事,但是该进程没有主窗口。 - Epu
1
如果没有主窗口,那么进程将无法获取窗口句柄(即 Process.MainWindowHandle == IntPtr.Zero)。 - Special Touch

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