Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   INFO: DLL's, BPL's, carga dinámica, carga estática y Packages en Runtime (https://www.clubdelphi.com/foros/showthread.php?t=68947)

Neftali [Germán.Estévez] 16-07-2010 13:07:27

INFO: DLL's, BPL's, carga dinámica, carga estática y Packages en Runtime
 
Es un tema que ya ha salido otras veces, y son muchas "palabrejas"; Al principio (a mi me pasó también) uno se lía un poco, así que ya que he escrito la explicación para un hilo concreto de estos foros, creo que es mejor publicarla aquí por si a alguien más le puede interesar.


DLL's, BPL's, carga dinámica, carga estática y Packages en Runtime

Sí ya se que el título es un poco largo... ;-)
Para aclarar un poco las cosas intentare explicar todos los casos que a mí se me ocurren, cómo configurarlos y lo que se puede hacer con ellos, de forma rápida; Aclararemos antes la opción "Build With Runtime Package" imprescindible para TODO esto:

·······························································································
BWRP = "Build with runtime packages"
Esta opción se encuentra en las "opciones de proyecto" 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 (BPLs) de la VCL; Al distribuir la aplicación se deben copiar también esos ficheros (los necesarios).
* DESACTIVADA: El programa incluye en el EXE los packages de librerías y componentes de terceros. En 1 sólo fichero va todo incluído. El EXE tiene mayor tamaño (lógico porque todo va incluído).
·······························································································


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

La DLL debe existir cuando se ejecuta la aplicación, de otra forma esta no funcionaría.
Hay que definir dónde se encuentra la DLL (en el que la carga) y con eso ya se puede hacer la llamada:

Código Delphi [-]
function Sumar(x,y:integer):Integer; stdcall; external 'sumas.dll';

Código Delphi [-]
Res := Sumar(4,5);

* EXE + DLL 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. Se utiliza para programas que cargan plugins o similares. La carga es un poco más compleja.
Se utiliza LoadLibrary para cargar la DLL en el momento en que se necesita:

Código Delphi [-]
  H := LoadLibrary('SUMAS.DLL');

Para acceder a las funciones de la DLL se usa GetProcAddress:
Código Delphi [-]
  @sum := GetProcAddress(H, 'Sumar2');

Tiene que estar predefinido el tipo de la función en el programa donde se llama:

Código Delphi [-]
  Sum: function(x,y:Integer):integer; stdcall;


* EXE + BPL 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.
Si el programa se compila con "BRWP Desactivado" TODO va dentro del EXE. Si se compila con "BWRP activado" la BPL va aparte, pero es obligatorio que exista, ya que la carga se ha hecho de forma estática (en diseño hemos añadido un USES); Si la BPL no existe el programa no puede funcionar.
Para trabajar así basta con añadir al USES las units del packages que necesitemos.

* EXE + BPL carga dinámica + RTTI(El EXE debe ser con BWRP)
Es el caso más potente que tenemos. Cargar BPL's (que 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 los BPL's. En 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:
Código Delphi [-]
  H := LoadPackage('RESTAS.BPL');

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

Código Delphi [-]
  @resta := GetProcAddress(H, 'Resta');

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

Código Delphi [-]
    AClass := GetClass('TFormMain');

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

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

* EXE + BPL carga dinámica sin RTTI(El EXE puede ser sin BWRP)
Hay una última opción (un poco rara), que sería uriliar un EXE 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. Esta opción, aunque es viable, como digo, no tiene mucho sentido, porque utiliza las BPL como si se trataran de DLL; 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:
Código Delphi [-]
  H := LoadPackage('RESTAS.BPL');

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

Código Delphi [-]
  @resta := GetProcAddress(H, 'Resta');

Por supuesto si intentamos acceder a información de clases (GetClass) el programa falla.

Espero que este "breve" ;) explicación haya servido para aclarar alguna cosa. Si tengo un hueco itentaré colgar los ejempos correspondientes a cada caso.

Un salduo.

roman 16-07-2010 13:27:02

Una pregunta:

Cuando activas la opción "Buld with runtime packages", ¿qué papel juegan los paquetes listados en el cuadro de edición adjunto?

// Saludos

Neftali [Germán.Estévez] 16-07-2010 14:19:50

Con ese Edit estás obligando (o indicando) a que ese package realmente sea un package en Runtime; No se van añadir al programa principal.

Imagina la siguiente situación; En un mismo directorio tienes los ficheros del package (Restas.BPL) y del Ejecutable (EXE).
Borras todos los ficheros compilados (DCU, DCP, EXE, BPL) del directorio.





Eliminamos el package RESTAS.BPL, de la lista de "Runtime Packages" con lo que estamos diciendo que "no es un package de Runtime" o no le estamos diciendo expresamente que lo sea:



Si ahora compilamos nuestro ejecutable, como en ningun sitio le hemos indicado que ese RESTAS.BPL es un "Runtime Package", lo que delphi hace (ya que encuentra los fuentes) es añadirlas al ejecutable.
Para comprobarlo vemos que en disco sólo se genera el fichero del ejecutable y si lo revisamos con alguna herramienta para ver recursos, se puede ver que el formulario que estaba definido dentro del package (UMain.pas con la clase TFormMain) se ha añadido al ejecutable:



Se ha añadido al EXE porque en nungun sitio se le ha especificado que los elementos pertenecientes a ese package deben ser tratados como tales.

Volvemos a borrar TODOS los ficheros compilados y probamos la segunda opción; Añadir a la lista de "packagesa en runtime" nuestro package "RESTAS.BPL".



Al intentar compilar ahora el ejecutable, vemos que ahora no añade esos elementos al EXE, sino que COMPILA primero el package RESTA.BPL (sin haberselo dicho explícitamente -lo saca de esa lista-) y posteriormente compila el EXE. Si miramos ahora el EXE vemos que no incluye los formularios que están en el package:



Determina qué packages en runtime va a utilizar tu aplicación, por lo tanto obligas a que esos packages estén generados y disponibles (DCP) en el momento de compilar tu EXE. En ese momento comprueba que en esos packages esta lo que tú necesitas (Units añadidas al uses) y compila tu EXE.
Lo que no está en esos packages, al encontrarlo se incluye en tu EXE.

Caral 16-07-2010 14:28:43

Hola
Muy interesante tema.
Hacia falta algo asi, ademas muy bien explicado.
Gracias por el aporte.
Saludos

urixiv 16-07-2010 18:03:08

Gran explicación sobre Bpls, Dlls, y exes!!!
Siempre va bien poderlo leer todo del tirón para entender bien como funciona!!

GRACIAS

roman 16-07-2010 21:36:01

Cita:

Empezado por Neftali (Mensaje 370622)
Con ese Edit estás obligando (o indicando) a que ese package realmente sea un package en Runtime; No se van añadir al programa principal.

Algo así tenía idea. Pero, entonces, si de dicha lista quitamos vcl, en teoría, podríamos generar un ejecutable en el que sólo nuestros paquetes personales sean dinámicos, evitando así tener que redistribuir la vcl.

Lo curioso es que si quitas todos los paquetes de la lista, la opción "Build with runtime packages" se desmarca sola. No he probado si agregamos un paquete personal y quitamos la vcl si realmente sucede lo que digo.

// Saludos

rgstuamigo 16-07-2010 22:11:39

Pues yo estaba casi nulo :o en éstos asuntos de "carga dinámica y estática de packages y dll", pero ahora con la super explicacion de Neftali pues ya le voy entendiendo a la cosa.:D.
Muchas gracias Neftali por dar tanto de tu conocimiento.;):D.
Saludos...:)

Casimiro Notevi 16-07-2010 22:17:30

Yo sólo conocía la parte de los bpl porque hace años nos encontramos que nuestro .exe era tan grande que los win98 y winMe no los podía ejecutar, así que lo compilamos con las bpl aparte y con el tamaño resultante del .exe ya sí que pudimos ejecutarlo en esos sistemas.
Sin embargo con winNT y 2000 sí que funcionaban con "todo dentro".

Neftali [Germán.Estévez] 19-07-2010 11:22:34

Cita:

Empezado por roman (Mensaje 370671)
Pero, entonces, si de dicha lista quitamos vcl, en teoría, podríamos generar un ejecutable en el que sólo nuestros paquetes personales sean dinámicos, evitando así tener que redistribuir la vcl.

En teoría sí, si eliminamos todos los DCPs y BPL's y tenemos disponible el código fuente, debería añadirlos.

Cita:

Empezado por roman (Mensaje 370671)
Lo curioso es que si quitas todos los paquetes de la lista, la opción "Build with runtime packages" se desmarca sola. No he probado si agregamos un paquete personal y quitamos la vcl si realmente sucede lo que digo.

Si dejas un sólo paquete lo mantiene, es al dejarlo vacío que lo desactiva.

Neftali [Germán.Estévez] 19-07-2010 14:29:25

2 Archivos Adjunto(s)
Lo prometido es deuda, así que he completado la entrada en mi blog, y allí mismo podéis encontrar el código de los ejemplos que os adjunto a este mensaje.

ContraVeneno 27-07-2010 00:39:52

Cita:

Empezado por Neftali (Mensaje 370622)
...Al intentar compilar ahora el ejecutable, vemos que ahora no añade esos elementos al EXE, sino que COMPILA primero el package RESTA.BPL (sin haberselo dicho explícitamente -lo saca de esa lista-) y posteriormente compila el EXE....

Muy buen tema, Gracias Neftalí.

Solo aclarar, que se compilaría pimero el BPL, solo si este tiene seleccionada la opción de "reconstruir cuando sea necesario" ("Rebuild as needed"). Si se tiene la opción de "reconstrucción explícita" ("Explicit rebuild") el BPL no tendría porque compilarse cada vez que se compile el proyecto donde se le requiera.

Saludos.

Neftali [Germán.Estévez] 27-07-2010 11:00:14

Cita:

Empezado por ContraVeneno (Mensaje 371683)
Solo aclarar, que se compilaría pimero el BPL, solo si este tiene seleccionada la opción de "reconstruir cuando sea necesario" ("Rebuild as needed"). Si se tiene la opción de "reconstrucción explícita" ("Explicit rebuild") el BPL no tendría porque compilarse cada vez que se compile el proyecto donde se le requiera.

Correctísima aclaración ContraVeneno. ;)
En mi caso estaba dando por supuesto que el package no existía, de ahí que asumía siempre se generara.

ecfisa 29-07-2010 07:50:13

Neftalí:

Muy esclarecedor el post.

Gracias por compartirlo.:)

Saludos.

BlueSteel 23-05-2011 18:09:50

Que interesante esto.. me apundo para tenerlo a manos despues


Salu2:D

ElDuc 29-11-2013 13:13:20

Hola, en realidad no sé si alguien verá este post, ya que el hilo se creó hace 9 años.

Bueno por si lo veis, comentaros que ahora yo estoy con este lio de los paquetes y que me estoy volviendo loco, trabajo con D2010, por si sirve de algo.

La explicación de Neftali está meridianamente clara y creo que la comprendo bien, pero cuando lo hago me sales esto:
" ..\Prueba1.exe no es una aplicación Win32 válida"

Alguien me puede ayudar?, muchas gracias.

ElDuc 29-11-2013 14:46:14

Perdonad, he pensado que si ponía toda la información sería más fácil que alguien me ayudara.

Paso 1:
Realizo un proyecto normal, Aquí pongo el project manager para que podáis ver el contenido del proyecto:


El código de las 2 unidades es el siguiente:
Código Delphi [-]
Unit uForm1;

Interface

Uses
    uForm2,
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls;

Type
    TForm1= Class(TForm)
        Button1: TButton;
    procedure Button1Click(Sender: TObject);
    Private
        { Private declarations }
    Public
        { Public declarations }
    End;

Var
    Form1: TForm1;

Implementation

{$R *.dfm}

Var
    Form2:TForm2;

procedure TForm1.Button1Click(Sender: TObject);
begin
Form2:=TForm2.Create(Application);
Form2.ShowModal;
Form2.Free;
end;

End.

Código Delphi [-]
unit uForm2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

end.

Selecciono en las opciones para que el Form2 no se cree automáticamente


Compilo y ejecuto y todo va bien, me aparece


Y cuando pulso el botón se abre el formulario 2


Guardo y cierro todo y creo el paquete


Compilo (build) y lo guardo, ahora vuelvo a abrir el proyecto original y cambio para que utilice el paquete


Guardo, compilo y ejecuto y me sale esto


No sé como solucionarlo, Help me!

Neftali [Germán.Estévez] 29-11-2013 16:05:03

¿Puedes meter todos los fuentes en un fichero ZIP y subirlo para que lo probemos?
A ver si hay algo raro....

ElDuc 29-11-2013 17:00:30

Hola Neftali,

Perdona pero no sé cómo se suben los archivos, ¿puedes indicarme cómo se hace?

Gracias y disculpa las molestias.

ContraVeneno 29-11-2013 20:10:23

puedes poner el archivo en dropbox, skydrive, googledrive, rapidshare o cualquier otro servicio de archivos, y aquí nos compartes el enlace para poder descargarlo.

ElDuc 29-11-2013 20:51:37

Hola otra vez, he tardado porque me he tenido que registrar y ver como funciona.

Aquí está el enlace
https://www.dropbox.com/s/2vg5au2w8xmave9/Fonts.ZIP

Gracias por tu ayuda.

Neftali [Germán.Estévez] 30-11-2013 14:15:45

Para evitar confusiones, al package le he añadido un form nuevo (form3) y he puesto cada proyecto en un directorio.
El proyecto queda con el form1 y el form2.

También es bueno que los coloque en diferentes directorio para asegurarte de que trabajas con packages y de que el EXE no te coge directamente los .PAS o
.DCU

1) El proyecto (sólo el EXE) funciona sin problemas
2) He redirigido los directorios para que genere el BPL y el DCP en el mismo directorio del EXE
3) Añades al EXE el packages desde las opciones y marcas que compile con Runtime packages.
4) Ahora ya puedes añadir al uses el nuevo uform3 (sin que esté en el directorio ni en el path); Debe encontrarlo en la BPL del package.
5) compilas y ejecutas.

Los proyectos:

https://www.dropbox.com/s/83scrxus9p...ckage_solo.zip (package solo)
https://www.dropbox.com/s/nmzdluezdu...ueba1_solo.zip (exe solo)
https://www.dropbox.com/s/9erbflk86i...on_package.zip (exe con el package)
https://www.dropbox.com/s/ajewxkrvy8p4rg7/TODO.zip (todo los ficheros)

Un saludo.

ElDuc 30-11-2013 18:15:06

!Ostras vaya faenada¡

Muchísimas gracia Neftali, voy a probarlo todo y te comento.

ElDuc 30-11-2013 19:34:17

Neftali, ya probé todo y creo que sigo siendo un poco inútil, te comento.

1. Prueba 1_solo.
Funciona correctamente pero, el paquete, ¿está en el directorio "Prueba 1_solo" por alguna razón?

2. Package_solo.
Me compila bien, pero tal como comentas he tenido que cambiar los directoros de salida porque el bpl me lo mandaba a "C:\Users\Public\Documents\RAD Studio\7.0\Bpl" y el dcp me lo mandaba a "C:\Users\Public\Documents\RAD Studio\7.0\Dcp"

Ahora ya tengo loa archivos en mi carpeta, pero si cambias los directorios de salida, ¿has de cambiar algún directorio de búsqueda para que encuentre los paquetes?, lo digo porque da la sensación que los directorios de "...\Rad Studio\..." de alguna manera los encuentra pero ¿cómo encontrará un programa un paquete en un directorio diferente?

3 . Pruaba 1_con_package.
Si ejecuto el Exe que viene en la carpeta me da un error conocido "... porque no se encontró rlt60.bpl ..." ".

Cuando he intentado compilar, me ha dado este error: "[DCC Fatal Error] uForm1.pas(8): F1026 File not found: 'uform3.dcu'", y me ha señalado la unidad uForm3 de la cláusula Uses.

He incluido el paquete en el edit "Build with runtime packages" mediantes el botón [...] y ya no me da este error, pero me vuelve a dar el error que ya te comenté "... win32 valid"

4. TODO.
Me pasa todo lo que ya he comentado.

¡Parece que los dichoso paquetes no quieren saber nada de mi!

Neftali [Germán.Estévez] 01-12-2013 17:16:02

Cita:

Empezado por ElDuc (Mensaje 470300)
¿cómo encontrará un programa un paquete en un directorio diferente?

Es importante que esté en un directorio diferente para que no te encuentre los fuentes de la unidad form3 (o los DCUs) y así te asegures de que está cogiendo el package.
El que encuentre el package correcto es porque se lo indicarás tú (seleccionandolo con el botón) en las opciones de configuración. Aunque en la lista sólo aparezca el nombre es importanto por el path.

Cita:

Empezado por ElDuc (Mensaje 470300)
Si ejecuto el Exe que viene en la carpeta me da un error conocido "... porque no se encontró rlt60.bpl ..." ".

Eso es normal, porque yo lo he compilado con D6 y veo que tú estás con otra versión. No podremos probarlo así, a no ser que de algún otro programa que tengas instalados copies esos packages al directorio.

Cita:

Empezado por ElDuc (Mensaje 470300)
Cuando he intentado compilar, me ha dado este error: "[DCC Fatal Error] uForm1.pas(8): F1026 File not found: 'uform3.dcu'", y me ha señalado la unidad uForm3 de la cláusula Uses.

No te está encontrando el package que te hace falta (BPL). Debe estar en el mismo directorio, en un directorio del path o donde le digas tú a través de las opciones.

Cita:

Empezado por ElDuc (Mensaje 470300)
He incluido el paquete en el edit "Build with runtime packages" mediantes el botón [...] y ya no me da este error, pero me vuelve a dar el error que ya te comenté "... win32 valid"

Ahora ya te lo está encontrando.
Lo que ya no se, es el porqué del otro error. Tal vez esté generando mal los ficheros. ¿?¿?¿

Cita:

Empezado por ElDuc (Mensaje 470300)
¡Parece que los dichoso paquetes no quieren saber nada de mi!

Alguna razón lógica ha de tener. :confused:
¿Puedes pasar el exe y el package compilado?

ElDuc 03-12-2013 15:07:51

De nuevo por aquí,

Perdonad, pero al final toqueteando toqueteando se me fundió el D2010 y lo he tenido que desinstalar y volver a instalar.

Lo he hecho varias veces, porque no soy capaz de eliminarlo completamente, es decir he desinstalado y borrado todo lo que huele a delphi, lo vualvo a instalar y aún hay cosas de la instalación anterior, como son pestañas de componentes y directorios de las options.

Ya he instalado 2 veces y no soy capaz de hacer una instalación "virgen", es decir sin nada de las instalaciones anteriores, no sé si me podéis ayudar primero a hacer esto porque el D210 ahora no me funciona correctamente y yo creo que es porque precisamente lo que no soy capaz de eliminar me sigue dando problemas. Por ejemplo tengo un paquete con 2 componentes que me funcionaban perfectamente y ahora, una vez los vuelvo a instalar y los pongo en un proyecto nuevo al compilar me pide las DCUs de los componentes, no comprendo nada.

Por cierto Neftali, he vuelto a entrar en el hilo original porque parece que en un momento de la conversación respondí de forma incorrecta y se generó como un hilo privado, no era mi intención.

Os agradezco la ayuda que me estáis prestando y os pido disculpas si soy muy patán, pero me gustaría superar esos problemas y continuar avanzando.

Neftali [Germán.Estévez] 04-12-2013 09:22:16

Teóricamente, si realizas la deinstalación desde el panel de control o desde el propio instalador del programa, luego borras todo rastro que quede en Archivos de programa (o program files), lo que quede en Documents & Setting y eliminas las claves de registro, no debería quedar rastro de Delphi.

ElDuc 13-12-2013 19:29:55

Hola, ya he vuelto.

Disculpad que haya desaparecido otra vez, pero no os podéis imaginar la aventura (desventura), trasteando, trasteando, al final me cargué el registro y he tenido que reinstalar el SO y todo el software.

Al final lo único que pretendía, era desinstalar completamente el D2010, pero es mucho más complicado de lo que yo pensaba porque deja mierda por todas partes.

Neftali, ahora ya me funciona todo el tema de los paquetes correctamente.

Muchas gracias por la colaboración.


La franja horaria es GMT +2. Ahora son las 16:28:49.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi