10

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

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

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

4

5 回答 5

25

我在这里回答了类似的问题:如何检测线程是否有窗口句柄?. 就像它所说的那样,主要思想是使用EnumWindowsEnumChildWindows API 调用枚举进程窗口及其子窗口以获取窗口句柄,然后使用 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;
}

希望这会有所帮助,问候

于 2009-12-28T02:07:37.517 回答
3

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

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

于 2009-12-28T01:07:02.033 回答
3

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

于 2009-12-28T01:42:52.777 回答
2

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

在此处输入图像描述

于 2016-01-12T06:20:26.473 回答
1

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

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

于 2009-12-28T01:28:53.617 回答