Windows en C++

| 2013-04-27 | No hay comentarios »

LA API DE WINDOWS

API (Aplication Programs Interface): es el equivalente a las interrupciones del MSDOS, son un conjunto de funciones que permiten dibujar ventanas, dibujar un botón. A

su vez la API llama a las interrupciones del DOS.

La API de Windows son mas de 600 funciones en C normal. Para acceder a ella los compiladores utilizan librerías de objetos (el Borland C++ usa ObjectWindows), permitiendo acceder a la API de manera mas fácil y reducida (son cien y pico clases).

Los programas hasta ahora en DOS, cuando un programa necesitaba hacer algo, llamaba a una interrupción, servicio de BIOS o de DOS, nosotros llamábamos al sistema operativo para hacer operaciones. En Windows esto cambia y es Windows quien llama al programa (mensajes), cuando movemos al ratón en Windows, Windows avisa al programa del cambio. El programa queda en memoria en espera de que Windows le comunique mensajes.

Evento: se produce el hecho de mover el ratón.

Mensaje: información que nos informa del evento.

 

LA CLASE TApplication

Para crear una aplicación, lo primero que debemos hacer es crear un objeto aplicación, siendo el constructor de la clase TApplication de la forma:

 

TApplication(const char far *Nombre=0);

 

siendo Nombre el titulo que aparece en la barra de la ventana. Por ejemplo:

 

TApplication prueba(“EJEMPLO”);

 

crea un objeto del tipo TApplication con el titulo EJEMPLO. Pero aparte de definir un objeto del tipo de aplicación, hay que poner a funcionar el paso y recepción de los mensajes (medio de comunicación entre Windows y nuestra aplicación). La función que realiza esto es la función Run

 

FUNCIÓN RUN

 

La función Run hace lo siguiente:

-Comprueba si ya se esta ejecutando una instancia del programa (si está ejecutándose varias veces), para ello consulta la variable hRevInstance, que es un dato miembro de la clase TApplication(=0 ninguna instancia, n hay n copias del programa ejecutándose).

-Si no hay ninguna instancia, RUN llama a una función que se llama InitApplication() (función miembro de la clase aplication). Esta función es virtual pura y no hace nada, a menos que queramos redefinirla para hacer algo.

– Después de InitApplication, llama a la función InitInstance(), que a su vez:

•llama a la función InitMainWindow(). Esta función es miembro de la clase y es la que construye un objeto ventana. La ventana que crea es normal, sin nada, no pone ni titulo, ni nada. Si queremos una ventana con menús, botones, etc., entonces tenemos que redefinir la función InitMainWindow.

•hace que la ventana se vea en pantalla.

 

Veamos esto con un ejemplo:

 

#include <owl\applicat.h>

// Como usamos objectvision, la main se llamara OwlMain (si programas con

// la API directamente se llamara WinMain

int OwlMain(int, char *[])

{

            TApplication a(“HOLA”); // Clase incluida y definida crea un objeto de tipo aplicación

            // bucle mensajes, función ventana, etc. …

            a.Run(); // Función miembro de la clase TApplication, empieza la ejecución

            // (inicializa el proceso de paso de mensajes entre Windows y la

            // aplicación)

            return a.Status; //Dato miembro de la clase TApplication (de una clase base

            // TAplication. Contiene un dato de 16 bits que dice si se

            // se ha creado bien la aplicación}

}

 

El resultado de este programa seria mas o menos :

o3

 

A continuación, veremos los principales campos de la clase TApplication:

 

CAMPO hPrevInstance

En esta variable guardamos el numero de instancias de un programa que actualmente estén ejecutándose. Bien, pero ¿que es una instancia?, una instancia es una copia del programa. Es decir, al ejecutar por primera vez un programa, el campo hPrevInstance valdrá 0, si es la segunda vez que ejecutemos el programa hPrevInstance valdrá 1, etc.

Para poder tener varias instancias en memoria en Windows es necesario que solo se utilice un solo segmento de datos (como ocurre en el modelo small de memoria). El modelo de memoria de Windows es large (para evitar la limitación del DOS de los 640k), este modelo usa solo un segmento de datos. En el momento que especificamos una variable de tipo FAR, le estamos diciendo es que creamos un nuevo segmento de datos, y entonces no podemos tener varias instancias en Windows.

Veamos un ejemplo:

 

#include <owl\applicat.h>

int OwlMain(int, char *[])

{

            TApplication a; // objeto del tipo TApplication

            if (!a.hPrevInstance) // Si todavía no se ha ejecutado…

            a.Run(); // … lo hacemos

            else // Si actualmente está ejecutándose…

            ::MessageBox(NULL,”Una sola instancia”,”Error”,MB_OK); //…mostramos una

// ventana con el mensaje de error

            return a.Status;

}

 

El resultado al ejecutar por primera vez este programa, es la figura 1, pero si en la segunda vez o posteriores será la figura 2

o4

 

FUNCIÓN MessageBox

Para crear una caja de diálogos(MsgBox), seria:

 

::MessageBox(NULL, “mensaje”, “titulo”, botones);

 

Cuando queremos llamar a una función de la API anteponemos ::, sino llama a funciones de ObjectVision, no a la de la API.

 

CAMPO nCmdShow

Este campo nCmdShow (int) sirve para especificar el aspecto de la ventana (maximizada, minimizada, en forma de icono). Si ponemos:

 

nCmdShow=SW_SHOWMAXIMIZED; (la ventana que se cree estará maximizada)

 

estamos diciendo que la ventana estará maximizada. Los posibles valores que puede tomar son:

 

SW_HIDE: esconder la ventana

SW_SHOW: volver a hacerla visible

SW_SHOWMAXIMIZED: ventana maximizada

SW_SHOWMINIMIZED: ventana minimizada

 

Ahora pasemos a ver las funciones miembro de la clase TApplication:

 

FUNCIÓN InitMainWindow

La función miembro virtual void InitMainWindow() es protegida (solo se puede llamar desde un objeto de TAplication o una derivada) no se puede llamar, y crea la ventana marco (es el tipo de marco de la ventana). Define todas las características interiores y exteriores de la ventana. Además de crear la ventana marco crea todos los controles asociados a la ventana marco. Donde se va a dibujar los datos y mostrar mensajes es la ventana cliente o ventana aplicación. Hay que redefinir la función si queremos que por ejemplo no aparezca el botón de minimizar, etc.

 

FUNCIÓN CanClose

virtual BOOL CanClose() devuelve un tipo de datos booleano(TRUE o FALSE). Esta función se ejecuta automáticamente cuando cerramos la ventana (Alt+F4, etc.). Si cuando salgamos queremos que muestre un mensaje de despedida, redefinimos esta función. Para que Windows cierre la aplicación CanClose debe devolver TRUE, si en la función devolvemos FALSE, Windows no cierra la aplicación.

 

FUNCIÓN IdleAction

BOOL IdleAction (long contador) se ejecuta automáticamente cuando la aplicación no esta haciendo nada. Hay que redefinir esta función para hacer algo en los tiempos muertos. Contador indica el numero de veces que se ha ejecutado esta función desde el ultimo cambio (mover ratón).

Veamos un ejemplo de esta función:

 

#include <owl\applicat.h>

class p: public TApplication{

            public:

            p():TApplication(“Segundo Plano”){};

            protected:

            BOOL IdleAction(long);

            };

 

BOOL p::IdleAction(long p)

{

            ::MessageBox(NULL,”Segundo Plano”,”Estoy en segundo Plano”,MB_OK);

            return TRUE;

}

 

int OwlMain(int, char*[])

{

            p a;

            a.Run();

            return a.Status;

}

 

 

Este programa lo que hace es redefinir la función IdleAction para que cuando el programa no este ejecutando nada, muestre una ventana como esta:

o5

esto puede ser útil para ejecutar cosas en segundo plano o, como en el Word, aprovechar el momento en que no se esta haciendo nada para avisar que se debe grabar el documento.

 

LA CLASE TWindow

Con esta clase definimos objetos del tipo ventana. Si creamos un objeto derivado de esta clase, podemos también redefinir una tabla de eventos propios. El constructor de la clase es el siguiente:

 

TWindow(TWindow *Madre, char far titulo=0, TModule *modulo=0);

Madre: es un puntero a la ventana madre, que será NULL para la ventana marco de la aplicación.

titulo: puntero al titulo de la ventana

modulo: información que debemos proporcionar si la ventana se crea a partir de una DLL

 

Uno de los principales campos de la clase TWindow es:

 

CAMPO Attr

Contiene las características de la ventana. A su vez, Attr se divide en una serie de campos:

 

•Style: características generales de la ventana, pudiendo ser uno de los siguientes valores:

 

WS_BORDER: ventana de borde delgado, impide modificar el tamaño de la ventana.

WS_CAPTION: indica la presencia de la barra de titulo.

WS_HSCROLL: barra de desplazamiento horizontal

WS_MAXIMIZEBOX: casilla de maximizar

WS_MINIMIZE: la ventana será lo mas pequeña posible

WS_OVERLAPPED: la ventana podrá cubrir otras y a su vez ser cubierta

WS_VSCROLL: barra de desplazamiento vertical

WS_THICKFRAME: borde grueso

 

Para poder activar una característica seria de la forma:

 

Attr.Style=WS_OVERLAPPED|WS_CAPTION|WS_BORDER;

Attr.Style|=WS_VSCROLL

 

y para desactivar alguna de las características:

 

Attr.Style &=~WS_MAXIMIZEBOX;

Attr.Style &=~(WS_MAXIMIZEDBOX|WS_MINIMIZEDBOX);

 

Ahora pasemos a ver las funciones miembro de la clase TWindow:

 

FUNCIÓN Show

La función void Show (int ShowCmd) modifica la apariencia de una ventana. El argumento que le pasamos puede valer:

 

SW_HIDE: esconde la ventana

SW_SHOW: vuelve a hacer visible la ventana

SW_SHOWMAXIMIZED: amplia la ventana al máximo

SW_SHOWMINIMIZED: minimiza la ventana a un icono

 

FUNCIÓN SetCaption

La función void SetCaption(const char far *titulo) modifica el titulo de la ventana, poniendo como titulo la cadena apuntada por titulo.

 

FUNCIÓN SetBkgndColor

La función void SetBkgndColor(DWORD color) cambia el color de fondo de la ventana. Los colores disponibles son:

 

TColor::Black negro TColor::LtBlue azul claro

TColor::LtGray gris claro TColor::LtMagenta magenta claro

 

 

TColor::Gray gris oscuro TColor::LtCyan cian claro

TColor::LtRed rojo claro TColor::White blanco

TColor::LtGreen verde claro

 

LA CLASE TFrameWindow

Aquí definimos la ventana marco de la aplicación, que posee el borde exterior y la ventana aplicación. El constructor de la clase es:

 

TFrameWindow(TWindow *madre, const char far titulo=0, TWindow *clientWnd=0)

 

en donde

madre: es el puntero a la ventana madre

titulo: puntero al titulo de la ventana.

clientWnd: es el puntero a la ventana de la aplicación

 

para crear la ventana marco y la ventana aplicación se crean (en InitMainWindow):

 

MainWindow=new TFrameWindow(NULL, “Creación de ventana”, new TWindow)

#include <owl\applicat.h>

#include <owl\framewin.h>

#include <owl\dc.h>

class b: public TApplication{

            public:

            void InitMainWindow();

            b():TApplication(){};

};

 

class TWinApp: public TWindow{

            public:

            TWinApp():TWindow(0,0,0)

            {

            SetBkgndColor(TColor::LtBlue);

            }

};

 

void b::InitMainWindow()

{

            MainWindow=new TFrameWindow(NULL, “Ventana ejemplo de SetBkgndColor”, new

            TWinApp);

 

 

            /* Si queremos modificar la ventana añadimos: */

            MainWindow->Attr.X=320; // Coordenada X=320

            MainWindow->Attr.Y=240; // Coordenada Y=240

            MainWindow->Attr.W=100; // Ancho 100 pixels

            MainW indow->Attr.H=100; // Alto 100 pixels

            MainWindow->Attr.Style=WS_OVERLAPPEDWINDOW|

            WS_HSCROLL|

            WS_VSCROLL;

}

 

int OwlMain(int, char*[])

{

            b prueba;

            prueba.Run();

            return prueba.Status;

}

 

Con lo que el resultado que daría, seria mas o menos:

o6

 

TABLAS DE EVENTOS

Para tratar un evento es necesario escribir una función (o de paso redefinimos una que ya existe) y tenemos que crear una tabla de eventos a tratar que esté asociada a la clase. La definición de la tabla de eventos en la clase es de la forma:

 

DECLARE_RESPONSE_TABLE(nombre_clase)

 

en donde:

 

nombre_clase: es el nombre de la clase y definiendo la tabla de eventos de la forma:

 

DEFINE_TABLE_RESPONSEn(nombre_clase)

Aquí van los eventos separados por comas

END_RESPONSE_TABLE;

n: numero de clases bases de donde deriva

Veamos un ejemplo:

class prueba: public TWindow

{

            …

            DECLARE_RESPONSE_TABLE(prueba);

            …

};

 

DEFINE_RESPONSE1(prueba) // Como prueba solo deriva de la clase TWindow,

… // entonces ponemos 1

END_RESPONSE_TABLE;

 

FUNCIÓN GetSystemMetrics

Esta función GetSystemMetrics(nIndex) devuelve distintas informaciones según el valor de nIndex. Los principales valores de Nindex son:

 

SM_CXSCREEN: anchura de la pantalla

SM_CYSCREEN: altura de la pantalla

 

Veamos un ejemplo de todo esto:

 

#include <owl\applicat.h>

#include <owl\framewin.h>

#include <owl\dc.h>

class b: public TApplication{

            public:

            void InitMainWindow();

            b():TApplication(){};

};

 

class TWinApp: public TWindow{

            public:

            TWinApp():TWindow(0,0,0)

            {

                        SetBkgndColor(TColor::LtBlue);

            }

};

 

class Marco: public TFrameWindow{

            public:

            void EvGetMinMaxInfo(MINMAXINFO far &);

            void EvSize(UINT , TSize &);

            DECLARE_RESPONSE_TABLE(Marco);

            Marco(TWindow *p, char far *t, TWindow *cliente): TFrameWindow

            (p,t,cliente){};

};

 

void Marco::EvSize(UINT marca, TSize &a)

{

            TFrameWindow::EvSize(marca,a); //Lamamos para que redibuje la ventana

            switch(marca)

            {

                        case SIZE_MAXIMIZED: ::MessageBox(NULL,”Ventana Maximizada”,”Aviso”,

                        MB_OK);

                        break;

                        case SIZE_MINIMIZED: ::MessageBox(NULL,”Ventana minimizada!”,”Aviso”,

                        MB_OK);

                        break;

                        case SIZE_RESTORED: ::MessageBox(NULL,”Ventana con tamaño original”,

                        “Aviso”, MB_OK);

                        break;

            }

}

 

void Marco::EvGetMinMaxInfo(MINMAXINFO far &a)

{

            a.ptMaxSize.x=300;

            a.ptMaxSize.y=200;

            a.ptMaxPosition.x=100;

            a.ptMaxPosition.y=100;

            a.ptMaxTrackSize.x=GetSystemMetrics(SM_CXSCREEN);

            a.ptMaxTrackSize.y=GetSystemMetrics(SM_CYSCREEN);

}

 

DEFINE_RESPONSE_TABLE1(Marco,TFrameWindow)

EV_WM_GETMINMAXINFO,

EV_WM_SIZE,

END_RESPONSE_TABLE;

 

void b::InitMainWindow()

{

            //TWindow *VentanaAplicacion;

            //VentanaAplicacion=new TWindow(0,0,0);

            MainWindow=new Marco(NULL, “Ventana marco a medida”, new TWinApp);

            /* Si queremos modificar la ventana añadimos: */

            MainWindow->Attr.X=GetSystemMetrics(SM_CXSCREEN)/4;

            // Coordenada X=320

            MainW indow->Attr.Y=GetSystemMetrics(SM_CYSCREEN)/4;

            // Coordenada Y=240

            MainWindow->Attr.W=200; // Ancho 100 pixels

            MainWindow->Attr.H=100; // Alto 100 pixels

            MainWindow->Attr.Style=WS_OVERLAPPEDWINDOW|

            WS_HSCROLL|

            WS_VSCROLL;

}

 

int OwlMain(int, char*[])

{

            b prueba;

            prueba.Run();

            return prueba.Status;

}

 

El resultado del programa es:

figura 1: al iniciar el programa

figura 2: al maximizar la ventana

figura 3: al minimizar la ventana

o7

 

EVENTOS DEL RATÓN

Son los eventos que manda Windows a nuestro programa cuando hacemos clic con el ratón, lo movemos, etc. Los valores posibles de dichos mensajes son:

 

WM_LBUTTONDOWN : botón izquierdo pulsado

WM_LBUTTONUP : botón izquierdo soltado

WM_MBUTTONDOWN : botón central pulsado

WM_MBUTTONUP : botón central soltado

WM_RBUTTONDOWN : botón derecho pulsado

WM_RBUTTONUP : botón derecho soltado

WM_MOUSEMOVE : cuando se mueve el ratón

 

La forma de saber como se escribe el evento y la función de tratamiento, es fácil sabiendo como se escribe el mensaje. El mensaje es igual que el evento, pero añadiendo EV_ al principio. La función de tratamiento es poner en minúsculas todo el nombre, menos el inicio de las palabras y eliminando los subrayados. Veamos unos ejemplos:

o8

Hay que indicar que el botón derecho y el izquierdo se refieren a los de Windows, y que es posible invertirlo, por lo que puede haber veces que el botón derecho del ratón de Windows sea el botón físico izquierdo del ratón.

Lógicamente si queremos hacer un programa que responda a los eventos del ratón, debemos definir la tabla de eventos. Como siempre, después de la explicación clara y concisa, vemos un ejemplo para enterarnos de algo:

 

#include<owl\applicat.h>

#include<owl\framewin.h>

class TWinApp: public TWindow

{

            void EvRButtonDown(UINT, TPoint &);

            void EvMButtonDown(UINT, TPoint &);

            void EvLButtonDown(UINT, TPoint &);

            public:

            TWinApp():TWindow(0,0,0){}

            DECLARE_RESPONSE_TABLE(TWinApp);

};

 

 

DEFINE_RESPONSE_TABLE1(TWinApp,TWindow)

EV_WM_RBUTTONDOWN,

EV_WM_LBUTTONDOWN,

EV_WM_MBUTTONDOWN,

END_RESPONSE_TABLE;

class TApp: public TApplication

{

            void InitMainWindow()

            {

                        MainWindow=new TFrameWindow(NULL,”Manejador de eventos del raton”, new

                        TWinApp);

            }

            public:

            TApp(): TApplication() {}

            };

            void TWinApp::EvRButtonDown(UINT a, TPoint &b)

                {

                               ::MessageBox(NULL,”Botón derecho pulsado”,”Aviso”,MB_OK);

                }

 

void TWinApp::EvMButtonDown(UINT a, TPoint &b)

{

                ::MessageBox(NULL,”Botón central pulsado”,”Aviso”,MB_OK);

}

 

void TWinApp::EvLButtonDown(UINT a, TPoint &b)

{

                ::MessageBox(NULL,”Botón izquierda pulsado”,”Aviso”,MB_OK);

}

 

int OwlMain(int, char * [])

{

                TApp b;

                b.Run();

                return b.Status;

}

 

El resultado del programa es el siguiente: cuando pulsamos la tecla izquierda, muestra la figura 1, si es la tecla central muestra la figura 2 y por ultimo si es la derecha muestra la figura 3.

o9

 

o10

 

FUNCIÓN SetCursorPos

Esta función SetCursorPos(x, y) pone el puntero del ratón en el punto de coordenadas (x, y) relativas a la pantalla. Si queremos pasar de las coordenadas de pantalla a las coordenadas de la ventana, usamos dos funciones:

 

ScreenToClient y ClientToScreen

 

FUNCIÓN ScreenToClient

La función ScreenToClient(TPoint &) pasa las coordenadas de pantalla a coordenadas de la ventana. Tenemos que pasar a esta función un objeto de tipo TPoint, que tiene dos campos x e y. Al pasar ese objeto por referencia, modificará las coordenadas del objeto

FUNCIÓN ClientToScreen

Esta función cuyo prototipo es ClientToScreen(TPoint &) pasa las coordenadas de la ventana a coordenadas de la pantalla. Ocurre exactamente que con la función anterior, guarda las nuevas coordenadas en el objeto del tipo TPoint.

 

Un ejemplo de estas dos funciones:

 

TPoint p; // Declaramos p como un objeto TPoint (de dos campos x e y)

p.x=10; p.y=20; // Coordenadas de la ventana …

ClientToScreen(p); // … las convertimos a coordenadas de la pantalla y …

SetCursorPos(p.x, p.y); // … ponemos el cursor del ratón en las nuevas

// coordenadas

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Developers SO Sistemas Operativos preferidos por los developers
  • Mejor antivirus para windows



SEGUÍNOS EN FACEBOOK


GITHUB