2011-03-09 13 views
9

Necesito manejar otra aplicación de Windows mediante programación, buscando google Encontré un ejemplo que maneja la calculadora de Windows utilizando DLLImport Attribute e importa las funciones de user32.dll en los gestionados en C#.FindWindowEx de user32.dll está devolviendo un identificador de cero y el código de error de 127 utilizando dllimport

La aplicación se está ejecutando, obtengo el identificador para la ventana principal, es decir, la propia calculadora, pero el código posterior no funciona. El método FindWindowEx no devuelve los identificadores de los elementos secundarios de Calculadora como los botones y el cuadro de texto.

He intentado usar SetLastError = True en DLLImport y he encontrado que recibo un código de error de 127 que es "Procedimiento no encontrado".

Este es el enlace desde donde conseguí aplicación de ejemplo:

http://www.codeproject.com/script/Articles/ArticleVersion.aspx?aid=14519&av=34503

Por favor ayuda si alguien sabe cómo solucionarlo.

ACTUALIZACIÓN: El DLLImport es:

[DllImport("user32.dll", SetLastError = true)] 
     public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle); 

El código que no está funcionando es:

hwnd=FindWindow(null,"Calculator"); // This is working, I am getting handle of Calculator 

// The following is not working, I am getting hwndChild=0 and err = 127 
hwndChild = FindWindowEx((IntPtr)hwnd,IntPtr.Zero,"Button","1"); 

       Int32 err = Marshal.GetLastWin32Error(); 
+0

¿Qué versión de Windows está ejecutando? –

+0

Si tiene un problema con P/Invoke, publique su declaración con el atributo 'DllImport'. Si tiene algún problema con algún código, publique el código que no está funcionando. – Gabe

+0

@CodyGray Tengo Windows 7 Professional. – teenup

Respuesta

11

El código que está tratando se basa en los títulos de los botones individuales para identificarlos. Por ejemplo, se utiliza el código siguiente para obtener un identificador para el botón "1":

hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "1"); 

que especifica "botón" para el nombre de la clase de ventana, y "1" para el nombre de la ventana (en el caso de un botón, esto es lo mismo que el texto de título que se muestra en el botón).

Este código funcionó bien en Windows XP (y versiones anteriores), donde los botones de la calculadora se identificaron con leyendas textuales. El botón "1" tenía un nombre de ventana de "1" y, por lo tanto, "1" se mostraba como el título del botón.

Sin embargo, parece que las cosas han cambiado en Windows 7 (posiblemente también en Vista, aunque no puedo verificar esto porque no tengo acceso a dicho sistema). El uso de Spy ++ para investigar la ventana de la calculadora confirma que el botón "1" ya no tiene un nombre de ventana de "1". De hecho, no tiene un nombre de ventana en absoluto; la leyenda es NULL Presumiblemente, el nuevo aspecto elegante de la calculadora requería que los botones se dibujaran a medida, por lo que los subtítulos ya no son necesarios para indicar qué botón corresponde a cada función. Las rutinas de pintura personalizadas se encargan de dibujar los subtítulos necesarios.

Como no se puede encontrar ningún botón con el texto de ventana que especificó, se devuelve un valor de 0 (NULL) para el identificador de ventana.

El documentation for the FindWindowEx function indica que puede especificar NULL para el parámetro lpszWindow, pero que esta voluntad, por supuesto, partido todas las ventanas de la clase especificada. Probablemente no es lo que quiere en este caso, ya que la aplicación de calculadora tiene un montón de botones.

No conozco una buena solución. La calculadora no fue diseñada para ser "automatizada" de esta manera, y Microsoft nunca garantizó que no cambiaría su funcionamiento interno. Ese es un riesgo que toma al usar este enfoque para meterse con las ventanas de otras aplicaciones.


EDIT: El código de haber asociado también está mal de otra manera bastante grave, incluso en las versiones anteriores de Windows. Declara la variable hwnd como tipo int, en lugar de como tipo IntPtr. Dado que un identificador de ventana es un puntero, siempre debe almacenarlo como un tipo IntPtr. Eso también soluciona el lanzamiento feo en la llamada a la función FindWindowEx que debería haber enviado banderas rojas.

También deberá corregir la declaración de SendMessage para que su primer parámetro sea del tipo IntPtr.

El código debería haberse escrito así:

IntPtr hwnd = IntPtr.Zero; 
IntPtr hwndChild = IntPtr.Zero; 

//Get a handle for the Calculator Application main window 
hwnd = FindWindow(null, "Calculator"); 
if(hwnd == IntPtr.Zero) 
{ 
    if(MessageBox.Show("Couldn't find the calculator" + 
         " application. Do you want to start it?", 
         "TestWinAPI", 
         MessageBoxButtons.YesNo) == DialogResult.Yes) 
    { 
     System.Diagnostics.Process.Start("Calc"); 
    } 
} 
else 
{ 
    //Get a handle for the "1" button 
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "1"); 

    //send BN_CLICKED message 
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero); 

    //Get a handle for the "+" button 
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "+"); 

    //send BN_CLICKED message 
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero); 

    //Get a handle for the "2" button 
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "2"); 

    //send BN_CLICKED message 
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero); 

    //Get a handle for the "=" button 
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "="); 

    //send BN_CLICKED message 
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero); 
} 
+0

¿Conoces alguna forma de enumerar ventanas secundarias usando EnumChildWindows y verificar la devolución de llamadas si esta es la ventana que necesitamos capturar? Estoy usando WINAPI por primera vez y no sé cómo escribir código para usar EnumChildWindows. – teenup

+0

@Puneet: para hacer eso, tendrá que encontrar alguna forma de identificar de manera única los botones en la Calculadora. Como expliqué en mi respuesta, no sé de una buena manera de hacer esto. Todos los botones tienen un título de ventana (título) de '" "', o 'NULL'. Mediante programación, será difícil encontrar una forma de identificar botones individuales, dado solo sus identificadores. –

+0

En mi cuadro de Win7, todos los botones de Calculadora tienen subtítulos Unicode estándar de acuerdo con Spy ++ (Probé los 4 modos en el menú Ver). Además, para la compatibilidad de 32 bits, todos los identificadores de ventanas pueden caber en 32 bits, por lo que un 'HWND' debe ser transferible a un' int' y viceversa. – Gabe

1

pude repro esta en Win7 Pro. Su problema es probable que las etiquetas de los botones se dibujen a través del tema de la calculadora y no como un título. Cuando el servicio Temas se está ejecutando, al iniciar la Calculadora, tendrá botones sin leyenda.

el fin de obtener títulos de los botones adecuados, es necesario:

  1. Detener el servicio Temas (net stop themes ejecutar desde un símbolo del sistema elevado o utilizar la herramienta administrativa Servicios).
  2. Calculadora de inicio.

Si tiene Calculadora ejecutándose cuando detiene el servicio Temas, se dará cuenta de que todos sus botones quedan en blanco.

+0

Ah, entonces estabas usando el modo Clásico. Ciertamente estoy de acuerdo con esa elección. Desactivar temas parece ser la solución, pero no muy buena. Simplemente está trabajando alrededor de las suposiciones hechas en el código, en lugar de corregir esas suposiciones. Es difícil imaginar la utilidad de una aplicación que solo funcionará correctamente en una configuración no predeterminada de Windows. –

+0

@Cody: Viendo cómo es inútil automatizar una calculadora desde C#, tendría que asumir que esto es solo un ejercicio. ¡Es difícil imaginar que alguien realmente esté creando una aplicación que se base en la automatización de Calc! – Gabe

+0

@Cody: Eventualmente mi necesidad no es automatizar la Calculadora a través de C#, escribí en mi pregunta, que encontré una aplicación de muestra para entender cómo hacer el trabajo real. Finalmente, necesito implementarlo para otra aplicación de Windows que es un sistema de reserva de tickets y una herramienta de informes. – teenup

2

El siguiente código funciona bien en Caculator de Windows 7 en el tema clásico (no se trabaja en el tema básico o Aero):

================= ===========================

IntPtr hwndFrame = FindWindowEx(hwnd, IntPtr.Zero, "CalcFrame", null); 
IntPtr hwndDialog = FindWindowEx(hwndFrame, IntPtr.Zero, "#32770", null); 
IntPtr hwndDialog2 = FindWindowEx(hwndFrame, (IntPtr)hwndDialog, "#32770", null); 


IntPtr hwndThree = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "3"); 
SendMessage((int)hwndThree, BN_CLICKED, 0, IntPtr.Zero); 

IntPtr hwndPlus = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "+"); 
SendMessage((int)hwndPlus, BN_CLICKED, 0, IntPtr.Zero); 

IntPtr hwndOne = FindWindowEx((IntPtr)hwndDialog2, IntPtr.Zero, "Button", "1"); 
SendMessage((int)hwndOne, BN_CLICKED, 0, IntPtr.Zero); 

IntPtr hwndEqual = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "="); 
SendMessage((int)hwndEqual, BN_CLICKED, 0, IntPtr.Zero); 
Cuestiones relacionadas