FUNDAMENTOS DE MFC.  

 

Por: Demian Panello.

 

 

MFC, (Microsoft Foundation Classes) es una librería de clases que provee un enfoque orientado a objetos sobre la API de Windows. Se dice que MFC "envuelve" gran parte de la funcionalidad de las API de Windows. Existen cientos de clases MFC, algunas de ellas uno usa directamente otras sirven como clase base para nuestras propias clases.

 

En realidad MFC es un "framework", lo que significa que es más que una simple colección de objetos, sino que permite definir una estructura de aplicación y manejar el comportamiento fundamental de la misma.

Inicialmente se tiene el objeto CWinApp que representa la aplicación misma, MFC encapsula todos los aspectos de una aplicación. 

Primero el framework de MFC brinda la función de entrada WinMain que requiere un programa Windows y luego esa función WinMain invoca funciones miembro de CWinApp que hace que el programa funcione. Una de las funciones que WinMain invoca es Run, que inicia el bucle de mensajes que se distribuyen a la ventana de la aplicación.

Otro punto importante del framework MFC radica en el nivel de abstracción presentado, que va incluso más allá que las API de Windows mismas. Por ejemplo la arquitectura Documento/Vista que MFC introduce y nos permite crear aplicaciones poderosas que separa de manera simple la información básica de la aplicación y sus posibles representaciones gráficas. Esta abstracción está totalmente más allá del alcance de la API de Windows. Lo mismo pasa con la creación de controles ActiveX y muchas otras características que MFC ofrece.

 

El framework de MFC está basado en una jerarquía de clases, (Jerarquía MFC.), diseñada para brindar soporte en un variado rango de problemáticas presentadas a la hora de escribir un programa Windows.

 

CObject.

La mayoría de las clases están derivadas directa o indirectamente de CObject. CObject le provee tres importantes características a toda clase derivada de él.

 

- Serialización.

- Información de la clase en tiempo de ejecución. (RTCI)

- Información de diagnóstico y depuración.

 

Serialización es el proceso por el cuál se puede guardar o cargar un objeto desde un archivo en disco. Usando la clase CObject como clase base, se pueden escribir clases cuyas instancias pueden ser guardadas o cargadas desde un archivo de manera muy simple. Tenga presente que el tipo de información representada por un objeto en particular puede ser de un alto grado de complejidad y la serialización facilita su escritura y lectura en disco.

RTCI permite obtener el nombre de la clase en tiempo de ejecución asi como otra información de la clase. Y la información de diagnóstico y depuración permite validar las instancias de la clase y depurar la información.

 

Funciones globales de MFC, (funciones AFX):

 

No todas las funciones de MFC son miembro de alguna clase, sino que hay un grupo de funciones cuyo nombre comienzan con Afx y estas funciones están disponibles en cualquier momento en cualquier lugar; no están supeditadas a un objeto en particular, por eso se dice que son "globales".

Hay decenas de funciones Afx, pero las más comunes son:

 

Nombre de la función Descripción
AfxAbort Termina una aplicación abruptamente. Generalmente se debe invocar esta función cuando ocurre un error grave.
AfxBeginThread Crea un nuevo proceso y lo inicia.
AfxEndThread Termina un proceso.
AfxMessageBox Muestra el cuadro de diálogo de Windows.
AfxGetApp Devuelve un puntero al objeto CWinApp de la aplicación.
AfxGetAppName Devuelve el nombre de la aplicación.
AfxGetMainWnd Devuelve un puntero a la ventana de la aplicación.
AfxGetInstanceHandle Devuelve un handle identificando la instancia actual de la aplicación.

 

También existen decenas de macros en MFC que son muy útiles. Para ver un detalle completo y organizado por grupos siga el siguiente link a MSDN: MFC Macros and Globals.  

 

Una aplicación MFC desde 0. 

 

Cuando se selecciona un proyecto MFC con el Application Wizard este genera abundante código. En éste código uno puede encontrar la clase derivada de CWinApp y una o más clases derivadas de CWnd o CDialog o CFrameWnd.

El programa MFC más básico que uno puede escribir consta de una clase derivada de CWinApp y una instancia de esa clase.

La clase derivada hereda muchos datos y funciones miembro de CWinApp, algunos de esos métodos son incluso virtuales lo que permite que uno los sobreescriba para personalizar su funcionamiento.

 

CWinApp. 

Datos (todos los datos miembros de las clases MFC comienzan con m_)

 

Descripción
m_pszAppName Nombre de la aplicación.
m_lpCmdLine Línea de comando pasada a la aplicación.
m_hInstance Handle de la aplicación.
m_hPrevInstance Handle a una instancia previa de la aplicación.
m_pMainWnd Handle de la ventana principal.
m_bHelpMode Indica si la aplicación se encuentra en Help Mode.
m_pszExeName Nombre del ejecutable.
m_pszProfileName Nombre del archivo INI
m_pszRegistryKey Nombre de la clave del registro en lugar de usar un archivo INI.
Funciones (algunas) Descripción
LoadCursor Carga un cursor presente como recurso.
LoadStandardCursor  Carga un cursor estandar, (IDC_) (ver WINDOWS.H).
LoadOEMCursor Carga un cursor estandar, (OIC_) (ver WINDOWS.H).
LoadIcon  Carga un icono de recurso.
LoadStardardIcon Carga un icono estandar, (IDI_) (ver WINDOWS.H).
LoadOEMIcon Carga un icono OEM, (OIC_) (ver WINDOWS.H).
ParseCommandLine  Inicializa un objeto CCommandLineInfo basado en la línea de comando.
ProcessShellCommand  Procesa comando de un objeto CCommandLineInfo.
GetProfileInt  Devuelve un entero de una entrada en el registro o archivo INI.
WriteProfileInt Escribe un entero en una entrada del registro o archivo INI.
GetProfileString Devuelve una cadena de una entrada del registro o archivo INI.
WriteProfileString Escribe una cadena en una entrada del registro o archivo INI.
AddDocTemplate Agrega una plantilla de documento a la aplicación
GetFirstDocTemplatePosition Devuelve un tipo POSITION de la primer entrada en la lista de documentos.
GetNextDocTemplate Devuelve el siguiente objecto CDocument de la lista de documentos.
OpenDocumentFile  Abre un documento por su nombre.
AddToRecentFileList Agregar un item a la lista de reciente archivos abiertos.
CreatePrinterDC   Crea un contexto de dispositivo para imprimir.
GetPrinterDeviceDefaults  Devuelve la impresora predeterminada.
InitInstance * Inicialización de una instancia
Run * Bucle de mensajes.
OnIdle * Proceso para el tiempo muerto.
ExitInstance * Terminación de una instancia.
PreTranslateMessage * Función para filtrar mensajes.
SaveAllModified * Avisa que hay documentos sin guardar.
ProcessWndProcException * Handle por defecto para la excepciones.
DoMessageBox *    Sobreescribiendo esta función se puede modificar AfxMessageBox.
ProcessMessageFilter * Permite interceptar algunos mensajes antes que alcancen la aplicación.
DoWaitCursor Coloca/quita el reloj de arena.
WinHelp Abre el archivo de ayuda.
LoadStdProfileSettings Carga los seteos por defectos de un archivo INI o del registro y la lista de últimos archivos abiertos.
SetDialogBkColor Especifica al color de fondo para todos los diálogos de la aplicación.
SetRegistryKey Hace que los seteos de la aplicación se guarden en el registro de Windows en lugar de un archivo INI.
EnableShellOpen Activa la posibilida de cargar documentos con sólo arrastras un archivo.
RegisterShellFile   Registra todos los tipos de documentos de la aplicación con el administrador de archivos de Windows.
OnFileNew  Implementa comando para ID_FILE_NEW 
OnFileOpen Implementa comando para ID_FILE_OPEN
OnFilePrintSetup Implementa comando para ID_FILE_PRINT_SETUP
OnContextHelp Permite controlar CTRL+F1 en la aplicación.
OnHelp Permite controlar F1 en la aplicación.
OnHelpIndex Implementa comando para ID_HELP_INDEX y provee un tópico por defecto.
OnHelpFinder Implementa comando para ID_HELP_FINDER e ID_DEFAULT_HELP.
OnHelpUsing Implementa comando para ID_HELP_USING.

 

* Métodos virtuales, (se pueden sobrescribir para personalizar su funcionamiento).

Muchas de estas funciones se usan en aplicaciones tipo Documento/Vista.

 

Elija un proyecto WIN32 vacío, (opción Empty Project en el AppWizard).

 

Luego pulse con el botón derecho del mouse sobre el icono del proyecto en el ClassView y elija New Class...

 

 

En la ventana de New Class:

 

Cree la clase CMiAplicacion derivada de CWinApp, acepte esta ventana y el mensaje de advertencia que continua.

 

Luego en el archivo de definición de la clase escriba lo indicado a continuación.

 

#include <afxwin.h>   // Se incluye el archivo de definición de MFC.


class CMiAplicacion : public CWinApp 
{
public:
     virtual BOOL InitInstance();    //  Como se pretende realizar algo al momento iniciar la instancia se agrega este método.
     CMiAplicacion();
     virtual ~CMiAplicacion();
};

 

Y en el archivo CPP de la clase agregue el método InitInstace y una instancia de la nueva clase.

 

#include "MiAplicacion.h"
#include "MiVentana.h"

CMiAplicacion app;    // Se crea una instancia de la clase

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


CMiAplicacion::CMiAplicacion()
{

}

CMiAplicacion::~CMiAplicacion()
{

}
// Método sobrescrito.
BOOL CMiAplicacion::InitInstance()
{

 AfxMessageBox("Hola Mundo");       // ¡Hola Mundo!


return FALSE;                                      // Se retorna FALSE porque se desea que la aplicación no continue.
}

 

Falta algo más para que la aplicación funcione, y es indicarle al compilador que use la librería MFC de forma compartida o estática.

Vaya a Project -> Settings y especifique en Microsof Foundation Classes la opción Use MFC in a shared DLL, acepte la ventana y construya la aplicación.

Ejecute el programa y obtendrá el archi famoso mensaje "Hola Mundo".

 

Acaba de construir la aplicación MFC más básica de todas.

 

Al menos una ventanita

 

Note que justamente el método InitInstance sobrescrito podría hacer algo más que mostrar un cuadro de diálogo con el mensaje "Hola Mundo", podría por ejemplo, al menos, crear  una ventana y mostrarla.

 

CFrameWnd.

 

La clase CWnd de MFC y sus derivadas, (ver Jerarquía MFC.), proveen una interface orientada a objetos para las ventanas que una aplicación crea. Entonces incorpore al proyecto una nueva clase llamada CMiVentana, (o el nombre que más le guste), derivada de CFrameWnd, (tal como hizo con la clase de la aplicación).

En el archivo de definición, (MiVentana.h) escriba:

 

#include <afxwin.h>  // Se incluye MFC

class CMiVentana : public CFrameWnd 
{
public:
CMiVentana ();
virtual ~CMiVentana ();

};

 

Escriba en el archivo de implementación, (MiVentana.cpp), en el constructor:

 

#include "CMiVentana .h"

CMiVentana ::CMiVentana  ()
{
   Create (NULL, _T ("Mi Ventana"));
}

CMiVentana ::~CMiVentana()
{

}

 

Y modifique el archivo de implementación de la aplicación para que incluya el archivo de definición de la ventana #include "MiVentana.h" y modifique InitInstance como sigue:

 

BOOL CMiAplicacion::InitInstance ()
{
m_pMainWnd = new CMiVentana ;     // Se crea un objeto CMiVentana 

m_pMainWnd->ShowWindow (m_nCmdShow);    // Se muestra la ventana.
m_pMainWnd->UpdateWindow ();                          // Se invoca UpdateWindow.

return TRUE;   // Ahora el método InitInstance debe retornar TRUE porque la aplicación continúa.
}

 

Ahora, en lugar de mostrar un mensaje "Hola Mundo", la aplicación crea una ventana y guarda la instancia en el dato miembro m_pMainWnd. La ejecución de esta línea, (la que crea la ventana con new), da a lugar a que en ese instante se ejecute el constructor de la clase CMiVentana el cual invoca el método Create().

Luego se muestra la ventana con ShowWindow() y se llama a UpdateWindow() para que se procese el mensaje WM_PAINT el cual ahora NO estamos procesando porque nuestra ventana aún no procesa mensajes.

 

El método InitInstance() ahora debe retornar TRUE para que la aplicación continue e inicie el bucle de mensaje que envia mensajes a nuestra ventana. No obstante nuestra ventana deja pasar todos los mensaje.

 

Puede construir y probar la aplicación, obtendrá una ventana típica de Windows.

 

La ventana CMiVentana es derivada de CFrameWnd que a su vez ha sido derivada de CWnd.

El objeto aplicación crea una ventana, creando un objeto CMiVentana que a su vez en el constructor invoca un el método Create() o CreateEx().

 

 Create (NULL, _T ("Mi Ventana"));

 

_T es una macro que hace que los caracteres de la cadena "Mi Ventana" sean neutrales, esto es importante si se deasea que la aplicación compile como UNICODE, (no es algo muy util ahora esto y puede quitar esa macro e igual va a funcionar el programa). Create() es una función miembro de CFrameWnd y que hemos heredado entre muchísimas otras cuando derivamos nuestra clase CMiVentana de CFrameWnd.

La función Create() tiene 8 parámetros de los cuales 6 admiten valores por defecto, y nuestro ejemplo sólo define valores para los primeros 2. El segundo es el título de la ventana.

Para un completo desarrollo de todos los parámetros remítase a la ayuda MSDN para CFrameWnd::Create().

 

El mapa de mensajes.

 

Para finalizar este articulo, y lograr conseguir escribir una aplicación MFC desde 0 que sea similar a lo generado por el Application Wizard, faltaría agregarle a nuestra ventana algo que permita procesar los mensajes que la aplicación le envía y además deseamos procesar.

Supongamos que queremos que en el centro de la ventana, (la región cliente), se imprima el texto "Hola Mundo", para variar. : )

Bueno, una ventana se pinta muchas veces mientras se está mostrando, es más, se pinta cada vez que llega el mensaje WM_PAINT, y esto ocurre cuando la ventana se muestra por primera vez, cuando se mueve la ventana, es ocultada por otra ventana, etc. Por lo tanto necesitamos un handle de mensaje, o sea una función que contenga el código que se ejecutará cada vez que llega el mensaje WM_PAINT.

Para ésto MFC implementa el mapa de mensajes definiendo por medio de algunas macros cuál es la función handle del X mensaje de Windows.

Para agregarle esta capacidad a nuestra ventana edite el archivo de definición agregando antes de la llave de cierre de la clase la siguiente línea:

 

DECLARE_MESSAGE_MAP ()

 

Sin ";".

 

Y además el siguiente prototipo:

 

protected:

      afx_msg void OnPaint ();

 

Luego edite el archivo de implementación, (MiVentana.cpp), agregando al principio, (luego del #include),  las siguientes líneas:

 

BEGIN_MESSAGE_MAP (CMiVentana, CFrameWnd) 

ON_WM_PAINT () 

END_MESSAGE_MAP ()

 

Todas sin ";".

 

Y la implementación de OnPaint() al final:

 

void CMiVentana::OnPaint ()
{
CPaintDC dc (this);

CRect rect;
GetClientRect (&rect);

dc.DrawText (_T ("Hola Mundo"), -1, &rect,
DT_SINGLELINE ¦ DT_CENTER ¦ DT_VCENTER);
}

 

Listo, ahora puede construir la aplicación y correr el programa. Obtendrá la misma ventana anterior pero ahora imprime el texto "Hola Mundo" centrado en la región cliente.

 

El mapa de mensaje es un mecanismo por el cual uno puede "redireccionar" los mensajes que desea procesar a funciones miembro de la clase sólo necesita saber que mensaje quiere, (todos son de la forma WM_), y agregar un método similar al OnPaint y una entrada en el mapa de mensajes.

MFC provee una macro tipo ON_WM_MENSAJE para muchos mensaje de Windows. Por ejemplo si desea agregar un handle para el mensaje WM_LBUTTONDOWN debería incluir una entrada en el mapa de mensajes como:

 

ON_WM_LBUTTONDOWN()

 

Y además una función: 

 

afx_msg void OnLButtonDown (UINT nFlags, CPoint point)

 

La documentación de MFC le informa cuántos y cuáles parámetros son los que necesita y que información traen.

Luego en la función OnLButtonDown() escribiría el código que desea se ejecute al pulsar el botón izquierdo del mouse.

 

En caso que no exista la macro MFC para el mensaje que desea procesar, se puede usar la macro ON_MESSAGE para agregar la entrada al mapa de mensajes y además especificar cuál es la función handle de dicho mensaje. Pero esto sería para un futuro análisis.

 


Resumiendo:

 

Todo esto es lo necesario para entender el código que el Application Wizard genera, por lo menos en lo que a la estructura más fundamental de una aplicación respecta. De esta forma se adquiere un mayor conocimiento de la aplicación MFC que se está desarrollando.

 

Hasta el próximo artículo.!!

 

 

Volver a la página principal