Как я могу получить функциональность, похожую на Spy++ в моем приложении C#?

Я заинтересован в работе над плагином для Keepass, менеджера паролей с открытым исходным кодом. Прямо сейчас Keepass в настоящее время определяет, какой пароль для вас копировать / вставлять, основываясь на заголовке окна. Это препятствует тому, чтобы Keepass обнаружил текущий пароль, который вам нужен для приложений, которые не обновляют заголовок своего окна на основе текущего сайта (например, Chrome).

Как я могу пройти через другие элементы окна процессов (кнопки, метки, текстовое поле), аналогично тому, как работает Spy++? Когда вы запускаете Spy++, вы можете наводить курсор на окна других программ и получать всевозможную информацию о различных свойствах, касающихся различных элементов управления (метки, текстовые поля и т. Д.). В идеале мне бы хотелось, чтобы мой плагин Keepass улучшал обнаружение текущего окна, просматривая элементы активного окна, чтобы найти подходящую учетную запись для копирования / вставки пароля.

Как я могу пройти другие элементы окна процессов и быть в состоянии получить значения меток и текстовых полей, используя C#?

5 ответов

Решение

Я отвечаю на подобные вопросы вот так: Как я могу определить, есть ли у потока дескрипторы окон?, Как говорится, основная идея состоит в том, чтобы перечислять через окна процессов и их дочерние окна, используя вызовы API EnumWindows и EnumChildWindows для получения дескрипторов окна, а затем вызывать GetWindowText или SendDlgItemMessage с WM_GETTEXT для получения текста окна. Я изменил код, чтобы сделать пример, который должен делать то, что вам нужно (извините, это немного долго:). Он перебирает процессы и их окна и выводит текст окна в консоль.

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;
}

надеюсь, что это помогает, привет

Взгляните на эту статью, которая содержит информацию об управляемом шпионе и почему автор написал инструмент.

Вы можете использовать EnumWindows, чтобы найти каждое окно Chrome верхнего уровня, а затем рекурсивно вызвать EnumChildWindows (см. Комментарий Jeroen Wiert Pluimers), чтобы получить каждого дочернего элемента главного окна. В качестве альтернативы, если у вас есть главное окно Chrome, вы можете использовать GetWindow для ручной навигации по дереву, поскольку вы, вероятно, знаете, что ищете ( дочерняя коллекция 3-го ребенка или что-то подобное).

Как только вы найдете свое окно, вы можете использовать SendMessage с параметром WM_GETTEXT, чтобы прочитать метку окна.

Вы можете использовать HWndSpy. Исходный код здесь.

Для функциональности указание на окно. Вам нужно SetCapture() так что вы получаете сообщения мыши, которые находятся за пределами вашего окна. Тогда используйте WindowFromPoint() преобразовать позицию мыши в окно. Сначала вам нужно будет преобразовать положение moust из координат клиента в координаты окна.

Если вы попробуете позвонить SetCapture() в любом месте, кроме сообщения о щелчке мыши, вы, вероятно, будете проигнорированы. По этой причине Spy++ заставляет вас щелкнуть значок и перетащить его в окно, на которое вы хотите указать.

Другие вопросы по тегам