5

I am building an applicaton, It will get all control have into application winform is running. First, I can inject dll into application winform is running and get handle of application winform is running. After I get all child window into applcation. Next, I want get all controls into child window by FindWindowEx. But I can't

Here is code :

static ArrayList GetAllChildrenWindowHandles(IntPtr hParent, int maxCount)
    {
        ArrayList result = new ArrayList();
        int ct = 0;
        IntPtr prevChild = IntPtr.Zero;
        IntPtr currChild = IntPtr.Zero;
        while (true && ct < maxCount)
        {
            currChild = FindWindowEx(hParent, prevChild, null, null);
            if (currChild == IntPtr.Zero)
            {
                int errorCode = Marshal.GetLastWin32Error();
                break;
            }
            result.Add(currChild);
            prevChild = currChild;
            ++ct;
        }
        return result;
    }

I get a handle of child window and use it is parent. But I can't get all control into child window by FindWindowEx . Sorry for my english

user2208401
  • 95
  • 1
  • 1
  • 5
  • 1
    If you're going to find all the children of some particular window, you want [`EnumChildWindows`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633494.aspx). – Jerry Coffin Apr 24 '13 at 15:52
  • The logical structure is a tree. Most easily traversed with a recursive function. However, that surely also means that an ArrayList is not the proper data structure to store the result. – Hans Passant Apr 24 '13 at 19:43
  • "The logical structure is a tree. Most easily traversed with a recursive function. However, that surely also means that an ArrayList is not the proper data structure to store the result" I think too, but I don't know how do this? Do you know? Thank :) – user2208401 Apr 25 '13 at 01:11

2 Answers2

8

You can use the code below. Put it into a helper class somewhere, and e.g. use it like this...

var hwndChild = EnumAllWindows(hwndTarget, childClassName).FirstOrDefault();  

You can 'lose' the class check if you wish - but usually you're checking for a specific target.

You may also wanna check this post I made a while go - which is using this method to set a focus on a remote window (and those scenarios are quite common, and you'll hit that snag sooner or later).
Pinvoke SetFocus to a particular control

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

[DllImport("user32.Dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static public extern IntPtr GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);

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);
    return true;
}

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

public static string GetWinClass(IntPtr hwnd)
{
    if (hwnd == IntPtr.Zero)
        return null;
    StringBuilder classname = new StringBuilder(100);
    IntPtr result = GetClassName(hwnd, classname, classname.Capacity);
    if (result != IntPtr.Zero)
        return classname.ToString();
    return null;
}

public static IEnumerable<IntPtr> EnumAllWindows(IntPtr hwnd, string childClassName)
{
    List<IntPtr> children = GetChildWindows(hwnd);
    if (children == null)
        yield break;
    foreach (IntPtr child in children)
    {
        if (GetWinClass(child) == childClassName)
            yield return child;
        foreach (var childchild in EnumAllWindows(child, childClassName))
            yield return childchild;
    }
}
Community
  • 1
  • 1
NSGaga-mostly-inactive
  • 14,052
  • 3
  • 41
  • 51
  • It's not work, I can get all controls into child windows of application winform running. I only get child windows but can't get all controls into child windows. – user2208401 Apr 25 '13 at 01:13
  • that works - try Spy++ (or [this](http://mwinapi.sourceforge.net/)) to test - see this exact same code [here](http://stackoverflow.com/questions/7014190/why-is-enumchildwindows-skipping-children) (and make sure you load all, give it time). And try my suggestion from 'SetFocus' link (attach to a 'thread'). If your controls are `non-standard` (non windows) it won't work. And remove the `.FirstOrDefault()` of course :) – NSGaga-mostly-inactive Apr 25 '13 at 01:50
  • I am getting error "The type or namespace name 'Win32Callback' could not be found (are you missing a using directive or an assembly reference?)" – Ashish Mar 18 '14 at 06:20
1

Try Spy++ and see the controls you are trying to enumerate are windows or not. If they are not windows, you can not enumerate them using this API.

crapple
  • 11
  • 1
  • 3
  • 2
    How could a control displayed by Spy++ not be a window? – Werner Henze Apr 25 '13 at 11:11
  • @WernerHenze: That's the point of using Spy++. The code in the question may fail because the control is not a window, but in that case Spy++ would fail in the same way. I.e. we use Spy++ to establish the correct behavior. – MSalters Apr 25 '13 at 12:27
  • @MSalters: So you are talking for example about a parent window which is drawing something in it's client area that looks like a control and handling mouse clicks so it acts like a control, but in fact it is not a real window, it just looks like one from user perspective!? – Werner Henze Apr 25 '13 at 12:57
  • @WernerHenze: Yup, like for example Internet Explorer does. – MSalters Apr 25 '13 at 12:59
  • 3
    So - I've used Spy++ and have determined that the text I need to get at isn't a control (or 'Window') in its own right, but I can get its parent window. Is there an API that I can use to somehow query this text anyway!? – Chris Roberts Jan 31 '18 at 15:17