13

Using Microsoft Spy++, I can see that the following windows that belong to a process:

Process XYZ window handles, displayed in tree form just like Spy++ gives me:

A
  B
  C
     D
E
F
  G
  H
  I
  J
     K

I can get the process, and the MainWindowHandle property points to the handle for window F. If I enumerate the child windows using I can get a list of window handles for G through K, but I can't figure out how to find the window handles for A through D. How can I enumerate windows that are not children of the handle specified by MainWindowHandle of the Process object?

To enumerate I'm using the win32 call:

[System.Runtime.InteropServices.DllImport(strUSER32DLL)]
            public static extern int EnumChildWindows(IntPtr hWnd, WindowCallBack pEnumWindowCallback, int iLParam);
Mark Elliot
  • 75,278
  • 22
  • 140
  • 160
Jeremy
  • 44,950
  • 68
  • 206
  • 332
  • There used to be a C# class that wrapped all this up nicely over on the GotDotNet site before it GotShutDown. Can't find it anymore but it's out there somewhere... – Swingline Rage Jun 10 '10 at 22:55

3 Answers3

12

Pass IntPtr.Zero as hWnd to get every root window handle in the system.

You can then check the windows' owner process by calling GetWindowThreadProcessId.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 4
    Is that the only way? Will try this. I wonder how time consuming this operation will be... – Jeremy Jun 11 '10 at 14:49
11

For everyone still wondering, this is the answer:

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;
}

for WindowsInterop:

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

for 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);

Now one can simply get every root window by GetRootWindowsOfProcess, and their children by GetChildWindows.

xamid
  • 440
  • 11
  • 25
  • Not just your example, but GetChildWindows simply doesn't work passing in Process.GetCurrentProcess().MainWindowHandle. I launch a child window (modally or not), call GetChildWindows, and the EnumWindow callback simply never fires. – dudeNumber4 Feb 24 '17 at 15:58
  • You mean OS? Win8. – dudeNumber4 Mar 13 '17 at 13:33
  • 1
    WIndows 8 is the problem. – xamid Mar 15 '17 at 14:09
  • No, just tried it on Win; same thing. This is one thing MS has always been good at; these API calls work the same across OSs. – dudeNumber4 Mar 17 '17 at 14:28
  • I tried Win8 and it didn't work. They changed how the screen and handles work in Win8. But for Win7, it works for me (same notebook - I downgraded it after those problems). So there must be another reason why it doesn't work for you on Win7. It's not the OS. – xamid Mar 18 '17 at 02:04
  • I wrote Win 10 (no space) above and it turned into a semicolon. So windows 10 doesn't work either. – dudeNumber4 Mar 27 '17 at 11:55
  • WIndows 8 is the problem - how can it be done on windows 8 then? – MrLister May 20 '17 at 04:49
8

You can use EnumWindows to get every top-level window, and then filter the results based on GetWindowThreadProcessId.

zildjohn01
  • 11,339
  • 6
  • 52
  • 58