获取一个进程的所有窗口句柄

13

使用 Microsoft Spy++,我可以看到属于一个进程的以下窗口:

进程 XYZ 的窗口句柄以树形形式显示,就像 Spy++ 一样:

A
  B
  C
     D
E
F
  G
  H
  I
  J
     K

我可以获取进程,而MainWindowHandle属性指向窗口F的句柄。如果我使用“EnumChildWindows”枚举子窗口,我可以获取G到K的窗口句柄列表,但我无法找出A到D的窗口句柄。如何枚举非进程对象MainWindowHandle指定的父级窗口的窗口?

我正在使用win32调用来进行枚举:

[System.Runtime.InteropServices.DllImport(strUSER32DLL)]
            public static extern int EnumChildWindows(IntPtr hWnd, WindowCallBack pEnumWindowCallback, int iLParam);

以前在GotDotNet网站上有一个很好地封装了所有这些的C#类,但现在该网站已经关闭了。虽然找不到它了,但它肯定还存在于某个地方... - Swingline Rage
3个回答

12

IntPtr.Zero作为hWnd传递,以获取系统中的每个根窗口句柄。

然后,您可以通过调用GetWindowThreadProcessId来检查窗口的拥有者进程。


4
那是唯一的方法吗?我会尝试这个。我想知道这个操作需要多少时间... - Jeremy

11

对于仍然疑惑的所有人,这是答案:

List<IntPtr> GetRootWindowsOfProcess(int pid)
{
    List<IntPtr> rootWindows = GetChildWindows(IntPtr.Zero);
    List<IntPtr> dsProcRootWindows = new List<IntPtr>();
    foreach (IntPtr hWnd in rootWindows)
    {
        uint lpdwProcessId;
        WindowsInterop.User32.GetWindowThreadProcessId(hWnd, out lpdwProcessId);
        if (lpdwProcessId == pid)
            dsProcRootWindows.Add(hWnd);
    }
    return dsProcRootWindows;
}

public static List<IntPtr> GetChildWindows(IntPtr parent)
{
    List<IntPtr> result = new List<IntPtr>();
    GCHandle listHandle = GCHandle.Alloc(result);
    try
    {
        WindowsInterop.Win32Callback childProc = new WindowsInterop.Win32Callback(EnumWindow);
        WindowsInterop.User32.EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
    }
    finally
    {
        if (listHandle.IsAllocated)
            listHandle.Free();
    }
    return result;
}

private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
    GCHandle gch = GCHandle.FromIntPtr(pointer);
    List<IntPtr> list = gch.Target as List<IntPtr>;
    if (list == null)
    {
        throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
    }
    list.Add(handle);
    //  You can modify this to check to see if you want to cancel the operation, then return a null here
    return true;
}

适用于WindowsInterop:

public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

对于 WindowsInterop.User32:

[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

[DllImport("user32.Dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);
现在可以通过 GetRootWindowsOfProcess 获取每个根窗口,通过 GetChildWindows 获取它们的子窗口。

不仅是你的例子,而是使用Process.GetCurrentProcess().MainWindowHandle作为参数调用GetChildWindows根本不起作用。我启动一个子窗口(无论是模态还是非模态),调用GetChildWindows,但EnumWindow回调根本没有触发。 - dudeNumber4
1
Windows 8 是问题所在。 - xamid
不,我只是在Win上尝试过;同样的结果。这是微软一直擅长的事情;这些API调用在各个操作系统上都能正常工作。 - dudeNumber4
我打了Win10(没有空格),但它变成了分号。所以Windows 10也不起作用。 - dudeNumber4
Windows 8是个问题 - 那么在Windows 8上怎么做呢? - MrLister
显示剩余2条评论

8

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