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

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

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

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

Ответ 1

Я отвечаю на подобные вопросы, такие как здесь: Как определить, работает ли поток с окнами?. Как и указано, основная идея состоит в том, чтобы перечислить через окна процессов и их дочерние окна с помощью EnumWindows и EnumChildWindows API-вызовы для получения оконных дескрипторов, а затем вызвать 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;
}

надеюсь, что это поможет, считает

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

введите описание изображения здесь