How can I get functionality similar to Spy++ in my C# app?

走远了吗. 提交于 2019-12-17 10:33:26

问题


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:


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




回答2:


Have a look at this article here which contains information about the Managed Spy and why the author wrote the tool.




回答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.




回答4:


You can use HWndSpy. Source code is here.




回答5:


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.



来源:https://stackoverflow.com/questions/1967604/how-can-i-get-functionality-similar-to-spy-in-my-c-sharp-app

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!