Home :: Windows :: Artículos :: Interceptando los mensajes de windows
 
Interceptando-los-mensajes-de-windows

Interceptando los mensajes de windows

feb 20, 2010 en Artículos por alfredo

Todas las ventanas de windows responden a mensajes como se explica en Como funciona el sistema de ventanas en windows. Si deseas que tu ventana responda de forma distinta ante un mensaje necesitas interceptarlo y eso lo voy a enseñar en este articulo.

FacebookGoogle BookmarksGoogle GmailTwitterYahoo MailHotmailLinkedInShare

Al recibir un mensaje una ventana decide como procesarlo y la gran mayoria de ventanas responden de la misma forma ante los mismos mensajes. Por ejemplo ante un mensaje "cerrar" la ventana se cierra, pero si lo interceptamos podriamos hacer un programa que en lugar de cerrarse le muestre un mensaje al usuario o que simplemente no se cierre o que haga cualquier otra cosa.

Si no sabes que es un mensaje en windows te recomiendo que primero leas el artículo Como funciona el sistema de ventanas en windows

Entendiendo como se procesan los mensajes

Cada vez que una ventana recibe un mensaje este pasa por la funcion WndProc que detecta el tipo de mensaje y procede según corresponda.

Para poder interceptar mensajes es necesario que hagamos nuestra propia implementacion de esta función de tal forma que los mensajes ya no pasen por la función por defecto sino por la nuestra.

Tienes que tener en cuenta que son muchisimos los mensajes que puede recibir una ventana y no vamos a poder responder a todos. Si dejamos de responder ante algún mensaje nuestra ventana dejará de funcionar como lo esperado. Me explico, si no procesamos el mensaje "cerrar" nuestra ventana no se cerrará, si no procesamos el mensaje "dibujar" nuestra ventana no se mostrará y asi sucesivamente.

Para evitar procesar todos los mensajes lo que tenemos que hacer es interceptar los que queramos y todo el resto de mensajes se los pasamos a la función original WndProc.

Aqui coloco el código de mi función wndproc que debería ir dentro de la implementación de tu formulario:

Visual Studio

Code Gear

Visual C#

public ref class Form1 : public System::Windows::Forms::Form
{
	protected: virtual void WndProc(Message %m) override
	{
		switch (m.Msg)
		{
			case <mensaje 1>:
				/* Procesamos el mensaje 1 */
				break;

			case <mensaje 2>:
				/* Procesamos el mensaje 2 */
				break;

			default:
				// Invocamos a la funcion original
				Form::WndProc(m);
				break;
		}
	}

	...

	// El resto de funciones del formulario
// No te olvides de agregar la cabecera
// en el .h del formulario bajo protected o public
void __fastcall TForm1::WndProc(TMessage &m)
{
	switch (m.Msg)
	{
		case <mensaje 1>:
			/* Procesamos el mensaje 1 */
			break;

		case <mensaje 2>:
			/* Procesamos el mensaje 2 */
			break;

		default:
			// Invocamos a la funcion original
			TForm::WndProc(m);
			break;
	}
}
public partial class Form1 : Form
{
	protected override void WndProc(ref Message m)
	{
		switch (m.Msg)
		{
			case <mensaje 1>:
				/* Procesamos el mensaje 1 */
				break;

			case <mensaje 2>:
				/* Procesamos el mensaje 2 */
				break;

			default:
				// Invocamos a la funcion original
				base.WndProc(ref m);
				break;
		}
	}

	...

	// El resto de funciones del formulario

 

Ten en cuenta que los case del switch los usaré para detectar el tipo de mensaje y el default se ejecutará para todos los mensajes que no intercepte llamando a la función por defecto (de la clase padre).

Además ten en cuenta que donde dice <mensaje 1> y <mensaje 2> colocaremos los mensajes de windows que vamos a interceptar.

Un típico mensaje de windows es el de cerrar que está definido como la constante WM_QUIT equivalente al número en hexadecimal 0x0012. Si colocamos el valor hexadecimal funcionará siempre mientras que si colocamos WM_QUIT necesitamos que esta constante esté definida por lo que es necesario incluir primero la librería windows.h.

Diferencia entre WndProc y los eventos del formulario

Normalmente, lo que siempre programamos va dentro de los eventos del formulario como por ejemplo el evento click del formulario o el evento close.

Sin embargo el WndProc está antes de dichos evento. Para que esto quede más claro te explico cual es el flujo del mensaje desde que llega a la ventana hasta que se levanta el evento del formulario.

  1. El mensaje llega a la ventana.
  2. El mensaje entra a la función WndProc.
  3. En el WndProc se procesa el comportamiento por defecto de dicho mensaje.
  4. Después de ejecutado el WndProc se invoca al evento y por lo tanto se ejecuta nuestro código.

Como puedes ver, el WndProc se llama mucho antes que al evento, inclusive se llama antes de que pase cualquier cosa por lo que si lo podemos interrumpir podemos cambiar el comportamiento por defecto.

En general para mí hay dos ventajas de utilizar el WndProc en lugar de eventos para ciertos casos:

  1. Podemos modificar el comportamiento por defecto ante los mensajes.
  2. No existen eventos para todos los mensajes que recibe nuestra ventana. Por ejemplo no hay un evento que nos permita detectar cuando alguien le hace clic a la barra de título de nuestro programa.

Ejemplos de mensajes

A continuación te voy a mostrar una serie de ejemplos de como interceptar algunos mensajes para que entiendas la potencia que nos puede dar la funcion WndProc.

El mensaje mouse down

El mensaje mouse down viene en una gran variedad de versiones. En este caso te voy a enseñar solamente dos.

Hex Constante Descripción
0x0201 WM_LBUTTONDOWN Cuando se ha hecho clic en la parte cliente de la ventana. La parte cliente es toda la parte interna de la ventana.
0x00A1 WM_NCLBUTTONDOWN Cuando se ha hecho clic en la parte NO cliente de la ventana. La parte NO cliente incluye los bordes y la barra de título.

En este ejemplo simplemente voy a reducir la ventana cuando hagas clic en la parte cliente y cambiar el color del formulario cuando hagas clic en la parte no cliente.

Visual Studio

Code Gear

Visual C#

protected: virtual void WndProc(Message %m) override
{
	switch (m.Msg)
	{
		case 0x0201: // WM_LBUTTONDOWN
			Width -= 10;
			Height -= 10;
			break;

		case 0x00A1: // WM_NCLBUTTONDOWN
			BackColor = Color::FromArgb(rand()%255, rand()%255, rand()%255);
			break;

		default:
			// Invocamos a la funcion original
			Form::WndProc(m);
			break;
	}
}
void __fastcall TForm1::WndProc(TMessage &m)
{
	switch (m.Msg)
	{
		case 0x0201: // WM_LBUTTONDOWN
			Width -= 10;
			Height -= 10;
			break;

		case 0x00A1: // WM_NCLBUTTONDOWN
			Color = (TColor)RGB(rand()%255, rand()%255, rand()%255);
			break;

		default:
			// Invocamos a la funcion original
			TForm::WndProc(m);
			break;
	}
}
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case 0x0201: // WM_LBUTTONDOWN
		    Width -= 10;
		    Height -= 10;
		    break;

        case 0x00A1: // WM_NCLBUTTONDOWN
            Random r = new Random();
		    BackColor = Color.FromArgb(r.Next(255), r.Next(255), r.Next(255));
		    break;

	    default:
		    // Invocamos a la funcion original
		    base.WndProc(ref m);
		    break;
    }
}

 

Cuando ejecutes tu programa ahora puedes ver que cuando haces clic en la parte interna la ventana se reduce en 10 pixeles a cada lado y cuando haces clic en la barra de título o si quieres cambiar el tamaño o tratar de cerrar o maximizar o minimizar tu programa simplemente cambia de color. Para cerrar tu ventana presiona Ctrl + F4 o detén la ejecución desde tu IDE.

Que pasa si no queremos que nuestra aplicación deje de moverse, cambiar de tamaño o cerrarse, es decir, no quiero que deje de funcionar como antes simplemente quiero que ahora adicionalmente cambie de color.

La respuesta es muy simple, basta con invocar a la función original mandándole el mensaje. Mira la primera línea del case WM_NCLBUTTONDOWN en el siguiente código:

Visual Studio

Code Gear

Visual C#

protected: virtual void WndProc(Message %m) override
{
	switch (m.Msg)
	{
		case 0x0201: // WM_LBUTTONDOWN
			Width -= 10;
			Height -= 10;
			break;

		case 0x00A1: // WM_NCLBUTTONDOWN
			Form::WndProc(m);
			BackColor = Color::FromArgb(rand()%255, rand()%255, rand()%255);
			break;

		default:
			// Invocamos a la funcion original
			Form::WndProc(m);
			break;
	}
}
void __fastcall TForm1::WndProc(TMessage &m)
{
	switch (m.Msg)
	{
		case 0x0201: // WM_LBUTTONDOWN
			Width -= 10;
			Height -= 10;
			break;

		case 0x00A1: // WM_NCLBUTTONDOWN
        	TForm::WndProc(m);
			Color = (TColor)RGB(rand()%255, rand()%255, rand()%255);
			break;

		default:
			// Invocamos a la funcion original
			TForm::WndProc(m);
			break;
	}
}
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case 0x0201: // WM_LBUTTONDOWN
		    Width -= 10;
		    Height -= 10;
		    break;

        case 0x00A1: // WM_NCLBUTTONDOWN
            base.WndProc(ref m);
            Random r = new Random();
            BackColor = Color.FromArgb(r.Next(255), r.Next(255), r.Next(255));
		    break;

	    default:
		    // Invocamos a la funcion original
		    base.WndProc(ref m);
		    break;
    }
}

 

Ahora tu ventana funciona normalmente pero cambia de color cuando el clic termina. Si quieres que cambie de color antes de que el clic termine entonces mueve la línea que invoca a la función original justo antes del break para que la función se invoque después de que cambiaste el color.

El mensaje de comandos del sistema con parámetros

Cada vez que en una ventana se realiza una operación que tenga que ver con el menú de la ventana se invoca al mensaje WM_SYSCOMMAND (0x0112). Cuando me refiero al menú de la ventana me refiero a sus botones para maximizar, minimizar, cerrar, scroll horizontal y vertical, etc.

Cuando llega este mensaje necesitamos identificar con precisión que comando ha sido el invocado ya que hay varias alternativas. Para esto podemos consultar el WParam que llega con el mensaje.

En este ejemplo voy a deshabilitar las opciones Maximizar y Minimizar y voy a escribir en la barra de título el mensaje correspondiente para que el usuario sepa que hizo pero en realidad no va a pasar nada.

A continuación te describo los parámetros que voy a interceptar:

Hex Constante Descripción
0xF030 SC_MAXIMIZE Cuando se da clic sobre el ícono de maximizar.
0xF032 SC_MAXIMIZE2 Cuando se da doble clic sobre la barra de título para maximizar la ventana.
0xF020 SC_MINIMIZE Cuando se da clic sobre el ícono de minimizar.

Visual Studio

Code Gear

Visual C#

protected: virtual void WndProc(Message %m) override
{
	switch (m.Msg)
	{
		case 0x0112: // WM_SYSCOMMAND:
			// Interceptamos el maximizar SC_MAXIMIZE(0xF030) y SC_MAXIMIZE2(0xF032)
			if (m.WParam.ToInt32() == 0xF030 || m.WParam.ToInt32() == 0xF032)
			{
				this->Text = "Maximizado";
				return;
			}

			// Interceptamos el minimizar SC_MINIMIZE(0xF020)
			if (m.WParam.ToInt32() == 0xF020)
			{
				this->Text = "Minimizado";
				return;
			}

			// Si es cualquier otro tipo
			Form::WndProc(m);
			break;

		default:
			// Invocamos a la funcion original
			Form::WndProc(m);
			break;
	}
}
void __fastcall TForm1::WndProc(TMessage &m)
{
	switch (m.Msg)
	{
		case 0x0112: // WM_SYSCOMMAND:
			// Interceptamos el maximizar SC_MAXIMIZE(0xF030) y SC_MAXIMIZE2(0xF032)
			if (m.WParam == 0xF030 || m.WParam == 0xF032)
			{
				this->Caption = "Maximizado";
				return;
			}

			// Interceptamos el minimizar SC_MINIMIZE(0xF020)
			if (m.WParam == 0xF020)
			{
				this->Caption = "Minimizado";
				return;
			}

			// Si es cualquier otro tipo
			TForm::WndProc(m);
			break;

		default:
			// Invocamos a la funcion original
			TForm::WndProc(m);
			break;
	}
}
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
         case 0x0112: // WM_SYSCOMMAND:
			// Interceptamos el maximizar SC_MAXIMIZE(0xF030) y SC_MAXIMIZE2(0xF032)
			if (m.WParam.ToInt32() == 0xF030 || m.WParam.ToInt32() == 0xF032)
			{
				this.Text = "Maximizado";
				return;
			}

			// Interceptamos el minimizar SC_MINIMIZE(0xF020)
			if (m.WParam.ToInt32() == 0xF020)
			{
				this.Text = "Minimizado";
				return;
			}

			// Si es cualquier otro tipo
			base.WndProc(ref m);
			break;

	    default:
		    // Invocamos a la funcion original
		    base.WndProc(ref m);
		    break;
    }
}

 

Nota: Si quieres deshabilitar todos los comandos, no solamente el maximizar y minimizar, simplemente comenta la última línea de este case.

Nota: Si usas Visual Studio prueba presionando Win + M para ver como todas las ventanas se minimizan pero la tuya se queda solamente que ahora muestra el título como Minimizado.

El mensaje cambio en la fuente de poder

El mensaje WM_POWERBROADCAST (0x0218) se le manda a todas las ventanas cada vez que hay un cambio en la fuente de poder. Uno de estos casos es cuando en una laptop cambiamos de batería a corriente.

En este caso simplemente le voy a cambiar el color al formulario cada vez que este mensaje llegue a mi aplicación.

Visual Studio

Code Gear

Visual C#

protected: virtual void WndProc(Message %m) override
{
	switch (m.Msg)
	{
		case 0x0218: // WM_POWERBROADCAST
			BackColor = Color::FromArgb(rand()%255, rand()%255, rand()%255);
			Form::WndProc(m);
			break;

		default:
			// Invocamos a la funcion original
			Form::WndProc(m);
			break;
	}
}
void __fastcall TForm1::WndProc(TMessage &m)
{
	switch (m.Msg)
	{
		case 0x0218: // WM_POWERBROADCAST
			Color = (TColor)RGB(rand()%255, rand()%255, rand()%255);
        	TForm::WndProc(m);
			break;

		default:
			// Invocamos a la funcion original
			TForm::WndProc(m);
			break;
	}
}
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case 0x0218: // WM_POWERBROADCAST
            Random r = new Random();
            BackColor = Color.FromArgb(r.Next(255), r.Next(255), r.Next(255));
            base.WndProc(ref m);
			break;

	    default:
		    // Invocamos a la funcion original
		    base.WndProc(ref m);
		    break;
    }
}

 

Para probar esto simplemente deja corriendo tu programa y conecta y desconecta el cable de corriente de tu laptop.

Mensajes de usuario

Adicionalmente a los mensajes de usuario tu puedes crear tus propios mensajes para que tus programas respondan entre sí y mandárselos con SendMessage. Tus mensajes deberían comenzar a partir del número WM_USER (0x400). Por lo general tus propios mensajes debes definirlos desde este número hacia adelante para que no interfieran con los mensajes de windows.

Lista de mensajes

No he encontrado una lista de mensajes completa en Internet, ni siquiera en la página de Microsoft pero aquí te dejo con un par de links.

Lo que si hay en Microsoft, en la parte de documentación MSDN, es una descripción de todos los mensajes de windows, simplemente busca el mensaje en base a su constante por ejemplo WM_QUIT y obtendrás toda la información que necesitas.

Resumen

En este artículo solo he colocado tres ejemplos de mensajes para no hacerlo muy largo pero que te quede claro que hay muchos mensajes más y las posibilidades son limitadas simplemente por tu creatividad así que comienza a jugar y probar con los diferentes mensajes para que encuentres cosas increibles que no creías que podías hacer.


Autor: alfredo


Comentarios (1)

Yelinna dice:

Este truquillo con WndProc permite personalizar mucho el comportamiento de las ventanas.
Este artículo va directo a Mis Favoritos :D

Deja un comentario

   

copstone en Facebook

Otros artículos

Cuando trabajamos con datos numéricos, cadenas, fechas, no necesariamente debemos de mostrarla como están almacenadas en memoria, podemos modificar su presentación. Conoce que alternativa tiene .Net para modificar el formato de presentación de la información.

FacebookGoogle BookmarksGoogle GmailTwitterYahoo MailHotmailLinkedInShare

Ahora veremos como vincular una aplicación de entorno Consola con el .Net Framework, de forma tal que podamos utilizar todo el potencial de esta plataforma.

FacebookGoogle BookmarksGoogle GmailTwitterYahoo MailHotmailLinkedInShare

Si en el post anterior vimos como conectarnos a un Servidor SQL, es momento de aprender como podemos recorrer la información obtenida mediante un DataSet. En este post he tratado de hacer una definición resumida de lo que es un DataSet pero principalmente he colocado el código necesario para que puedas ir aprendiendo más acerca de esta clase importante de ADO.NET

FacebookGoogle BookmarksGoogle GmailTwitterYahoo MailHotmailLinkedInShare

Si sigues colocando todo el código de tu aplicación dentro del formulario y no sabes que tipo de proyecto usar, en Visual Studio, para separar la capa de presentación de la lógica de nuestra aplicación? Entonces este artículo es para tí.

FacebookGoogle BookmarksGoogle GmailTwitterYahoo MailHotmailLinkedInShare

Cuando hablamos de Windows de seguro recuerdas en tu mente una bandera con los colores rojo, verde, azul y amarillo o quizás el sonido clásico de este Sistema Operativo cuando iniciamos una sesión. Ya pasaron varios años y sobre todos varias versiones (7 para ser exactos), desde la primera vez que apareció el primer Sistema Operativo Windows. En esta ocasión Microsoft ha lanzado la nueva versión de su clásico producto y sin temor a equivocarme es largamente superior a su versión anterior.

FacebookGoogle BookmarksGoogle GmailTwitterYahoo MailHotmailLinkedInShare

Calendario

febrero 2010
L M X J V S D
« ene   mar »
1234567
891011121314
15161718192021
22232425262728

Categorías

Comparte este artículo

  • Facebook
  • Google Bookmarks
  • Google Gmail
  • Twitter
  • Yahoo Mail
  • Hotmail
  • LinkedIn
  • Share
TIENES ALGO QUE PREGUNTAR? ESCRÍBENOS AQUÍ

Copyright © 2012 - Programando por diversion

Subir