11

I'm interested in working on a plugin for Keepass, the open-source password manager. Right now, Keepass currently detects what password to copy/paste for you based off of the window title. This prevents Keepass from detecting the current password you need for apps that don't actively update their window title based on the current site (Chrome for instance).

How can I walk through another processes window elements (buttons, labels, textbox) similar to how Spy++ works? When you run Spy++ you can hover over other programs windows and get all kinds of information about various properties concerning various controls (labels, textboxes, etc). Ideally, I'd like my Keepass plugin to enhance the current window detection by walking through the active window's elements in an effort to find a matching account to copy/paste the password.

How can I walk other processes window elements and be able to retrieve label and textbox values using C#?

  • 1
    Interesting questin, I wouldn't be surprised though if it turned out that you will have to PInvoke to WINAPI to achieve that. – Tamas Czinege Dec 28 '09 at 01:08

4 Answers4

25

I've being answering similar questions like this here: How can I detect if a thread has windows handles?. Like it states, the main idea is to enumerate through process windows and their child windows using EnumWindows and EnumChildWindows API calls to get window handles and then call GetWindowText or SendDlgItemMessage with WM_GETTEXT to get window text. I've modified code to make an example which should be doing what you need (sorry it's a bit long :). It iterates through processes and their windows and dumps window text into console.

static void Main(string[] args)
{
    foreach (Process procesInfo in Process.GetProcesses())
    {
        Console.WriteLine("process {0} {1:x}", procesInfo.ProcessName, procesInfo.Id);
        foreach (ProcessThread threadInfo in procesInfo.Threads)
        {
            // uncomment to dump thread handles
            //Console.WriteLine("\tthread {0:x}", threadInfo.Id);
            IntPtr[] windows = GetWindowHandlesForThread(threadInfo.Id);
            if (windows != null && windows.Length > 0)
                foreach (IntPtr hWnd in windows)
                    Console.WriteLine("\twindow {0:x} text:{1} caption:{2}",
                        hWnd.ToInt32(), GetText(hWnd), GetEditText(hWnd));
        }
    }
    Console.ReadLine();
}

private static IntPtr[] GetWindowHandlesForThread(int threadHandle)
{
    _results.Clear();
    EnumWindows(WindowEnum, threadHandle);
    return _results.ToArray();
}

// enum windows

private delegate int EnumWindowsProc(IntPtr hwnd, int lParam);

[DllImport("user32.Dll")]
private static extern int EnumWindows(EnumWindowsProc x, int y);
[DllImport("user32")]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowsProc callback, int lParam);
[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);

private static List<IntPtr> _results = new List<IntPtr>();

private static int WindowEnum(IntPtr hWnd, int lParam)
{
    int processID = 0;
    int threadID = GetWindowThreadProcessId(hWnd, out processID);
    if (threadID == lParam)
    {
        _results.Add(hWnd);
        EnumChildWindows(hWnd, WindowEnum, threadID);
    }
    return 1;
}

// get window text

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);

private static string GetText(IntPtr hWnd)
{
    int length = GetWindowTextLength(hWnd);
    StringBuilder sb = new StringBuilder(length + 1);
    GetWindowText(hWnd, sb, sb.Capacity);
    return sb.ToString();
}

// get richedit text 

public const int GWL_ID = -12;
public const int WM_GETTEXT = 0x000D;

[DllImport("User32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int index);
[DllImport("User32.dll")]
public static extern IntPtr SendDlgItemMessage(IntPtr hWnd, int IDDlgItem, int uMsg, int nMaxCount, StringBuilder lpString);
[DllImport("User32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd);

private static StringBuilder GetEditText(IntPtr hWnd)
{
    Int32 dwID = GetWindowLong(hWnd, GWL_ID);
    IntPtr hWndParent = GetParent(hWnd);
    StringBuilder title = new StringBuilder(128);
    SendDlgItemMessage(hWndParent, dwID, WM_GETTEXT, 128, title);
    return title;
}

hope this helps, regards

Community
  • 1
  • 1
serge_gubenko
  • 20,186
  • 2
  • 61
  • 64
  • 1
    EnumChildWindows already recurses, so you do double recursion here; see [The Old New Thing on EnumChildWindows](http://blogs.msdn.com/b/oldnewthing/archive/2007/01/16/1478717.aspx) – Jeroen Wiert Pluimers Jan 24 '12 at 11:28
  • 1
    I noticed that on a few answers people say to call EnumChildWindows recursively. DON'T DO THAT. Read the MSDN doc for it - it is already recursive, should have been more aptly named EnumDescendants. – Mike Marynowski Feb 11 '11 at 04:16
  • How could we avoid an infinite loop here which happens on my machine. It should logically strike out after drilling through child windows. – Raheel Khan Jun 05 '13 at 10:49
3

You can use EnumWindows to find every top-level Chrome window and then call EnumChildWindows recursively (see Jeroen Wiert Pluimers' comment) to get every child of the main window. Alternatively, once you have the main Chrome window, you can use GetWindow to manually navigate the tree since you probably know what you're looking for (3rd child's children collection or something similar).

Once you find your window, you can use SendMessage with a WM_GETTEXT parameter to read the window's label.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
Blindy
  • 65,249
  • 10
  • 91
  • 131
  • @Blindy, thank you for the info! I'm looking to do this to any active window, not just Chrome. Once I have the handles to the windows/child-windows, how do I navigate through that windows elements? – mmcdole Dec 28 '09 at 01:10
  • These are all native Windows API calls though and not managed .NET Framework calls so they require P/Invoke from CLR. – Tamas Czinege Dec 28 '09 at 01:11
  • @DrJokepu, Aye. I figured I'd have to be doing some P/Invoke magic. – mmcdole Dec 28 '09 at 01:12
  • @Blindy, Once I have a reference to a window, how can I iterate through all its buttons, labels, textbox, etc? – mmcdole Dec 28 '09 at 01:15
  • Browsers usually use windowless controls, due to the 10k handle limit in windows. You may need to write specific hooks for each browser. – arul Dec 28 '09 at 01:54
  • Well if you can see it in Spy++, you can see it with this too. To navigate the windows, you use `GetWindow` with `GW_HWNDNEXT`/`GW_HWNDPREV` for siblings and `GW_CHILD` for children. – Blindy Dec 28 '09 at 05:49
  • 1
    Don't recurse EnumChildWindows as it already recurses. See [The Old New Thing on EnumChildWindows](http://blogs.msdn.com/b/oldnewthing/archive/2007/01/16/1478717.aspx) – Jeroen Wiert Pluimers Jan 24 '12 at 11:29
2

You can use HWndSpy. Source code is here.

enter image description here

Community
  • 1
  • 1
Stack
  • 205
  • 2
  • 11
1

For the functionality of pointing to a window. You need to SetCapture() so that you get mouse messages that are outside of your window. Then use WindowFromPoint() to convert a mouse position to a Window. You will need to convert the moust position from client coordinates to window coordinates first.

If you try an call SetCapture() anywhere but on a mouse click message, you will probably be ignored. This is the reason that Spy++ makes you click on an Icon and drag and drop it on the window you want to point to.

John Knoeller
  • 33,512
  • 4
  • 61
  • 92
  • What I'm attempting to do is simply iterate through a windows elements without the mouse interaction. Once I call EnumChildWindows, how can I get the text of whatever elements belong to that child window? – mmcdole Dec 28 '09 at 01:33
  • Send the window messages. WM_GETTEXT, etc. If that's not good enought, you need to define what you mean by 'elements'? – John Knoeller Dec 28 '09 at 01:40
  • @John Knoeller, all text labels, textboxes and buttons. – mmcdole Dec 28 '09 at 01:44
  • `EnumChildWindows` of your child windows. repeat recursively until you run out of generations. – John Knoeller Dec 28 '09 at 01:54
  • @Simucal, where you able to enumerate all elements of the child window and get the corresponding values? if yes, please share – Zee99 Sep 17 '10 at 15:20