Escribir una DLL de extensión.

 

Por: Demian Panello.

 

Visual C++ nos permite escribir tres tipos de librerías:

 

- Librería de extensión con soporte para MFC.

- Librería de enlace dinámico.

- Librería de enlace estático para Win32.

 

En los tres casos uno crea un archivo .dll, pero lo que cambia son varias cosas, como por ejemplo las cosas que la librería puede exportar, el tipo de enlazado, (implícito o explícito), la forma de invocar alguna función, enlazado simbólico u ordinal, en fin muchas cosas cambian de acuerdo al tipo de dll a escribir.

Lo que aquí haremos es crear una dll que será un módulo de un programa que también escribiremos. Es muy simple, escribiremos un programa que permita convertir medidas de longitud, o sea, dado un valor en metros poder obtener su equivalente en pulgadas o en pies, (y viceversa).

 

Por un lado tendremos un programa, (al cual se le suele llamar "cliente"), que tendrá cuadros de edición, botones de opción, botones, etc, donde el usuario ingresará el valor a convertir. Este programa lo desarrollaremos al final del artículo.

Y por otro lado, para no tener funciones miembros fuera de lo común en la clase del diálogo, (me refiero a las funciones de conversión entre medidas que deberíamos tener), escribiremos una clase, (por ejemplo CFactor), en una DLL de extensión. Entonces, desde el programa cliente si lo enlazamos debidamente con esta dll, podremos crear un objeto CFactor apropiado para la conversión.

Manos a la obra entonces.

 

Creación de la DLL de extensión.

 

Primero que todo cree una carpeta, por ejemplo con el nombre "Proyecto DLL", (o cualquier otro). Aquí se alojarán los proyectos de la dll y luego de la aplicación cliente.

 

Seleccione la opción "MFC AppWizard (dll)". El nombre del proyecto será "Factores", (no se olvide de guardarlo en la carpeta que creó, para eso pulse el botón "Location").

 

 

Acepte las ventanas siguientes y el AppWizard creará el proyecto.

 

Notará que el proyecto creado consta de un archivo de implementación que tiene una función llamada DllMain() más o menos como ésta:

 

// factores.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include <afxdllx.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

static AFX_EXTENSION_MODULE FactoresDLL = { NULL, NULL };

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
// Remove this if you use lpReserved
UNREFERENCED_PARAMETER(lpReserved);

if (dwReason == DLL_PROCESS_ATTACH)
 {
   TRACE0("FACTORES.DLL Initializing!\n");
   // Extension DLL one-time initialization
   if (!AfxInitExtensionModule(FactoresDLL, hInstance))
      return 0;

   // Insert this DLL into the resource chain
   // NOTE: If this Extension DLL is being implicitly linked to by
   // an MFC Regular DLL (such as an ActiveX Control)
   // instead of an MFC application, then you will want to
   // remove this line from DllMain and put it in a separate
   // function exported from this Extension DLL. The Regular DLL
   // that uses this Extension DLL should then explicitly call that
   // function to initialize this Extension DLL. Otherwise,
   // the CDynLinkLibrary object will not be attached to the
   // Regular DLL's resource chain, and serious problems will
   // result.


   new CDynLinkLibrary(FactoresDLL);
  }
else if (dwReason == DLL_PROCESS_DETACH)
 {
   TRACE0("FACTORES.DLL Terminating!\n");
   // Terminate the library before destructors are called
  
AfxTermExtensionModule(FactoresDLL);
  }
return 1; // ok
}

 

 Bien, este es el punto de entrada a la dll, así como en las aplicaciones es Main() o WinMain(). Para este ejemplo no escribiremos nada en esa función, sino que escribiremos una nueva clase.

 

La Clase CFactor.

 

Crearemos una clase que será nuestro objeto, (herramienta), para realizar las conversiones entre medidas. Pulse con el botón derecho sobre el icono del Workspace en el Class View, (es el primer nodo del árbol) y seleccione, del menú contextual, la opción "New Class". Esto abrirá la ventana "New Class":

 

 

Colóquele el nombre CFactor a la clase de tipo Generic Class.

Nota: En la imagen de arriba observará que en File name dice "Factor1.cpp"; a usted debería aparecerle "Factor.cpp", lo que ocurre es que yo ya tengo la clase creada y la captura de pantalla la realice luego, así que no le dé importancia a lo que dice la imagen, no le va a quedar igual, (a menos que así lo desee).

 

Ahora haremos varias modificaciones a nuestra clase.

Primero vaya al archivo de definición de la clase "Factor.h" y agregue los datos y funciones miembros indicados y lo que está resaltado.

 

 

// Factor.h: interface for the CFactor class.
//
//////////////////////////////////////////////////////////////////////


...
#if _MSC_VER > 1000
   #pragma once
#endif // _MSC_VER > 1000

enum CONVERTIONFORMAT
{
  CF_MTOI, //metter to inch (metros a pulgadas)
  CF_ITOM, //inch to metter
  CF_MTOF, //metter to feed (metros a pies)
  CF_FTOM //feed to metter
};

class AFX_EXT_CLASS CFactor 
{
private:
      double m_value;
      int m_type;

public:
   CFactor();
   CFactor(int ConvertionFormat , double value);
   double GetValue();
   void SetValue(double value);
   int GetConvetionFormat();
   void SetConvertionFormat(int FormatConvertion);
   virtual ~CFactor();
};
...

 

Es fundamental no olvidarse de especificar AFX_EXT_CLASS en la definición de la clase, puesto que ésta es la clave para indicarle al compilador que la clase forma parte de un módulo de extensión, (esto entra en juego luego, cuando escribamos el programa cliente).

Ahora nos toca retocar el archivo de implementación.

 

// Factor.cpp: implementation of the CFactor class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Factor.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

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


CFactor::CFactor(int ConvertionFormat, double value)
{
  m_value=value;
  m_type= ConvertionFormat;
}

CFactor::~CFactor()
{
}

double CFactor::GetValue()
{
  double res;
  switch (m_type)
  {
    case CF_MTOI:
           res = m_value*39.37;
           break;
    case CF_ITOM:
           res=m_value/39.37;
           break;
    case CF_MTOF:
           res=m_value*3.281;
           break;
    case CF_FTOM:
           res=m_value/3.281;
}
return res;
}

void CFactor::SetValue(double value)
{
   m_value=value;
}

void CFactor::SetConvertionFormat(int FormatConvertion)
{
  m_type=FormatConvertion;
}

int CFactor::GetConvetionFormat()
{
  return m_type;
}

 

El objeto recibe en su constructor dos parámetros que le indican qué tipo de conversión, (dado por el tipo enumerado CONVERTIONFORMAT) y el valor a convertir. Luego están los métodos, los típicos Set.. y Get.. que especifican o devuelven un valor y el método GetValue() que aplica el factor de conversión apropiado.

No me interesa en este momento el objeto CFactor en sí, sino la creación del la dll de extensión.

Construya el proyecto y verá que en la carpeta Debug se generó un archivo .dll y otro .lib. Copie el archivo .dll al directorio Windows/System o Windows/System32, (o Winnt en caso de tener Windows NT o 2000).

 

Descargar proyecto DLL de extensión 

 

El programa cliente.

 

Cierre el proyecto anterior y cree uno nuevo, (MFC AppWizard exe), Dialog Based con el nombre "Factores_demo" y ubíquelo en la carpeta que creó al principio.

 

En primer lugar deberá incluir en el proyecto el archivo de definición de la clase CFactor, para esto copie el archivo Factor.h a la carpeta de éste proyecto, (probablemente si siguió al pié de la letra lo que está leyendo sería "Proyecto Dll/Factores_demo"), y luego vaya al menú Project -> Add to project -> Files e incluya éste archivo al proyecto.

Luego agregue en el archivo de definición del diálogo #include "factor.h", para que el diálogo sepa de la existencia de este objeto, (¡sólo el archivo .h!).

 

Además deberá especificarle al compilador que debe enlazar este ejecutable con la librería .lib del proyecto dll de extensión. Esto lo puede hacer seleccionado el menú Project -> Settings, y luego la solapa Link y en Object / Library modules escriba "factores.lib", (sin las comillas).

 

 

Nota: escriba simplemente factores.lib como indica la imagen si previamente copió el archivo .lib al directorio de ésta aplicación cliente, sino deberá escribir toda la ruta.

 

Diseñe el diálogo más o menos como muestra la siguiente imagen:

 

 

Especifique las siguiente propiedades, (además de las apreciables en la imágen) y variables miembros a los controles:

 

Objeto ID Propiedades Variable miembro
Option button IDC_RADIO_CONVERTION Group int m_iConvertion
Option button valor por defecto por defecto ninguna
Option button valor por defecto por defecto ninguna
Option button valor por defecto por defecto ninguna
Edit Box IDC_VALOR number double m_dvalor
Edit Box IDC_RESULTADO numbre, read only double m_dResultado
Button IDC_CONVERTIR Default button ninguna
Button IDC_CERRAR por defecto ninguna

 

En el constructor del diálogo inicialice la variable m_iConvertion en 0 así por defecto queda seleccionado el primer option button.

 

Y finalmente los métodos OnConvertir() y OnCerrar() deben quedar así:

 

void CFactores_demoDlg::OnConvertir() 
{
UpdateData(TRUE);
CFactor m_Factor(m_iConvertion, m_dvalor);
m_dResultado=m_Factor.GetValue();
UpdateData(FALSE);
}

void CFactores_demoDlg::OnCerrar() 
{
EndDialog(IDOK); 
}

 

En OnConvertir() primero transferimos la información de los controles a las variables asociadas con UpdateData(TRUE) y luego creamos un objeto CFactor, (proveniente de la DLL que escribimos), pasándole al constructor parámetros que indican el tipo de conversión, (dado por la variable de transferencia de los option buttons que cuando es 0, metros -> pulgadas, coincide con el primer valor enumerado CF_MTOI y así sucesivamente) y el valor a convertir.

Luego llamamos al método GetValue() que nos devuelve el valor en la nueva unidad.

Entonces, pasamos la nueva información a los controles con UpdateData(FALSE).

 

Hemos llegado al final de este breve articulo introductorio sobre la creación de dlls de extensión, puede extender la clase CFactor agregandole más factores de conversión, por ejemplo peso, volumen, densidad, tiempo, etc. Para ésto probablemente necesite adecuar la versión actual para ese propósito. O bien, experimente con nuevos objetos derivados de objetos MFC, (botones, listas, ventanas), tratando de aunar lo visto en los diversos artículos de subclassing con la creación éste de dll, (y será su primer paso para la creación de un control ActiveX).

 

Descargar fuente de los ejemplos: factores.zip(20 kb) y factores_demo.zip(33 kb)

Volver a la página principal