Enviar mensajes y acceder a una ventana de otra aplicación.
Por: Demian Panello.
Enviar mensajes:
Escribir una aplicación en VC++, (en principio usando MFC), radica básicamente en crear los manejadores de mensajes, (o eventos), que necesitemos, como por ejemplo: WM_LBUTTONDOWN para un botón, WM_DRAW para dibujar un diálogo, y cualquier otro mensaje que reciba la aplicación.
Pero además de esto también puede uno enviar algún mensaje, lo que significa que se fuerza la ejecución del código escrito en ese mensaje a pesar de que el mismo no ha sido recibido por alguna acción del usuario. Por ejemplo, se puede enviar el mensaje WM_LBUTTONDOWN de un diálogo desde un botón.
Esto se hace a través de la función SendMessage(), que es una función miembro de CWnd.
Supongamos que tenemos un diálogo cuya clase es CEnviarmensajesDlg, y que en el mensaje WM_LBUTTONDOWN del mismo hay lo siguiente:
|
void
CEnviarmensajesDlg::OnLButtonDown(UINT nFlags, CPoint point) |
Obviamente al pulsar con el mouse en el diálogo se mostrará el mensaje pasado por parámetro a la función AfxMessageBox().
Si además tenemos un botón y queremos que al pulsarlo se ejecute el mensaje WM_LBUTTONDOWN del diálogo deberemos enviar este mensaje a Windows, (que es lo que en realidad ocurre si pulsamos con el mouse sobre el diálogo), deberíamos escribir:
| void
CEnviarmensajesDlg::OnClickdiag() { //Envió el mensaje WM_LBUTTONDOWN //también podríamos haber escrito: this->SendMessage (WM_LBUTTONDOWN); SendMessage(WM_LBUTTONDOWN); AfxMessageBox("Se envió el mensaje WM_LBUTTONDOWN al pulsar el botón"); } |
Simplemente se llama a la función SendMessage del diálogo, ya que el mensaje a enviar pertenece al él, y listo.
Una última cosa: a SendMessage() se le puede pasar dos parámetros más, wParam y lParam, que varían de acuerdo al mensaje. Para WM_LBUTTONDOWN wParam indica si se tiene pulsada alguna tecla y lParam el punto en el diálogo donde se pulsó el mouse.
Sugiero que experimenten bastante con esta función, (no puede pasar nada grave, salvo que se "cuelgue" la aplicación), y busquen información en los CD's de MSDN o en la red.
Acceder a una ventana de otra aplicación:
Una vez me preguntaba si se podría desde mi aplicación acceder a una ventana de otra aplicación que en ese momento esté en ejecución para, por ejemplo, cambiar el título de esa otra ventana. Mi pregunta tuvo respuesta luego de leer algunos artículos de MSDN y para mi sorpresa era mucho más fácil de lo que pensaba.
Hallé la función FindWindow de CWnd, la cual pide dos parámetros: el primero una cadena expresando el nombre de la clase de ventana a buscar y el segundo el título de la ventana, (se puede poner NULL en uno de ellos y especificar el otro o poner los dos). Y la función devuelve un puntero a CWnd que puede llegar a ser NULL si no se encuentra esa ventana en memoria.
Supongamos que en el diálogo del ejemplo anterior tenemos otro botón, y queremos que al pulsarlo, si hay una ventana del explorador de Windows en ejecución, se cambie el título de la misma y el icono, (hemos creado un icono con el ID: IDI_ICON1).
Entonces escribimos en el botón, (cuyo ID es IDC_CAMBIAR):
| void
CEnviarmensajesDlg::OnCambiar() { HICON miIcono; //Handle para un icono miIcono=AfxGetApp()->LoadIcon (IDI_ICON1); //cargo el icono //Obtengo un handle para una ventana del explorador que esté abierta CWnd* pExplo = CWnd::FindWindow("ExploreWClass", NULL); (1) if (pExplo!=NULL) //si pudo encontrar alguna ventana del explorador de windows { //le cambio el título. pExplo->SetWindowText ("Icono y titulo cambiado desde una aplicación"); //le cambio el icono pExplo->SetIcon(miIcono,TRUE); } else AfxMessageBox("No se encuentra ninguna ventana del Explorador de Windows"); } |
Bueno, las primeras líneas simplemente declaran un manejador para el icono y se carga el icono. En (1) está la clave de todo, se hace una llamada a FindWindow() pasándole como parámetro el nombre de clase para el explorador de Windows sin importar el título.
Esta llamada devuelve un puntero a CWnd que si es distinto a NULL es porque se encontró la ventana y entonces tenemos la llave de la misma y podremos hacer cualquier cosa con ella.
Quizás se pregunte cómo "diablos" supe el nombre de clase del explorador de Windows, muy simple, usé el utilitario SPY++ que viene con Visual Studio.
Este programa muestra entre muchas otras cosas, toda la información de las ventanas que se encuentran en ejecución.
En el menú Find de Spy++, seleccione Find Window y luego tilde la opción Hide Spy. Esta es la ventana FIND WINDOW:

Si arrastra el icono que tiene el dibujo de una mira, (Finder Tool), hasta alguna ventana en ejecución verá el nombre de la clase en el cuadro de edición CLASS, (además de otra info interesante). De aquí obtuve el nombre de la clase.
A continuación listo las clases de ventana de algunas aplicaciones:
Class Name
Application
SciCalc
CALC.EXE
CalWndMain
CALENDAR.EXE
Cardfile
CARDFILE.EXE
Clipboard
CLIPBOARD.EXE
Clock
CLOCK.EXE
CtlPanelClass
CONTROL.EXE
XLMain
EXCEL.EXE
Session
MS-DOS.EXE
Notepad
NOTEPAD.EXE
pbParent
PBRUSH.EXE
Pif
PIFEDIT.EXE
PrintManager
PRINTMAN.EXE
Progman
PROGMAN.EXE (Windows Program Manager)
Recorder
RECORDER.EXE
Solitaire
SOL.EXE
Terminal
TERMINAL.EXE
WFS_Frame
WINFILE.EXE
MW_WINHELP WINHELP.EXE
#32770
WINVER.EXE
OpusApp
WINWORD.EXE
MSWRITE_MENU WRITE.EXE
Windows 95 Only:
MSPaintApp
PBRUSH.EXE
ExploreWClass
EXPLORER.EXE
WordPadClass WORDPAD.EXE
(información extraída del articulo Knowledge Base Q88167).
Así quedaría la ventana del explorador de Windows luego de pulsar el botón de nuestra aplicación, (observe la barra de título):

Para finalizar, hago notar, a modo de relación entre los dos ejemplos de este artículo, que teniendo un puntero a CWnd de cualquier ventana, se podría enviar algún mensaje de la misma por medio de SendMessage().
Descargar fuente de los ejemplos: enviarmensajes.zip (28 Kb).