¿Por qué utilizar paquetes?
La respuesta es simple: por el poder del paquete. Los paquetes en tiempo de diseño simplifican el lanzamiento y la instalación de componentes personalizados; los paquetes en tiempo de ejecución inyectan nueva potencia a la programación tradicional. Una vez que compila código reutilizable en una biblioteca en tiempo de ejecución, puede compartirlo entre múltiples aplicaciones. Todas las aplicaciones pueden acceder a componentes estándar a través de paquetes, y el propio Delphi lo hace. Debido a que la aplicación no tiene que copiar una biblioteca de componentes separada en el archivo ejecutable, esto ahorra enormemente recursos del sistema y espacio en disco. Además, los paquetes reducen el tiempo dedicado a la compilación porque solo necesita compilar código específico de la aplicación.
Si los paquetes se pueden usar dinámicamente, entonces podemos obtener más beneficios. Los paquetes proporcionan un enfoque modular novedoso para el desarrollo de aplicaciones. A veces es posible que desee convertir ciertos módulos en componentes opcionales de la aplicación, como un sistema de contabilidad que viene con un módulo de recursos humanos opcional. En algunos casos, solo necesita instalar la aplicación básica, mientras que en otros casos puede que necesite instalar módulos de recursos humanos adicionales. Este enfoque modular se puede implementar fácilmente mediante tecnología de paquetes. En el pasado, esto sólo se podía lograr cargando dinámicamente una DLL, pero utilizando la tecnología de empaquetado de Delphi, se puede "empaquetar" cada tipo de módulo de la aplicación en paquetes. En particular, los objetos de clase creados a partir de paquetes son propiedad de la aplicación y, por lo tanto, pueden interactuar con objetos de la aplicación.
Paquetes y aplicaciones en tiempo de ejecución
Muchos desarrolladores sólo piensan en los paquetes de Delphi como un lugar para colocar componentes, cuando en realidad los paquetes pueden (y deben) usarse en el diseño de aplicaciones modulares.
Para demostrar cómo usar paquetes para modularizar su aplicación, creemos un ejemplo:
1. Cree un nuevo programa Delphi con dos formularios: Form1 y Form2;
2. Elimine Form2 de la lista de formularios creada automáticamente (PRoject | Opciones | Formularios);
3. Coloque un botón en Form1 e ingrese el siguiente código en el controlador de eventos OnClick del botón:
con TForm2.Create(aplicación) hacer
comenzar
MostrarModal;
Gratis;
Fin;
4. Recuerde agregar Unidad2 a la cláusula de usos de Unidad1;
5. Guarde y ejecute el proyecto.
Creamos una aplicación simple que muestra un formulario con un botón que, cuando se hace clic, crea y muestra otro formulario.
Pero, ¿qué debemos hacer si queremos incluir Form2 en el ejemplo anterior en un módulo reutilizable y que siga funcionando normalmente?
La respuesta es: ¡Bao!
Para crear un paquete para Form2 se requiere el siguiente trabajo:
1. Abra el Administrador de proyectos (Ver | Administrador de proyectos);
2. Haga clic derecho en el grupo de proyectos y seleccione "Agregar nuevo proyecto...";
3. Seleccione "Paquete" en la lista de proyectos "Nuevo";
4. Ahora deberías poder ver el editor de paquetes;
5. Seleccione el elemento "Contiene" y haga clic en el botón "Agregar";
6. Luego haga clic en el botón "Examinar..." y seleccione "Unit2.pas";
7. El paquete ahora debería contener la unidad "Unit2.pas";
8. Finalmente guarde y compile el paquete.
Ahora hemos completado el paquete. Debería haber un archivo llamado "paquete1.bpl" en su directorio Proyecto/BPL. (BPL es la abreviatura de Borland Package Library y DCP es la abreviatura de Delphi CompiledPackage).
Este paquete está completo. Ahora necesitamos activar el interruptor de opciones del paquete.
y vuelva a compilar la aplicación original.
1. Haga doble clic en "Project1.exe" en el administrador de proyectos para seleccionar el proyecto;
2. Haga clic derecho y seleccione "Opciones..." (también puede seleccionar Proyecto | Opciones... en el menú);
3. Seleccione la página de opción “Paquetes”;
4. Seleccione la casilla de verificación "Compilar con paquetes de tiempo de ejecución";
5. Edite el cuadro de edición "Paquetes de tiempo de ejecución": "Vcl50;Paquete1" y haga clic en el botón "Aceptar";
6. Nota: No elimine la Unidad2 de la aplicación;
7. Guarde y ejecute la aplicación.
La aplicación se ejecutará como antes, pero la diferencia se puede ver en el tamaño del archivo.
Project1.exe ahora tiene un tamaño de solo 14 KB, en comparación con los 293 KB anteriores. Si utiliza el Explorador de recursos para ver el contenido de los archivos EXE y BPL, verá que el DFM y el código de Form2 ahora están guardados en el paquete.
Delphi completa la vinculación estática de paquetes durante la compilación. (Es por eso que no puede eliminar Unit2 del proyecto EXE).
Piense en lo que puede ganar con esto: puede crear un módulo de acceso a datos en el paquete y, cuando cambie las reglas de acceso a datos (como cambiar de conexiones BDE a conexiones ADO), modifique ligeramente y vuelva a publicar el paquete. Alternativamente, puede crear un formulario en un paquete que muestre el mensaje "Esta opción no está disponible en la versión actual" y luego crear un formulario completamente funcional en otro paquete con el mismo nombre. Ahora tenemos el producto en versiones "Pro" y "Enterprise" sin ningún esfuerzo.
Carga y descarga dinámica de paquetes.
En la mayoría de los casos, una DLL o BPL vinculada estáticamente será suficiente. Pero ¿qué pasa si no queremos lanzar BPL? "La biblioteca de vínculos dinámicos Package1.bpl no se puede encontrar en el directorio especificado" es el único mensaje que podemos recibir antes de que finalice la aplicación. O, en una aplicación modular, ¿podemos utilizar cualquier número de complementos?
Necesitamos conectarnos dinámicamente a BPL en tiempo de ejecución.
Para las DLL, existe un método simple, que consiste en utilizar la función LoadLibrary:
función LoadLibrary(lpLibFileName: Pchar): HMODULE;stdcall;
Después de cargar la DLL, podemos usar la función GetProcAddress para llamar a las funciones y métodos exportados de la DLL:
función GetProcAddress(hModule: HMODULE; lpProcName:LPCSTR): FARPROC;
Finalmente, usamos FreeLibrary para desinstalar la DLL:
función FreeLibrary(hLibModule: HMODULE): BOOL;stdcall;
En el siguiente ejemplo cargamos dinámicamente la biblioteca HtmlHelp de Microsoft:
función TForm1.ApplicationEvents1Help(Comando: Word; Datos: Entero; var CallHelp: Booleano):Booleano;
tipo
TFNHtmlHelpA = function(hwndCaller: HWND; pszFile: PansiChar; uCommand: UINT;dwData: Dword): HWND;
var
Módulo de ayuda: Hmodule;
Ayuda HTML: TFNHtmlHelpA;
comenzar
Resultado := Falso;
Módulo de ayuda := LoadLibrary('HHCTRL.OCX');
si HelpModule <> 0 entonces
comenzar
@HtmlHelp := GetProcAddress(HelpModule, 'HtmlHelpA');
si @HtmlHelp <> nulo entonces
Resultado := HtmlHelp(Application.Handle,Pchar(Application.HelpFile), Comando,Datos) <> 0;
Biblioteca gratuita (módulo de ayuda);
fin;
LlamarAyuda := Falso;
fin;
Cargando dinámicamente BPL
Podemos usar el mismo método simple para lidiar con BPL, o debería decir básicamente de la misma manera simple.
Podemos cargar paquetes dinámicamente usando la función LoadPackage:
función LoadPackage (nombre constante: cadena): HMODULE;
Luego use la función GetClass para crear un objeto de tipo TPersistentClass:
función GetClass(const AclassName: cadena):TPersistentClass;
Una vez hecho todo, utilice UnLoadPackage(Module:HModule);
Hagamos algunos pequeños cambios al código original:
1. Seleccione "Project1.exe" en el administrador de proyectos;
2. Haga clic derecho y seleccione "Opciones...";
3. Seleccione la página de opción “Paquetes”;
4. Elimine "Paquete1" del cuadro de edición "Paquetes de tiempo de ejecución" y haga clic en el botón Aceptar;
5. En la barra de herramientas de Delphi, haga clic en el botón "Eliminar archivo del proyecto";
6. Seleccione "Unidad2 | Form2" y haga clic en Aceptar;
7. Ahora, en el código fuente de "Unit1.pas", elimine Unit2 de la cláusula de usos;
8. Ingrese el código de tiempo OnClick del Botón1;
9. Agregue dos variables de tipo HModule y TPersistentClass:
var
Módulo de paquete: HModule;
Clase A: TPersistentClass;
10. Utilice la función LoadPackage para cargar el paquete Pacakge1:
PackageModule := LoadPackage('Paquete1.bpl');
11. Compruebe si PackageModule es 0;
12. Utilice la función GetClass para crear un tipo persistente:
Clase A := GetClass('TForm2');
13. Si este tipo persistente no es nulo, podemos volver al anterior
Crea y usa objetos de este tipo de la misma manera:
con TComponentClass(AClass).Create(Application) como lo hace TcustomForm
comenzar
MostrarModal;
Gratis;
fin;
14. Finalmente, utilice el proceso UnloadPackage para desinstalar el paquete:
DescargarPaquete(PackageModule);
15. Guarde el proyecto.
Aquí está la lista completa de controladores de eventos OnClick:
procedimiento TForm1.Button1Click (Remitente: Tobject);
var
Módulo de paquete: HModule;
Clase A: TPersistentClass;
comenzar
PackageModule := LoadPackage('Paquete1.bpl');
si PackageModule <> 0 entonces
comenzar
Clase A := GetClass('TForm2');
si AClass <> nulo entonces
con TComponentClass(AClass).Create(Application) como lo hace TcustomForm
comenzar
MostrarModal;
Gratis;
fin;
DescargarPaquete(PackageModule);
fin;
fin;
Desafortunadamente, eso no es todo.
El problema es que la función GetClass sólo puede buscar tipos registrados. Las clases de formulario y las clases de componentes a las que normalmente se hace referencia en un formulario se registran automáticamente cuando se carga el formulario. Pero en nuestro caso, el formulario no se puede cargar antes de tiempo. Entonces, ¿dónde registramos el tipo? La respuesta es, en la bolsa. Cada unidad del paquete se inicializa cuando se carga el paquete y se limpia cuando se descarga el paquete.
Ahora volvamos a nuestro ejemplo:
1. Haga doble clic en "Paquete1.bpl" en el administrador de proyectos;
2. Haga clic en el signo + junto a "Unidad2" en la sección "Contiene";
3. Haga doble clic en "Unit2.pas" para activar el editor de código fuente de la unidad;
4. Agregue la sección de inicialización al final del archivo;
5. Utilice el procedimiento RegisterClass para registrar el tipo de formulario:
Clase de registro (TForm2);
6. Agregue una sección de finalización;
7. Utilice el procedimiento UnRegisterClass para cancelar el registro del tipo de formulario:
AnularRegisterClass(TForm2);
8. Finalmente, guarde y compile el paquete.
Ahora podemos ejecutar "Proyecto1" de forma segura y funcionará igual que antes, pero ahora puedes cargar los paquetes como quieras.
fin
Recuerde, ya sea que esté utilizando paquetes de forma estática o dinámica, active Proyecto | Opciones | Construir con paquetes de tiempo de ejecución.
Antes de desinstalar un paquete, recuerde destruir todos los objetos de clase del paquete y cancelar el registro de todas las clases registradas. El siguiente proceso puede ayudarle:
procedimiento DoUnloadPackage(Módulo: HModule);
var
i: Entero;
M: TMemoryBasicInformación;
comenzar
para i: = Application.ComponentCount - 1 hasta 0
comenzar
VirtualQuery(GetClass(Application.Components[i].ClassName), M, Sizeof(M));
si (Módulo = 0) o (HMODULE(M.AllocationBase) = Módulo) entonces
Componentes.de.aplicación[i].Gratis;
fin;
Anular el registro de clases de módulo (módulo);
DescargarPaquete(Módulo);
fin;
Antes de cargar el paquete, la aplicación necesita conocer los nombres de todas las clases registradas. Una forma de mejorar esta situación es crear un mecanismo de registro que le indique a la aplicación los nombres de todas las clases registradas por el paquete.
Ejemplo
Paquetes múltiples: los paquetes no admiten referencias circulares. Es decir, una unidad no puede hacer referencia a una unidad que ya hace referencia a esa unidad (jeje). Esto dificulta que el método llamado establezca ciertos valores en el formulario de llamada.
La solución a este problema es crear algunos paquetes adicionales a los que hacen referencia tanto el objeto que llama como los objetos del paquete. ¿Imagínese cómo hacemos que la aplicación sea propietaria de todos los formularios? La variable Aplicación se crea en Forms.pas y se incluye en el paquete VCL50.bpl. Es posible que haya notado que su aplicación no solo necesita compilar VCL50.pas, sino que también requiere VCL50 en su paquete.
En nuestro tercer ejemplo, diseñamos una aplicación para mostrar información del cliente y, bajo demanda, pedidos de los clientes (dinámicamente).
Entonces, ¿por dónde podemos empezar? como todas las aplicaciones de bases de datos
El procedimiento es el mismo, necesitamos conectarnos. Creamos un módulo de datos principal que contiene una conexión TDataBase. Luego encapsulamos este módulo de datos en un paquete (cst_main).
Ahora, en la aplicación, creamos un formulario de cliente y hacemos referencia a DataModuleMain (vinculamos estáticamente VCL50 y cst_main).
Luego creamos un nuevo paquete (cst_ordr) que contiene el formulario de pedido del cliente y requiere cst_main. Ahora podemos cargar dinámicamente cst_ordr en la aplicación. Dado que el módulo de datos principal ya existe antes de que se cargue el paquete dinámico, cst_ordr puede usar directamente la instancia del módulo de datos principal de la aplicación.
La imagen de arriba es un diagrama funcional de esta aplicación:
Paquetes reemplazables: otro caso de uso de paquetes es la creación de paquetes reemplazables. La implementación de esta funcionalidad no requiere las capacidades de carga dinámica del paquete. Supongamos que queremos lanzar una versión de prueba del programa por tiempo limitado, ¿cómo lograrlo?
Primero creamos un formulario "Splash", generalmente una imagen con la palabra "Prueba", y lo mostramos durante el inicio de la aplicación. Luego creamos un formulario "Acerca de" que proporciona información sobre la aplicación. Finalmente, creamos una función que prueba si el software está desactualizado. Encapsulamos estos dos formularios y esta función en un paquete y lo lanzamos con la versión de prueba del software.
Para la versión paga, también creamos un formulario "Splash" y un formulario "Acerca de" - con los mismos nombres de clase que los dos formularios anteriores - y una función de prueba (que no hace nada) y los agregamos encapsulados en un paquete con el mismo nombre.
¿Qué qué? ¿Es esto útil, preguntas? Bueno, podemos lanzar una versión de prueba del software al público. Si un cliente compra la aplicación, solo necesitamos enviarle el paquete que no es de prueba. Esto simplifica enormemente el proceso de lanzamiento del software, ya que solo se requiere una instalación y una actualización del paquete de registro.
El paquete abre otra puerta al diseño modular para las comunidades de desarrollo de Delphi y C++ Builder. Con los paquetes ya no es necesario pasar identificadores de ventana, no más funciones de devolución de llamada, no más otra tecnología DLL. Esto también acorta el ciclo de desarrollo de la programación modular. Todo lo que tenemos que hacer es dejar que los paquetes de Delphi trabajen para nosotros.