2009-12-28 31 views
9

Estoy interesado en trabajar en un complemento para Keepass, el administrador de contraseñas de código abierto. En este momento, Keepass actualmente detecta qué contraseña copiar/pegar en función del título de la ventana. Esto evita que Keepass detecte la contraseña actual que necesita para las aplicaciones que no actualizan activamente el título de su ventana en función del sitio actual (por ejemplo, Chrome).¿Cómo puedo obtener una funcionalidad similar a la de Spy ++ en mi aplicación C#?

¿Cómo puedo caminar a través de otros elementos de la ventana de procesos (botones, etiquetas, cuadros de texto) de forma similar a cómo funciona Spy ++? Cuando ejecuta Spy ++, puede pasar el puntero del mouse sobre otras ventanas de programas y obtener todo tipo de información sobre varias propiedades relacionadas con varios controles (etiquetas, cuadros de texto, etc.). Idealmente, me gustaría que mi complemento Keepass mejore la detección de ventanas actual al recorrer los elementos de la ventana activa en un esfuerzo por encontrar una cuenta coincidente para copiar/pegar la contraseña.

¿Cómo puedo caminar otros elementos de la ventana de procesos y ser capaz de recuperar valores de etiquetas y cuadros de texto usando C#?

+1

questin interesante, no me sorprendería aunque si resultó que tendrá que PInvoke a Winapi para lograrlo. –

Respuesta

22

He estado respondiendo preguntas similares a esta aquí: How can I detect if a thread has windows handles?. Como dice, la idea principal es enumerar a través de ventanas de proceso y sus ventanas secundarias usando EnumWindows y EnumChildWindows API llamadas para obtener identificadores de ventana y luego llamar a GetWindowText o SendDlgItemMessage con WM_GETTEXT para obtener texto de ventana. He modificado el código para hacer un ejemplo que debería estar haciendo lo que necesita (lo siento, es un poco largo :). Se repite a través de los procesos y sus ventanas y vuelca el texto de la ventana en la consola.

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

esperanza que esta ayuda, que se refiere a

+1

EnumChildWindows ya está recursivamente, por lo que realiza doble recursión aquí; ver [The Old New Thing en EnumChildWindows] (http://blogs.msdn.com/b/oldnewthing/archive/2007/01/16/1478717.aspx) –

+1

Noté que en algunas respuestas la gente dice llamar a EnumChildWindows recursivamente . NO HAGAS ESO Lea el documento de MSDN para ello: ya es recursivo, debería haber sido más acertadamente llamado EnumDescendants. –

+0

¿Cómo podríamos evitar un bucle infinito aquí que ocurre en mi máquina? Debería apagarse lógicamente después de explorar ventanas secundarias. –

3

Puede utilizar EnumWindows para encontrar todas las ventanas de Chrome de nivel superior y luego llamar EnumChildWindows recursiva(ver comentario Jeroen Wiert Pluimers') para llegar a todos los niños de la ventana principal. Alternativamente, una vez que tenga la ventana principal de Chrome, puede usar GetWindow para navegar manualmente por el árbol ya que probablemente sepa lo que está buscando (colección de niños del 3er niño o algo similar).

Una vez que encuentre su ventana, puede usar SendMessage con un parámetro WM_GETTEXT para leer la etiqueta de la ventana.

+0

@Blindy, ¡gracias por la información! Estoy buscando hacer esto en cualquier ventana activa, no solo en Chrome.Una vez que tengo los identificadores de windows/child-windows, ¿cómo navego a través de esos elementos de Windows? – mmcdole

+0

Todas estas son llamadas nativas de la API de Windows y no son llamadas de .NET Framework administradas, por lo que requieren P/Invoke de CLR. –

+0

@DrJokepu, Aye. Pensé que tendría que estar haciendo algo de magia de P/Invoke. – mmcdole

1

Para la funcionalidad de señalar a una ventana. Necesita SetCapture() para obtener mensajes del mouse que estén fuera de su ventana. Luego use WindowFromPoint() para convertir la posición de un mouse en una Ventana. Primero tendrá que convertir la posición de la palanca de las coordenadas del cliente a las coordenadas de la ventana.

Si intenta una llamada SetCapture() en cualquier lugar que no sea con un clic del mouse, es probable que lo ignoren. Esta es la razón por la que Spy ++ hace que haga clic en un icono y lo arrastre y lo suelte en la ventana a la que desea apuntar.

+0

Lo que intento hacer es simplemente iterar a través de una ventana elementos sin la interacción del mouse. Una vez que llamo a EnumChildWindows, ¿cómo puedo obtener el texto de los elementos que pertenecen a esa ventana secundaria? – mmcdole

+0

Envía los mensajes de la ventana. WM_GETTEXT, etc. Si eso no es lo suficientemente bueno, ¿necesita definir lo que quiere decir con 'elementos'? –

+0

@ John Knoeller, todas las etiquetas de texto, cuadros de texto y botones. – mmcdole

3

Eche un vistazo a este artículo here que contiene información sobre Managed Spy y por qué el autor escribió la herramienta.

+0

Tenga en cuenta que la aplicación a la que se refiere solo funciona para aplicaciones * administradas *, no como las que no son administradas como la mayoría de los navegadores. – Blindy

0

Puede utilizar HWndSpy. El código fuente es here.

enter image description here

Cuestiones relacionadas