Mostrando entradas con la etiqueta componentes. Mostrar todas las entradas
Mostrando entradas con la etiqueta componentes. Mostrar todas las entradas

17 julio 2009

El componente BackwroundWorker (y 2)

Vamos a ver un ejemplo de cómo utilizar este componente con un hilo de ejecución que recorra todos los números del 1 al 100 y nos calcule los múltiplos de 5, 7 y 9. Este sería el formulario principal de nuestro proyecto:


El formulario se compone de los siguientes componentes:

- 2 botones de la clase TButton llamados BComenzar y BCancelar para iniciar o detener el hilo de ejecución.

- 1 componente TBackgroundWorker llamado BackgroundWorker.

- 1 barra de progreso de la clase TProgressBar llamada Progreso y con su propiedad Smooth a True.

- 3 etiquetas (TLabel) y 3 casillas (TEdit) llamadas Multiplos5, Multiplos7 y Multiplos9 donde se irán alojando los múltiplos encontrados de cada uno.

- 1 barra de estado de la clase TStatusBar llamada Estado con su propiedad SimplePanel a True.

Comencemos a meter código:

1º Al pulsar el botón Comenzar ponemos en marcha el hilo:

procedure TForm1.BComenzarClick(Sender: TObject);
begin
BackgroundWorker.Execute;
end;

2º Al pulsar el botón Cancelar le pedimos al hilo que se detenga:

procedure TForm1.BDetenerClick(Sender: TObject);
begin
BackgroundWorker.Cancel;
end;

3º En el evento OnWork del componente BackgroundWorker recorremos los 100 números:

procedure TForm1.BackgroundWorkerWork(Worker: TBackgroundWorker);
var
i: Integer;
begin
for i := 1 to 100 do
begin
// ¿Hay que cancelar el proceso?
if Worker.CancellationPending then
begin
// Le indicamos al hilo que lo cancelamos
Worker.AcceptCancellation;
Exit;
end;

// ¿Es múltiplo de 5?
if i mod 5 = 0 then
Worker.ReportFeedback(5, i);

// ¿Es múltiplo de 7?
if i mod 7 = 0 then
Worker.ReportFeedback(7, i);

// ¿Es múltiplo de 9?
if i mod 9 = 0 then
Worker.ReportFeedback(9, i);

// Esperamos 50 milisegundos
Sleep(50);

// Incrementamos la barra de progreso
Worker.ReportProgress(i);
end;
end;

Al principio del bucle controlamos si nos piden abandonar el hilo de ejecución:

// ¿Hay que cancelar el proceso?
if Worker.CancellationPending then
begin
// Le indicamos al hilo que lo cancelamos
Worker.AcceptCancellation;
Exit;
end;

Después comprobamos los múltiplos de cada número y en el caso de que así sea envío provoco un evento OnWorkFeedBack para notificar el número que he encontrado:

// ¿Es múltiplo de 5?
if i mod 5 = 0 then
Worker.ReportFeedback(5, i);

// ¿Es múltiplo de 7?
if i mod 7 = 0 then
Worker.ReportFeedback(7, i);

// ¿Es múltiplo de 9?
if i mod 9 = 0 then
Worker.ReportFeedback(9, i);

Y por último provoco un pequeño retardo de 50 milisegundos (para ir viendo el progreso) y provoco un evento OnWorkProgress para notificar el incremento en la barra de progreso:

// Esperamos 50 milisegundos
Sleep(50);

// Incrementamos la barra de progreso
Worker.ReportProgress(i);

Sigamos...

4º En el evento OnWorkFeedBack recogemos el mensaje que nos manda el hilo para ir guardando en cada casilla los múltiplos de cada número:

procedure TForm1.BackgroundWorkerWorkFeedback(Worker: TBackgroundWorker;
FeedbackID, FeedbackValue: Integer);
begin
case FeedbackID of
5: Multiplos5.Text := Multiplos5.Text + IntToStr(FeedbackValue) + ',';
7: Multiplos7.Text := Multiplos7.Text + IntToStr(FeedbackValue) + ',';
9: Multiplos9.Text := Multiplos9.Text + IntToStr(FeedbackValue) + ',';
end;
end;

5º En el evento OnWorkProgress actualizamos la barra de progreso y la barra de estado en pantalla:

procedure TForm1.BackgroundWorkerWorkProgress(Worker: TBackgroundWorker;
PercentDone: Integer);
begin
Progreso.Position := PercentDone;
Estado.SimpleText := 'Procesando... ' + IntToStr(PercentDone) + '%';
end;

6º En el evento OnWorkComplete le decimos al usuario en la barra de progreso que hemos terminado:

procedure TForm1.BackgroundWorkerWorkComplete(Worker: TBackgroundWorker;
Cancelled: Boolean);
begin
Estado.SimpleText := 'Proceso finalizado';
end;

7º Por si acaso al usuario le da por cerrar el formulario mientras está el hilo en marcha tenemos que cancelarlo y esperar a que termine (al igual que hacen los programas como Emule o BitTorrent cuando lo cerramos y espera a liberar memoria y cerrar las conexiones):

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// ¿Esta el hilo en marcha?
if BackgroundWorker.IsWorking then
begin
// Le indicamos que se detenga
BackgroundWorker.Cancel;
// Esperamos a que se detenga
BackgroundWorker.WaitFor;
end;
end;

Al ejecutar el programa irá rellenando las casillas correspondientes:


Hasta finalizar el proceso:


Como podemos apreciar, este componente nos facilita mucho la labor con los hilos de ejecución respecto a la clase TThread. Por las pruebas que he realizado en otros programas más complejos (facturación) es bastante sencillo de manejar y muy estable.

Pruebas realizadas en RAD Studio 2007.

03 julio 2009

El componente BackgroundWorker (1)

Aunque haya finalizado el tema referente a los hilos de ejecución con la clase TThread no está demás hablar de otros componentes no oficiales de Delphi que también están relacionados con los hijos de ejecución.

En este caso el componente me lo recomendó un lector de este blog (gracias a Jorge Abel) y me puse a echarle un vistazo a ver que tal. Este componente puede descargarse gratuitamente de esta página web (licencia Freeware):

http://www.delphiarea.com/products/

Concretamente en la sección de componentes que viene más abajo:


Al igual que la clase TThread, este componente permite crear un hilo de ejecución paralelo al hilo primario de nuestra aplicación pero de una manera sencilla y sin tener que heredad de clases.

INSTALAR EL COMPONENTE

Como es un componente que va suelto (sin paquete) vamos a proceder a crear un paquete para el sólo. Para ello creamos una carpeta llamada DephiArea donde vamos a descomprimir los archivos del componente:


Ahora nos vamos a Delphi y seleccionamos File -> New -> Package – Delphi for Win32. Aparecerá esto a la derecha en la ventana del proyecto:


Ahora seleccionamos File -> Save all y nos vamos a la carpeta donde hemos descomprimido el componente, por ejemplo:

D:\CodeGear\RAD Studio\5.0\Componentes\DelphiArea\

Y guardamos el proyecto con el nombre DelphiArea.dproj. Volvemos a la ventana del proyecto y pinchamos el nombre del mismo con el botón derecho del ratón y seleccionamos Add:


En esta ventana que aparece pulsamos el botón Browse y elegimos el archivo BackgroundWorker.pas:


Ahora ya sólo tenemos que seleccionar Compile:


Y después Install:


Si todo ha ido bien mostrará este mensaje:


Si creamos un nuevo proyecto y nos fijamos en la paleta de componentes veremos el nuevo componente que hemos instalado:


Ya estamos listos para comenzar a trabajar con el mismo.

LAS CARACTERÍSTICAS DEL COMPONENTE BACKGROUNDWORKER

Insertamos el componente en el formulario donde queremos lanzar el hilo de ejecución:


Si nos fijamos en el inspector de objetos veremos que no se han estresado añadiendo propiedades:


Aunque mediante código tenemos acceso a estas variables:

CancellationPending: esta bandera booleana es utilizada para indicarnos que debemos abandonar el bucle principal del hilo (el evento OnWork que veremos más adelante). Por defecto está a False. Se pondrá a True cuando llamemos al método Cancel.

IsCancelled: variable booleana que nos dice si el hilo ha sido cancelado.

IsWorking: indica si el hilo se sigue ejecutando con normalidad.

ThreadID: es un número entero con el identificador que Windows ha asignado a nuestro hilo. Podemos utilizar esta propiedad para llamar a otras funciones de la API de Windows relacionadas con los hilos y procesos.

Y para modificar su comportamiento tenemos estos métodos:

procedure Execute;

Comienza la ejecución del hilo.

procedure Cancel;

Solicita al hilo que detenga la ejecución, es decir, pone la variable CancellationPending a True para que seamos nosotros los que abandonemos el bucle cerrado del evento OnWork.

procedure WaitFor;

Espera a que termine la ejecución del hilo.

procedure ReportProgress(PercentDone: Integer);

A este método sólo podemos llamarlo dentro del evento OnWork. Lo que hace es realizar una llamada al evento OnWorkProgress para que situemos en el mismo lo que vamos a hacer para informar al hilo primario del porcentaje de trabajo que hemos realizado.

procedure ReportProgressWait(PercentDone: Integer);

Igual que el procedimiento anterior pero espera a que termine el código que hemos colocado dentro de OnWorkProgress.

procedure ReportFeedback(FeedbackID, FeedbackValue: Integer);

Al igual que los dos procedimientos anteriores, sólo podemos llamar al mismo dentro del evento OnWork. Realiza una llamada al evento OnWorkFeedBack para que podamos enviar mensajes al hilo primario.

procedure ReportFeedbackWait(FeedbackID, FeedbackValue: Integer);

Lo mismo que el procedimiento anterior pero espera a que termine la ejecución del código que hemos puesto en el evento OnWorkFeedBack.

procedure Synchronice(Method: ThreadMethod);

Sólo podemos llamar a este procedimiento dentro del evento OnWork. Le pasamos como parámetro el método del hilo principal (VCL) para que lo ejecute por nosotros, por ejemplo, para mostrar información en pantalla o incrementar una barra de progreso.

procedure AcceptCancellation;

Sólo podemos llamar a este procedimiento dentro del evento OnWork. Debemos llamar a este procedimiento cuando dentro del evento OnWork hemos visto que la variable CancellationPending es True y entonces abandonamos el bucle principal y llamamos a este procedimiento para informar a hilo que hemos aceptado salir del mismo.

Estos son los eventos que incorpora:


Veamos para que sirve cada evento:

OnWork: Este método equivale al procedimiento Execute de la clase TThread, de hecho, se ejecuta cuando llamamos al procedimiento Execute. Es aquí donde debemos introducir el bucle continuo que va a hacer el trabajo pesado. A cada ciclo del bucle debemos comprobar el valor de la variable CancellationPending para saber si debemos terminarlo. Una vez abandonemos el bucle debemos llamar al método AcceptCancellation que pondrá la variable IsCancelled a True.

OnWorkComplete: este evento se ejecutará cuando termine la ejecución del código que hemos puesto en el evento OnWork ya sea porque ha terminado su ejecución o porque lo hemos cancelado. Aquí podemos poner el código encargado de liberar los objetos creados o cerrar archivos abiertos.

OnWorkProgress: se ejecutará el código que introducimos a este evento cuando desde el evento OnWork hemos hecho una llamada a los procedimientos ReportProgress o ReportProgressWait. Desde aquí podemos informar al hilo primario de sobre cómo vamos.

OnWorkFeedBack: se ejecuta cuando nuestro hilo envía mensajes al hilo primario mediante los procedimientos ReportFeedBack o ReportFeedBackWait.

COMO INSTALARLO EN DELPHI 7

Para instalar este componente en Delphi 7 hay que seguir estos pasos:

1º Seleccionamos en el menú superior Install -> Component.

2º En la ventana que aparece seleccionamos la pestaña Into new Package.

3º Pulsamos el botón Browse del campo Unit file name y seleccionamos el archivo BackgroundWorker.pas.

4º Pulsamos el botón Browse del campo Package file name y escribimos DelphiArea.dpk.

5º En el campo Package Description escribimos Delphi Area.

Suponiendo que el componente lo tengo descomprimido en esta carpeta:

D:\Borland\Delphi7\Componentes\BackgroundWorker\

Quedaría la ventana de este modo:


Pulsamos el botón Ok y aparecerá esta mensaje:


Pulsamos el botón Yes y si todo ha ido bien aparecerá este mensaje:


Con esto ya tiene que aparecer arriba en la paleta de componentes:


En el próximo artículo veremos un ejemplo de cómo crear un hilo de ejecución utilizando este componente.

Pruebas realizadas en RAD Studio 2007.

28 noviembre 2008

Leyendo datos de hojas de cálculo de Microsoft Excel

Si hay algo que nos piden frecuentemente a los programadores de gestión es poder recoger o enviar datos a los programas ofimáticos más populares: Microsoft Word, Microsoft Excel y Microsoft Access.

Lo más común suele ser leer datos de una hoja de cálculo y guardarlos en nuestra base de datos, aunque también tenemos la posibilidad de enviarle la información para que Excel nos haga una gráfica espectacular.

Una hoja de cálculo es realmente un libro que puede contener una o más hojas de cálculo:


A su vez, Microsoft Excel puede abrir varios libros a la vez.

EL COMPONENTE TEXCELAPPLICATION

En la paleta de componentes tenemos el componente de la clase TExcelApplication que se encuentra en la sección Servers:


Lo insertamos en el formulario donde vamos a hacer la importación y lo llamamos Excel para simplificar:


Supongamos que queremos leer esta hoja de cálculo:


Primero abrimos la hora de cálculo:

Excel.Workbooks.Open( ExtractFilePath( Application.ExeName ) + 'Hoja.xls',
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, 0 );

Hay que reconocer que la cantidad de parámetros que tiene el método Open es impresionante:


Aunque no hay que asustarse porque lo que necesitamos realmente es el primer parámetro (el nombre del archivo excel a abrir). Los demás son para elegir si queremos abrirlo en modo sólo lectura, introducir una contraseña, etc.

Una vez que hemos abierto la hora de cálculo tenemos que situarnos en la Hoja1. Para ello creamos las siguientes variables:

var
i: Integer;
si: String;
Hoja: _WorkSheet;

Las variables i y si son para recorrer las celdas de la hoja de cálculo y la variable Hoja va a apuntar a una hoja en concreto. En nuestro caso la primera:

Hoja := Excel.Worksheets.Item[1] as _WorkSheet;

Para leer información de las celdas de la hoja de cálculo tenemos que conocer las coordenadas horizontales (A,B,C,…) y las verticales (1,2,3,…). Cuando lea la información la voy a volcar a un componente ListView configurado con estas columnas:


Ahora sólo queda ir recorriendo cada fila hasta que encontremos una fila vacía:

i := 2;
si := IntToStr( i );
repeat
with ListView.Items.Add do
begin
SubItems.Add( Hoja.Range['A'+si,'A'+si].Value2 ); // Código
SubItems.Add( Hoja.Range['B'+si,'B'+si].Value2 ); // Nombre
SubItems.Add( Hoja.Range['C'+si,'C'+si].Value2 ); // CIF
SubItems.Add( Hoja.Range['D'+si,'D'+si].Value2 ); // Saldo
end;

Inc( i );
si := IntToStr( i );
until ( VarType( Excel.Range['A'+si,'A'+si].Value2 ) = VarEmpty );

La propiedad Value2 de cada celda devuelve un tipo Variant, por ello utilizo la función VarType para comprobar si lo que hay en la celda esta vacío (VarEmpty). De ese modo sabemos cuando hemos terminado de leer datos.

Por último cerramos la hoja de cálculo con:

Excel.Workbooks.Close( 0 );

Con esto ya hemos conseguido traernos la información de la hoja de cálculo:


Igualmente podemos escribir datos en cualquier celda de la hoja de cálculo con total naturalidad:

Hoja.Range['B2','B2'].Value2 := ‘TRANSPORTES GARCIA, S.L.’;

De esta manera podemos crear nuestras plantillas de hojas de cálculo con gráficas incluidas y desde Delphi le enviamos la información.

Pruebas realizadas en RAD Studio 2007.

21 noviembre 2008

El componente ZipMaster

Si en el anterior artículo vimos como comprimir y descomprimir archivos con el componente ZipForge cuyo único inconveniente es que es comercial, hoy vamos a ver como hacer lo mismo con el componente ZipMaster, el cual es gratuito (con licencia LGPL).

El único inconveniente que tiene este componente es que necesita tener una librería dinámica al lado de nuestro ejecutable: DelZip179.dll. Según sus autores, podemos definir una directiva para añadir la librería de manera estática, aunque yo no he podido conseguirlo como comentaré más adelante.

DESCARGANDO EL COMPONENTE DE SU PAGINA WEB

La página oficial del componente ZipMaster es esta:

http://www.delphizip.org/


La última versión a fecha de este artículo es la 1.79. Soporta desde Delphi 5 hasta Delphi 2007.

Nos bajamos el archivo zm179setup1004.exe que tiene un tamaño de 3,07 MB y lo instalamos:


Después de instalarlo debemos instalar el paquete DPK en nuestra versión de Delphi habitual con los siguientes pasos:

1. Seleccionamos File -> Open y en el cuadro de búsqueda que aparece seleccionamos en el campo Tipo los archivos de tipo DPK:


2. En mi caso voy a instalar la versión ZipMaster11.dpk que corresponde a la versión 2007 de Delphi.

3. Seleccionamos el paquete abierto con el botón derecho del ratón y seleccionamos Install:


4. Si todo ha ido bien aparecerá este mensaje:


Una vez instalado debe aparecer en la paleta de componentes:


AÑADIENDO EL COMPONENTE A NUESTRO PROYECTO

Insertamos el componente ZipMaster en un formulario y lo llamamos Zip para abreviar:


También debemos vincular el directorio de búsqueda del proyecto al directorio donde se encuentra la unidad ZipMaster. Esto se hace seleccionando Proyect -> Options y en la sección Directories/Conditionals pulsamos el botón […] a la derecha del campo Search Path y añadimos:

D:\CodeGear\RAD Studio\5.0\Componentes\ZipMaster\

Suponiendo que sea ese el directorio donde he instalado el componente ZipMaster. También sería una buena costumbre copiar la librería DelZip179.dll del directorio:

D:\CodeGear\RAD Studio\5.0\Componentes\ZipMaster\DLL\

a donde tengamos nuestro proyecto, aunque no es necesario ya que se encuentra en C:\Windows\System32\. Es bueno llevarlo al lado del ejecutable cuando tenemos que instalar el programa en otros ordenadores.

COMO COMPRIMIR ARCHIVOS CON ZIPMASTER

Ahora supongamos que quiero comprimir un par de hojas de cálculo que se encuentran en la carpeta D:\prueba\:


Este sería el proceso para comprimir todos los archivos de esa carpeta:

Zip.DLLDirectory := ExtractFilePath( Application.ExeName );
Zip.ZipFileName := 'D:\prueba\calculos.zip';
Zip.TempDir := 'D:\prueba\';
Zip.FSpecArgs.Clear;
Zip.FSpecArgs.Add( 'D:\prueba\*.*' );
try
Zip.Add;
except
raise exception.Create( 'Error al comprimir los archivos.' );
end;

He realizado los siguientes pasos:

1º Le he especificado el directorio donde se encuentra la librería dinámica DelZip129.dll con la propiedad DLLDirectory.

2º Con la propiedad ZipFileName le indico como se va a llamar el archivo zip que voy a crear.

3º Aunque no es obligatorio, si es recomendable decirle el directorio temporal donde procesar los archivos temporales (TempDir). Lo normal es que sea el mismo directorio donde se están comprimiendo los archivos, por si se detiene el proceso y deja archivos temporales.

4º Por último vamos añadiendo los archivos que queremos comprimir:

Zip.FSpecArgs.Clear;
Zip.FSpecArgs.Add( 'D:\prueba\*.*' );

También podemos añadir una contraseña al archivo comprimido de este modo:

Zip.Password := '1234';

CREANDO ARCHIVOS AUTOEXTRAIBLES

Para crear un archivo comprimido y autoextraible hay que introducir en nuestro formulario el componente ZipSFX:


Suponiendo que el componente se llame ZipSFX escribimos el siguiente código que convierte un archivo zip ya comprimido en uno ejecutable:

ZipSFX.SFXPath := 'D:\CodeGear\RAD Studio\5.0\Componentes\ZipMaster\Res\';
ZipSFX.SourceFile := 'D:\prueba\calculos.zip';
ZipSFX.TargetFile := 'D:\prueba\calculos.exe';
ZipSFX.Convert;

El archivo que hace de autoextraible procede del directorio:

..\ZipMaster\Res\

El cual contiene dentro el archivo sfx_std.zip que hay que descomprimir en el mismo directorio.

Al ejecutar el archivo autoextraible aparece esta ventana:


Supuestamente también nos permite crear el archivo autoextraible en varios idiomas, incluido el español. Esto es lo que habría que añadir:

ZipSFX.SFXLanguage := ‘es’;

Lo que ocurre es que al ejecutarlo da este error:


Y ya he comprobado de que se encuentran estos archivos en ese directorio:

Dzsfxes.res
Dzsfxes.bin

Pero no hay manera de que me haga caso.

COMO DESCOMPRIMIR ARCHIVOS CON ZIPMASTER

Para descomprimir uno o más archivos con ZipMaster hacemos esto:

Zip.DLLDirectory := ExtractFilePath( Application.ExeName );
Zip.ZipFileName := 'D:\prueba\calculos.zip';
Zip.TempDir := 'D:\prueba\';
Zip.ExtrBaseDir := 'D:\prueba\';
Zip.FSpecArgs.Clear;
Zip.FSpecArgs.Add( '*.*' );
try
Zip.Extract;
except
raise exception.Create( 'Error al descomprimir los archivos.' );
end;

INTENTANDO AÑADIR LA LIBRERÍA DELZIP179.DLL

En la documentación de este componente nos indica que si añadimos la directiva:

{$DEFINE STATIC_LOAD_DELZIP_DLL}

en nuestro proyecto ya no necesitamos la librería DelZip179.dll al lado de nuestro ejecutable. Aunque he probado a quitar esta DLL y al ejecutarlo me da el error:


Que me lo expliquen.

CONCLUSIONES

Pese a las dos cosas que he intentado hacer y que no me han funcionado he de reconocer que el rendimiento de este componente es excelente y tiene una cantidad de opciones impresionante.

Pruebas realizadas en RAD Studio 2007.

14 noviembre 2008

Comprimir y descomprimir archivos con ZipForge

Aunque hay muchos componentes de terceros para Delphi que comprimen y descomprimen archivos zip con total naturalidad hay que reconocer que muchos de ellos necesitan librerías DLL externas. Sin embargo el componente ZipForge permite manipular archivos zip sin ninguna librería adicional. Todo va dentro de nuestro ejecutable.

Este componente es comercial y se vende al precio de 49 € (sin el código fuente). Para probarlo podemos bajar una versión personal para uso no comercial desde su página web:

http://componentace.com/zip_component_zip_delphi_zipforge.htm


Lo bueno de este componente es que está disponible para todas las versiones de Delphi:

En mi caso me he bajado la versión para RAD Studio 2007, cuya instalación va comprimida en un archivo zip de 2,81 MB.

INSTALANDO EL COMPONENTE

Para instalarlo sólo hay que descomprimir el archivo y ejecutar Install.exe, para luego ir pulsando el botón Next hasta que finalice la instalación:


Lo bueno de esta instalación es que no hay que instalar paquetes DPK ya que lo hace automáticamente. Sólo hay que cerrar Delphi, volver a abrirlo y veremos este mensaje al arrancar:


Cuando compremos el producto y lo registremos desaparecerá este mensaje. Al crear o abrir un nuevo proyecto ya tenemos visible el componente ZipForge en la paleta de componentes:


COMPRIMIR ARCHIVOS CON ZIPFORGE

Insertamos el componente en el formulario y le ponemos en su propiedad name el nombre Zip para simplificar:


Supongamos que tengo en la carpeta D:\prueba estos tres archivos:


Para comprimir estos archivos en un solo archivo zip hacemos lo siguiente:

With Zip do
begin
FileName := 'd:\prueba\documentos.zip';
OpenArchive( fmCreate );
BaseDir := 'd:\prueba\';
TempDir := 'd:\prueba\';
AddFiles( '*.pdf' );
CloseArchive;
end;

Con FileName le hemos dicho como va a llamarse el archivo zip que vamos a crear. Después creamos el archivo con OpenArchive y le decimos con la propiedad BaseDir desde donde vamos a comprimir los archivos.

No es necesario decirle también la propiedad TempDir (directorio temporal de compresión/decompresión) aunque recomiendo utilizar el mismo directorio para que no deje archivos temporales en Windows en caso de que se interrumpa el proceso de compresión/descompresión.

Por último sólo hay que decirle mediante AddFiles los archivos que vamos a comprimir (admitiendo incluso *.*) y cerramos el archivo creado.

Esta sería una compresión de archivos normal pero tenemos muchas más opciones. Por ejemplo, le podemos poner una contraseña al archivo zip escribiendo esto antes del comando AddFiles:

Password := '1234';

Si queremos asegurarnos de que los archivos se han comprimido correctamente podemos ejecutar esto:

AddFiles( '*.*' );

try
TestFiles('*.*');
except
Application.MessageBox( 'Error al comprimir los archivos.',
'Atención', MB_ICONSTOP );
end;

CloseArchive;

Otra opción que podemos elegir es el nivel de compresión:

CompresionLevel := clMax;

Permite todos estos valores:

clMax -> es la que más comprime pero la más lenta
clNormal -> compresión normal
clFastest -> es la compresión más rápida pero la que menos comprime
clNone -> no comprime nada, sólo empaqueta los archivos.

Si queremos controlar el proceso de compresión con una barra de progreso tenemos que añadir este código en el evento OnFileProgress:

procedure TForm1.ZipFileProgress(Sender: TObject; FileName: WideString;
Progress: Double; Operation: TZFProcessOperation;
ProgressPhase: TZFProgressPhase; var Cancel: Boolean);
begin
if ProgressPhase = ppProcess then
ProgressBar1.Position := Round(Progress);
end;

Lo que contiene el valor Progress no es el incremento total de la compresión sino el porcentaje de cada archivo que está comprimiendo. Por lo tanto sería conveniente hacer esto antes de empezar:

ProgressBar1.Max := 100;

Por último y no menos importante, también nos permite comprimir archivos desde un stream, desde un buffer de memoria o una cadena de texto:

procedure AddFromStream(FileName: WideString; Stream: TStream;
CopyToBuffer: Boolean = True; Position: Integer = 0;
Count: Integer = 0; Attr: Integer = faArchive; DateTime: TDateTime = 0);

procedure AddFromBuffer(FileName: WideString; const Buffer; Count: Integer;
Attr: Integer = faArchive; DateTime: TDateTime = 0);

procedure AddFromString(FileName: WideString; Text: String;
Attr: Integer = faArchive; DateTime: TDateTime = 0);

Algo sin duda muy interesante para no tener que crear archivos intermedios.

CREANDO ARCHIVOS AUTOEJECUTABLES

Para comprimir un archivo y hacerlo autoejecutable debemos utilizar un ejecutable que trae predeterminado el componente ZipForge y que viene en el directorio:

\ComponentAce\ZipForge\SFXStub\SFXStub.exe

Esa ruta hay que indicársela antes de crearlo:

With Zip do
begin
FileName := 'd:\prueba\documentos.zip';
OpenArchive( fmCreate );
BaseDir := 'd:\prueba\';
TempDir := 'd:\prueba\';
AddFiles( '*.*' );
CloseArchive;
SFXStub := 'D:\CodeGear\RAD Studio\5.0\Componentes\ZipForge\SFXStub\SFXStub.exe';
MakeSFX( 'd:\prueba\documentos.exe' );
end;

Al hacer doble clic sobre el ejecutable nos pedirá la ruta de descompresión:


DESCOMPRIMIR ARCHIVOS CON ZIPFORGE

Descomprimir archivos es todavía más fácil que comprimirlos:

With Zip do
begin
FileName := 'd:\prueba\documentos.zip';
OpenArchive( fmOpenRead );
BaseDir := 'd:\prueba\';
TempDir := 'd:\prueba\';
ExtractFiles( '*.*' );
CloseArchive;
end;

Si nos fijamos en las propiedades que tiene el componente ZipForge en el inspector de objetos podemos configurar si queremos que cree carpetas al descomprimir (si las hay) o elegir por ejemplo si queremos sobrescribir archivos:


Un componente como este se hace casi imprescindible para dar a nuestros programas funcionalidades tales como crear copias de seguridad de bases de datos, descomprimir archivos de configuración en directorios remotos o incluso proteger nuestros gráficos y recursos dentro de un zip con contraseña para que no puedan ser utilizados por otro programador.

Pruebas realizadas en RAD Studio 2007.

19 septiembre 2008

Enviar mensajes entre aplicaciones con IdUDPClient

En el último artículo vimos como enviar mensajes entre dos aplicaciones sin utilizar ningún protocolo de comunicación cuando los dos ejecutables se encuentran en el mismo PC. Pero si el mensaje hay que enviarlo entre dos ejecutables que se encuentran en dos PC distintos estamos obligados a utilizar un protocolo de comunicación y el más sencillo es el protocolo UDP.

El protocolo UDP goza de la ventaja de que no se establece una conexión entre la IP origen y la IP destino, sino que envía la información sin saber si va a llegar o no (al contrario de TCP que si establece una conexión segura entre ambos puntos).

El protocolo UDP es utilizado comúnmente en aplicaciones P2P para envío rápido de mensajes entre clientes y servidores así como en juegos online donde es necesario transmitir las coordenadas de cada personaje en tiempo real sin que existan demoras.

CREANDO LA APLICACIÓN QUE ENVÍA EL MENSAJE

Creamos un nuevo proyecto y en el formulario principal insertamos estos componentes:


Va a constar de dos componentes TEdit para guardar la IP del destinatario y su puerto (que por defecto le hemos puesto el 80). También va a tener un componente TMemo llamado Mensaje que va a contener las líneas de texto a enviar. Luego añadimos el componente IdUDPClient que se encuentra dentro de la paleta de componentes Indy Clients y que vamos a llamar Cliente.

En el evento OnClick del botón Enviar escribimos el siguiente código:

procedure TFEnviar.BEnviarClick(Sender: TObject);
begin
Cliente.Host := IP.Text;
Cliente.Port := StrToIntDef( Puerto.Text, 0 );
Cliente.Send( Mensaje.Text );
end;

Es algo tan sencillo como pasarle la IP destino (el Host), el puerto y ejecutamos el método Send que envía la información.

El único inconveniente que tiene este método es que no sabe si el destinatario existe o si le ha llegado el mensaje, pero es muy rápido.

CREANDO LA APLICACIÓN QUE RECIBE EL MENSAJE

Volvemos a crear un nuevo proyecto con este formulario:


Sólo va a contener un componente TMemo llamado Mensaje y el componente IdUPDServer que llamaremos Servidor. Al servidor le vamos a poner en su propiedad Port el valor 80. Después lo activamos poniendo su propiedad Active a True.

Ahora escribimos este código en el evento OnUDPRead:

procedure TFRecibir.ServidorUDPRead(Sender: TObject; AData: TBytes;
ABinding: TIdSocketHandle);
begin
Mensaje.Lines.Add( 'De: ' + ABinding.PeerIP );
Mensaje.Lines.Add( 'Mensaje: ' + PChar( AData ) );
end;

Mediante la propiedad ABinding.PeerIP obtenemos la IP del que envía el mensaje y la variable AData contiene el mensaje enviado en bytes.

Al ejecutar ambos programas y pulsar el botón Enviar este sería el resultado:


Si el mensaje se va a enviar entre dos aplicaciones que se encuentran en el mismo PC sólo hay que poner como dirección IP la 127.0.0.1 y funciona perfectamente. Incluso si seguimos escribiendo mensajes se añadirán a los que ya están en el destino.

Con algo tan simple como esto ya tenemos un pequeño cliente de mensajería entre dos equipos de la red local o en Internet. Para que la conversación pudiera ser bidireccional habría que crear un solo programa que tuviera un cliente UDP y un servidor UDP. También habría que dejar a elegir al usuario que IP y puertos va a utilizar para enviar y recibir.

Si la información que vamos a enviar es confidencial también sería interesante que las cadenas de caracteres que se envíen fueran encriptadas de algún modo.

Pruebas realizadas en RAD Studio 2007.

Publicidad