Как я могу получить функциональность, похожую на 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, чтобы прочитать метку окна.
Для функциональности указание на окно. Вам нужно SetCapture()
так что вы получаете сообщения мыши, которые находятся за пределами вашего окна. Тогда используйте WindowFromPoint()
преобразовать позицию мыши в окно. Сначала вам нужно будет преобразовать положение moust из координат клиента в координаты окна.
Если вы попробуете позвонить SetCapture()
в любом месте, кроме сообщения о щелчке мыши, вы, вероятно, будете проигнорированы. По этой причине Spy++ заставляет вас щелкнуть значок и перетащить его в окно, на которое вы хотите указать.