Fundamentos de MFC y Desarrollo de Aplicaciones en Windows

Las aplicaciones de escritorio para Windows desarrolladas con el framework MFC (Microsoft Foundation Classes) se dividen generalmente en dos categorías: aplicaciones basadas en diálogos y aplicaciones basadas en la arquitectura Documento-Vista. Mientras que las aplicaciones basadas en diálogos son ideales para interfaces repletas de controles interactivos como botones y campos de entrada, la arquitectura Documento-Vista suele emplear menús y barras de herramientas para la interacción, facilitando la gestión de datos complejos.

Estructura básica de una aplicación MFC

Para crear un programa MFC desde cero, es necesario configurar el proyecto para utilizar MFC en una DLL compartida y establecer el subsistema en "Windows". A continuación, se presenta una implementación mínima que integra una consola de depuración dentro de una ventana de marco estándar.

#include <afxwin.h>
#include <iostream>

// Clase para el marco principal de la ventana
class VentanaPrincipal : public CFrameWnd {
    DECLARE_MESSAGE_MAP()
public:
    VentanaPrincipal() {}
protected:
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnClose();
private:
    FILE* flujoConsola;
};

BEGIN_MESSAGE_MAP(VentanaPrincipal, CFrameWnd)
    ON_WM_CREATE()
    ON_WM_CLOSE()
END_MESSAGE_MAP()

int VentanaPrincipal::OnCreate(LPCREATESTRUCT lpCreateStruct) {
    // Inicialización de consola para logs de depuración
    AllocConsole();
    freopen_s(&flujoConsola, "CONOUT$", "w", stdout);
    return CFrameWnd::OnCreate(lpCreateStruct);
}

void VentanaPrincipal::OnClose() {
    if (flujoConsola) fclose(flujoConsola);
    FreeConsole();
    CFrameWnd::OnClose();
}

// Clase de la aplicación
class MiAppSencilla : public CWinApp {
public:
    virtual BOOL InitInstance() override {
        VentanaPrincipal* pFrame = new VentanaPrincipal();
        pFrame->Create(NULL, _T("Aplicación Base MFC"));
        m_pMainWnd = pFrame;
        pFrame->ShowWindow(SW_SHOW);
        pFrame->UpdateWindow();
        return TRUE;
    }
};

MiAppSencilla laAplicacion; // Instancia global única

El ciclo de vida y el bucle de mensajes

El inicio de una aplicación MFC sigue una secuencia defindia que culmina en un bucle de eventos. El punto de entrada oculto es WinMain, que invoca a AfxWinMain. Este último orquestaliza la inicialización de recursos a través de InitApplication e InitInstance, para finalmente entrar en el método Run, donde se procesan los mensajes del sistema.

Existen dos formas principales de gestionar hilos en MFC:

  • AfxBeginThread (basado en función): Crea un hilo simple pasando un puntero a una función.
  • AfxBeginThread (basado en clase): Utiliza una clase derivada de CWinThread para hilos con su propio bucle de mensajes.

Mecanismos internos de Win32 y Diálogos

Para comrpender cómo MFC encapsula la API de Windows, es útil observar cómo funciona un diálogo de forma nativa. El sistema utiliza una cola de mensajes que pueden ser de sistema (compartidos) o de aplicación (específicos del hilo).

// Ejemplo conceptual de un procedimiento de diálogo nativo
INT_PTR CALLBACK GestorDialogo(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

En MFC, esto se simplifica mediante la clase CDialog. Al trabajar con diálogos, el intercambio de datos se gestiona mediante el método DoDataExchange (DDX), que vincula variables del código con controles de la interfaz.

Arquitectura Documento-Vista (SDI)

En aplicaciones de Interfaz de Documento Único (SDI), la lógica se separa en tres componentes: el Documento (datos), la Vista (representación visual) y el Frame (contenedor). Es común utilizar CSplitterWnd para dividir el área del cliente en múltiples secciones.

class DocTecnico : public CDocument {
    DECLARE_DYNCREATE(DocTecnico)
};
IMPLEMENT_DYNCREATE(DocTecnico, CDocument)

class VistaTecnica : public CView {
    DECLARE_DYNCREATE(VistaTecnica)
public:
    virtual void OnDraw(CDC* pDC) {
        pDC->TextOut(50, 50, _T("Renderizado de vista"));
    }
};
IMPLEMENT_DYNCREATE(VistaTecnica, CView)

class MarcoSdi : public CFrameWnd {
    DECLARE_DYNCREATE(MarcoSdi)
protected:
    CSplitterWnd m_divisor;
    virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) {
        return m_divisor.CreateStatic(this, 1, 2) &&
               m_divisor.CreateView(0, 0, pContext->m_pNewViewClass, CSize(200, 0), pContext) &&
               m_divisor.CreateView(0, 1, pContext->m_pNewViewClass, CSize(0, 0), pContext);
    }
};
IMPLEMENT_DYNCREATE(MarcoSdi, CFrameWnd)

Interfaz de Documentos Múltiples (MDI)

La arquitectura MDI permite abrir varios documentos simultáneamente bajo un mismo marco principal. MFC moderno utiliza CMDIFrameWndEx y CMDIChildWndEx para ofrecer una experiencia de pestañas similar a los navegadores web actuales mediante EnableMDITabbedGroups.

Consideraciones Técnicas Relevantes

  1. Vinculación HWND y CWnd: MFC utiliza un "hook" global (_AfxCbtFilterHook) para interceptar la creación de ventanas y asociar el manejador de Windows (HWND) con el objeto C++ (CWnd) en un mapa global.
  2. Flujo de Comandos: Los mensajes no solo viajan por las ventanas; MFC los enruta a través de una cadena de mando: Vista Activa -> Documento -> Marco -> Aplicación.
  3. Subclasificación: Es posible asociar un objeto CWnd a una ventana ya existente mediante SubclassWindow, lo cual es fundamental para personalizar controles estándar de Windows.
  4. Inicialización de Diálogos: A diferencia de las ventanas normales que reciben WM_CREATE, los diálogos utilizan WM_INITDIALOG una vez que todos los controles internos han sido creados.

Etiquetas: MFC C++ Win32 Windows-SDK Desktop-Development

Publicado el 5-30 18:06