如何在我的 C# 应用程序中获得类似于 Spy++ 的功能?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1967604/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 22:16:33  来源:igfitidea点击:

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

c#winapispy++

提问by

I'm interested in working on a plugin for Keepass, the open-source password manager. Right now, Keepasscurrently 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).

我有兴趣为开源密码管理器Keepass 开发插件。现在,Keepass当前根据窗口标题检测要为您复制/粘贴的密码。这可以防止 Keepass 检测您需要的当前密码,这些应用程序不会根据当前站点(例如 Chrome)主动更新其窗口标题。

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.

如何遍历类似于 Spy++ 工作方式的另一个进程窗口元素(按钮、标签、文本框)?当您运行 Spy++ 时,您可以将鼠标悬停在其他程序窗口上,并获取有关各种控件(标签、文本框等)的各种属性的各种信息。理想情况下,我希望我的 Keepass 插件通过遍历活动窗口的元素来增强当前窗口检测,以便找到匹配的帐户来复制/粘贴密码。

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

如何遍历其他进程窗口元素并能够使用 C# 检索标签和文本框值?

采纳答案by serge_gubenko

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 EnumWindowsand EnumChildWindowsAPI 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.

我在这里回答类似的问题:如何检测线程是否具有 Windows 句柄?. 就像它所说的那样,主要思想是使用EnumWindowsEnumChildWindowsAPI 调用枚举进程窗口及其子窗口以获取窗口句柄,然后使用 WM_GETTEXT 调用 GetWindowText 或 SendDlgItemMessage 以获取窗口文本。我已经修改了代码以制作一个示例,该示例应该可以满足您的需求(对不起,它有点长:)。它遍历进程及其窗口并将窗口文本转储到控制台。

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

希望这有帮助,问候

回答by Blindy

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

您可以使用EnumWindows查找每个顶级 Chrome 窗口,然后递归调用EnumChildWindows (请参阅 Jeroen Wiert Pluimers 的评论)以获取主窗口的每个子窗口。或者,一旦您拥有 Chrome 主窗口,您就可以使用GetWindow手动导航树,因为您可能知道自己在寻找什么(第三个孩子的孩子集合或类似的东西)。

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

找到您的窗口后,您可以使用带有 WM_GETTEXT 参数的SendMessage来读取窗口的标签。

回答by John Knoeller

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.

用于指向窗口的功能。您需要这样做才能SetCapture()获得窗口外的鼠标消息。然后用于WindowFromPoint()将鼠标位置转换为窗口。您需要先将鼠标位置从客户端坐标转换为窗口坐标。

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.

如果您在SetCapture()任何地方尝试呼叫,但不是在鼠标单击消息上,您可能会被忽略。这就是 Spy++ 让您单击图标并将其拖放到您想要指向的窗口的原因。

回答by t0mm13b

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

这里查看这篇文章其中包含有关托管间谍的信息以及作者编写该工具的原因。

回答by Stack

You can use HWndSpy. Source code is here.

您可以使用 HWndSpy。源代码在这里

enter image description here

在此处输入图片说明