Archivo

Entradas Etiquetadas ‘BPL’

DLL’s, BPL’s, Carga dinámica/Estática y «Packages en Runtime»

lunes, 19 de julio de 2010 9 comentarios
Share Button

Cuando uno empieza a conocer Delphi (al menos a mi me pasó) y dependiendo de si «viene» de otros lenguajes, se hace un lío con las diferentes formas en que podemos generar un programa; Con la forma de trabajar con packages o sin packages, con los ficheros a distribuir,… Y le empiezan a surgir diferentes preguntas:

¿Porqué mi ejecutabe ocupa tanto si sólo he puesto un botón?
¿Qué son los packages de liberías? ¿Los debo copiar con mi aplicación?
¿Porque si marco «Build with runtime packages» mi programa ocupa tampoco?
¿Porque si marco «Build with runtime packages» mi programa no funciona en otras máquinas?
¿Como trabajar con DLL’s? ¿Y con BPL’s? ¿Cual es mejor? ¿Es lo mismo?

Y muchas otras relacionadas con los elementos que aparecen en el título de esta entrada.

Mi idea en este caso es, intentar aclarar algunos de estos términos y las diferentes formas de generar un programa desde Delphi (utilizando DLL’s y BPL’s -packages-). Otra cosa, es que lo consiga…  ;-D

Antes de comenzar, debemos aclarar algunos términos o elementos que debemos tener claros antes de continuar.

——————————————————————————————————————————————-

BWRP = «Build with runtime packages»

Esta opción se encuentra en las «Opciones de proyecto/Packages» y con ella conseguimos:

  • ACTIVADA: El programa no incluye las librerías (normalmente la VCL y componentes de terceros) en el EXE; El ejecutable es más pequeño, pero necesita de los packages (BPL’s) de la VCL; Al distribuir la aplicación se deben copiar también esos ficheros (los necesarios -NOTA1-) y los packages que hayamos creado nosotros.
  • DESACTIVADA: El programa incluye en el EXE los packages de librerías y componentes de terceros. En un único fichero va todo incluído. El EXE tiene mayor tamaño (lógico porque todo va incluído).

NOTA1: Se puede saber qué librerías (DLL’s o BPL’s) necesita un ejecutable utilizando herramientas como «Dependency Walker», GExperts o CnWizards.

DLL = «Dinamic Link Libray / Librería de enlace dinámico»

Son ficheros con código ejecutable, que se utilizan en sistemas Windows, y que forma parte de otras aplicaciones o del propieo sistema operativo. Permiten hacer que los programas sean más pequeños y que el código que contienen pueda ser usado por diferentes aplicaciones.

BPL = «Borland Package Library»

En pocas palabras podemos decir que una BPL es una DLL sólo para Delphi. Las BPL’s son más potentes que las DLL’s. Permiten almacenar más información fFunciones, procedimientos, Units, pero también Clases, Objetos y Componentes). La mayor potencia de estos ficheros se «paga» con la exclusividad de poderlos utilizar únicamente en Delphi.

Carga estática de librerías

Tanto para DLL’s como para BPL’s, cuando hablamos de carga estática, nos referimos a que al ejecutarse el programa, se carga automáticamente esa librería. Aunque en ese momento no se vaya a utilizar el código que hay dentro de la librerías, esta se carga al iniciar. Si la librerías no existe en disco el programa falla durante la carga y no es posible ejecutarlo.

Carga dinámica de librerías

La carga dinámica de packages, tiene como contrapartida que es más compleja (programáticamente) que la carga estática. La carga se debe hacer de forma expresa por el programador cuando se dese. Esto significa que sólo se cargará esa librería cuando se necesite. El programa puede funcionar aunque no esté (el programador deberá comprobarlo antes de cargarla). Este sistema hace que este tipo de carga sea ideal para sistemas con plugins/Addins.

——————————————————————————————————————————————-

Una vez revisado esto, voy a intentar explicar las diferencias al crear un proyecto, segun si utilizamos DLL’s o BPL’s y carga estática/dinámica.

EXE + DLL con Carga estática(El EXE puede ser con o sin BWRP)

Tal y como hemos dicho, la DLL debe existir en disco cuando se ejecuta la aplicación, de otra forma esta no funcionaría y fallaría al arrancar.
Hay que definir dónde se encuentra la DLL (en el que la carga -el EXE-)

function Sumar(x,y:integer):Integer; stdcall; external 'sumas.dll';

Y con eso ya se puede hacer la llamada:

Res := Sumar(4,5);

EXE + DLL con Carga dinámica(El EXE puede ser con o sin BWRP)

En este caso el programa puede funcionar aunque la DLL no exista; En el momento de intentar cargarla se debe comprobar si está o no en disco. Se puede cargar cuando se necesita y descargarla cuando ya no es útil. La carga es un poco más compleja que el caso anterior.

Se utiliza LoadLibrary para cargar la DLL en el momento en que se necesita:

H := LoadLibrary('SUMAS.DLL');

Para acceder a las funciones de la DLL se usa GetProcAddress:

@sum := GetProcAddress(H, 'Sumar2');

En el programa que hace la llamada (el EXE) debe estar predefinido el tipo de la función a la que vamos a realizar la llamada:

Sum: function(x,y:Integer):integer; stdcall;

EXE + BPL con Carga estática(El EXE puede ser con o sin BWRP)

En este caso no hace falta definir nada referente a la función que se encuentra en el package, simplemente hacer referencia a ella en diseño (añadiendo la unit al USES).

Si el programa principal (EXE) se compila con «BRWP Desactivado» todo va dentro del EXE (componentes de terceros y código de la VCL); Si se compila con «BWRP activado» la VCL y los packages de terceros van aparte; Estos packages se deberán distribuir junto al EXE.

Utilizando carga estática es obligatorio que exista el package en disco y que esté accesible al arrancar la aplicación; Si la BPL no existe el programa no puede funcionar.

EXE + BPL con Carga dinámica + RTTI(El EXE debe ser con BWRP)

Es el caso más potente que tenemos. Cargar el package (hemos dicho que las BPL’s son más potentes que las DLL’s) de forma dinámica. Para ello hacemos uso de RTTI (Runtime Type Information) que es lo que le da la potencia extra a las BPL’s. Es obligatorio en este caso que «BWRP esté activado»; Es decir, obligatoriamente debemos distribuir nuestra aplicación con packages.

Los packages se cargan de forma dinámica con LoadPackage (de forma similar a cómo se hace con LoadLibrary):

H := LoadPackage('RESTAS.BPL');

Podemos acceder a las funciones de la misma forma que lo hacemos con las DLL’s, utilizando GetProcAddress:

@resta := GetProcAddress(H, 'Resta');

Podemos utilizar RTTI para acceder a classes que previamente se han registrado (con RegisterClass) utilizando funciones de RTTI como GetClass:

AClass := GetClass('TFormMain');

Y podemos acceder a métodos de las clases utilizando también RTTI, utilizando MethodAddress:

// Acceder al método
Routine.Data := Pointer((F as FormClass));
// Ejecutar al código
Routine.Code := (F as FormClass).MethodAddress('ExecForm');

No es necesario definir nada en el programa principal, puesto que podemos obtener todo la información de las clases definidas en el package utilizando RTTI.

Quedaría una última combinación, posible, pero un poco extraña, que sería EXE sin «runtime packages» y con carga dinámica. Y digo que es un poco rara, porque es un poco contracorriente. Utilizar BPL’s tiene la ventaja que le confiere utilizar RTTI, sin ella los BPL’s pasan a ser DLL’s normales. Esta opción haría justo eso, utilizar BPL’s, pero sin RTTI.

EXE + BPL con Carga dinámica sin RTTI(El EXE sin BWRP)

Esta opción (un poco rara) sería la de utilizar una aplicación con «BRWP Desactivado» (es decir TODO en un único EXE) y cargar en él package de forma dinámica. Esto es posible, pero en este caso perdemos la opción de utilizar RTTI; El EXE (fichero único) podría funcionar sin que exista la BPL que vamos a cargar. Al no poder utilizar RTTI, las «mejoras» que tienen las BPL’s se pierden y pasan a usarse como DLL’s.

Se pueden cargar las BPL’s de forma dinámica con LoadPackage:

H := LoadPackage('RESTAS.BPL');

Y para acceder a las funciones usamos GetProcAddress (como en las DLL’s):

@resta := GetProcAddress(H, 'Resta');

Por supuesto si intentamos acceder a información de clases (GetClass) el programa falla.
Más o menos hasta aquí las diferentes opciones de compilar el proyecto y algunas referencias a cómo hacer las llamadas y las definiciones.

Todo ello está implementado en los ejemplos que ajunto al final de esta entrada. Hay un grupo de proyectos (compilado con Delphi 6) que incluye los siguientes ficheros:

  • sumas.dpr: Proyecto que genera un DLL con una función de suma
  • Call_suma_estat.dpr: Proyecto para llamar a una DLL con carga estática.
  • Call_suma_dinam.dpr: Proyecto para llamar a una DLL con carga dinámica.
  • restas.dpk: Proyecto para generar una BPL con una función de resta y un formulario.
  • Call_resta_estat.dpr: Proyecto para llamar a la BPL con carga estática.
  • Call_suma_dinam.dpr: Proyecto para llamar a la BPL con carga dinámica (uso de RTTI y proyecto compilado con Runtime Packages).
  • Call_resta_dinam_sin_pack.dpr: Proyecto para llamar a la BPL con carga dinámica, pero sin compilación con packages (sin uso de RTTI).

El Grupo de proyectos completo se puede descargar desde aquí.

[Link descarga código fuente]

AÑADIDO: El otro día me dejé sin poner algunas referencias que tengo sobre el tama y que me parecen interesante, así que ahí van:

Categories: OOP Tags: , ,