306
DELPHI AL LIMITE  (http://delphiallimite.blogspot.com/)  CONTENIDO CONTENIDO....................................................................................................... ............ 1 Explorar unidades y directorios ................................................................. ................... 4 Mostrando datos en el componente StringGrid ............................................ ................ 7 La barra de estado...................................... ................................................................. 11 Creando un navegador con el componente WebBrowser ........................................... 13 Creando consultas SQL con parámetros................................. ...................... .............. 16 Creando consultas SQL rápidas con IBSQL .............................................................. 19 Como poner una imagen de fondo en una aplicación MDI ........................................ 2 2 Leyendo los metadatos de una tabla de Interbase /Firebird..................... .................... 2 3 Generando números aleatorios .................................................................. ................. 27 Moviendo sprites con el teclado y el ratón ................................................................. 31 Mover sprites con doble buffer............................. ...................................................... 35 Como dibujar sprites transparentes............................. ............................................ .... 39 Creando tablas de memoria con ClientDataSet .......................................................... 42 Cómo crear un hilo de eje cución ................................................................................ 45 Conectando a pelo con INTERBASE o FIREBIRD ................................. ................. 47 Efectos de animación en las ventanas....................................... ...................... ............ 49 Guardando y cargando opciones................. ............................................ .................... 52 Minimizar en la bandeja del sis tema .......................................................................... 57 Cómo ocultar una aplicación ................................................................. ..................... 59 Capturar el teclado en Windows................................................................................. 60 Capturar la pantalla de Windows................................... ............................................. 61 Obtener los favoritos de Internet Explorer ................................................................. 6 2 Crear un a cceso directo........................ .................................................................. ..... 63 Averiguar la versión de Windows .............................................................................. 65 Recorrer un á rbol de direct orios ................................................................................. 66 Ejecutar un programa al arrancar Windows ............................................................... 67 Listar los programas instalados en Windows ............................................ ................. 6 9 Ejecutar un programa y esperar a que termine .......................................... ................. 7 0 Obtener modos de video ............................................................................................. 71 Utilizar una fuente TTF sin instalarla ................................................................... ...... 72 Convertir un icono en imagen BMP ........................................................................... 73 Borrar archivos temporales de Internet ........................... ........................................... 74 Deshabilitar el cortafuegos de Windows XP .............................................................. 75 Leer el número de serie de una unidad ....................................................................... 76 Leer l os archivos del portapapeles.............................................................. ................ 77 Averiguar los datos del usuario de Windows ............................................................. 77 Leer la cabec era PE de un programa ................................................................ .......... 79 Leer las dimensiones de imágenes JPG, PNG y GIF ................................................. 83 Descargar un archivo de Internet sin utilizar componentes.......................... .............. 87

Delphi Al Limite

Embed Size (px)

Citation preview

Page 1: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 1/306

DELPHI AL LIMITE

  (http://delphiallimite.blogspot.com/)

  CONTENIDO

CONTENIDO................................................................................................................... 1

Explorar unidades y directorios .................................................................................... 4

Mostrando datos en el componente StringGrid ............................................................ 7

La barra de estado....................................................................................................... 11

Creando un navegador con el componente WebBrowser........................................... 13

Creando consultas SQL con parámetros..................................................................... 16

Creando consultas SQL rápidas con IBSQL .............................................................. 19Como poner una imagen de fondo en una aplicación MDI ........................................ 22

Leyendo los metadatos de una tabla de Interbase/Firebird......................................... 23

Generando números aleatorios ................................................................................... 27

Moviendo sprites con el teclado y el ratón................................................................. 31

Mover sprites con doble buffer................................................................................... 35

Como dibujar sprites transparentes............................................................................. 39

Creando tablas de memoria con ClientDataSet .......................................................... 42

Cómo crear un hilo de ejecución ................................................................................ 45

Conectando a pelo con INTERBASE o FIREBIRD .................................................. 47

Efectos de animación en las ventanas......................................................................... 49

Guardando y cargando opciones................................................................................. 52

Minimizar en la bandeja del sistema .......................................................................... 57

Cómo ocultar una aplicación ...................................................................................... 59

Capturar el teclado en Windows................................................................................. 60

Capturar la pantalla de Windows................................................................................ 61

Obtener los favoritos de Internet Explorer ................................................................. 62

Crear un acceso directo............................................................................................... 63

Averiguar la versión de Windows .............................................................................. 65

Recorrer un árbol de directorios ................................................................................. 66

Ejecutar un programa al arrancar Windows ............................................................... 67

Listar los programas instalados en Windows ............................................................. 69Ejecutar un programa y esperar a que termine ........................................................... 70

Obtener modos de video............................................................................................. 71

Utilizar una fuente TTF sin instalarla......................................................................... 72

Convertir un icono en imagen BMP ........................................................................... 73

Borrar archivos temporales de Internet ...................................................................... 74

Deshabilitar el cortafuegos de Windows XP.............................................................. 75

Leer el número de serie de una unidad....................................................................... 76

Leer los archivos del portapapeles.............................................................................. 77

Averiguar los datos del usuario de Windows ............................................................. 77

Leer la cabecera PE de un programa .......................................................................... 79

Leer las dimensiones de imágenes JPG, PNG y GIF ................................................. 83Descargar un archivo de Internet sin utilizar componentes........................................ 87

Page 2: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 2/306

Averiguar el nombre del procesador y su velocidad .................................................. 88

Generar claves aleatorias ............................................................................................ 90

Meter recursos dentro de un ejecutable ...................................................................... 91

Dibujar un gradiente en un formulario ....................................................................... 93

Trocear y unir archivos............................................................................................... 94

Mover componentes en tiempo de ejecución ............................................................. 95Trabajando con arrays dinámicos ............................................................................... 96

Clonar las propiedades de un control ......................................................................... 97

Aplicar antialiasing a una imagen .............................................................................. 98

Dibujar varias columnas en un ComboBox.............................................................. 100

Conversiones entre unidades de medida................................................................... 102

Tipos de puntero ....................................................................................................... 106

Dando formato a los números reales ........................................................................ 107

Conversiones entre tipos numéricos ......................................................................... 110

Creando un cliente de chat IRC con Indy (I) ............................................................ 113

Creando un cliente de chat IRC con Indy (II) .......................................................... 115

Creando un cliente de chat IRC con Indy (III) ......................................................... 117Creando un procesador de textos con RichEdit (I) ................................................... 121

Creando un procesador de textos con RichEdit (II) ................................................. 125

Dibujando con la clase TCanvas (I) ......................................................................... 128

Dibujando con la clase TCanvas (II) ........................................................................ 133

Dibujando con la clase TCanvas (III)....................................................................... 137

El componente TTreeView (I).................................................................................. 143

El componente TTreeView (II) ................................................................................ 147

Explorar unidades y directorios ................................................................................ 149

Mostrando datos en el componente StringGrid ........................................................ 153

Funciones y procedimientos para fecha y hora (I) ................................................... 156

Funciones y procedimientos para fecha y hora (II) .................................................. 159

Funciones y procedimientos para fecha y hora (III) ................................................. 163

Implementando interfaces en Delphi (I) ................................................................... 166

Implementando interfaces en Delphi (II).................................................................. 168

Implementando interfaces en Delphi (III) ................................................................ 171

La potencia de los ClientDataSet (I)......................................................................... 174

La potencia de los ClientDataSet (II) ....................................................................... 177

La potencia de los ClientDataSet (III)...................................................................... 179

La potencia de los ClientDataSet (IV)...................................................................... 183

La potencia de los ClientDataSet (V)....................................................................... 187

Mostrando información en un ListView (I).............................................................. 190Mostrando información en un ListView (II) ............................................................ 193

Mostrando información en un ListView (III) ........................................................... 195

Trabajando con archivos de texto y binarios (I) ....................................................... 197

Trabajando con archivos de texto y binarios (II)...................................................... 199

Trabajando con archivos de texto y binarios (III) .................................................... 202

Trabajando con archivos de texto y binarios (IV) .................................................... 205

Trabajando con archivos de texto y binarios (V) ..................................................... 208

Trabajando con documentos XML (I) ...................................................................... 211

Trabajando con documentos XML (II)..................................................................... 213

Trabajando con documentos XML (III) ................................................................... 216

Creando informes con Rave Reports (I) ................................................................... 223Creando informes con Rave Reports (II).................................................................. 228

Page 3: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 3/306

Creando informes con Rave Reports (III) ................................................................ 233

Creando informes con Rave Reports (IV) ................................................................ 237

Creando informes con Rave Reports (V) ................................................................. 239

Como manejar excepciones en Delphi (I) ................................................................ 243

Como manejar excepciones en Delphi (II) ............................................................... 246

Creando aplicaciones multicapa (I) .......................................................................... 250Creando aplicaciones multicapa (II)......................................................................... 253

Creando aplicaciones multicapa (III) ....................................................................... 257

Creando aplicaciones multicapa (IV) ....................................................................... 261

Enviando un correo con INDY................................................................................. 264

Leyendo el correo con INDY ................................................................................... 265

Subiendo archivos por FTP con INDY .................................................................... 269

Descargando archivos por FTP con INDY............................................................... 271

Operaciones con cadenas de texto (I) ....................................................................... 273

Operaciones con cadenas de texto (II)...................................................................... 274

Operaciones con cadenas de texto (III) .................................................................... 277

Operaciones con cadenas de texto (IV) .................................................................... 280Operaciones con cadenas de texto (V) ..................................................................... 283

El objeto StringList (I).............................................................................................. 288

El objeto StringList (II) ............................................................................................ 291

El objeto StringList (III) ........................................................................................... 294

Convertir cualquier tipo de variable a String (I)....................................................... 300

Convertir cualquier tipo de variable a String (II) ..................................................... 303

Page 4: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 4/306

Explorar unidades y directoriosSi importante es controlar el manejo de archivos no menos importante es elsaber moverse por las unidades de disco y los directorios.

Veamos que tenemos Delphi para estos menesteres:

function CreateDir( const Dir: string ): Boolean;

Esta función crea un nuevo directorio en la ruta indicada por Dir. DevuelveTrue o False dependiendo si ha podido crearlo o no. El único inconvenienteque tiene esta función es que deben existir los directorios padres. Porejemplo:

Cr eat eDi r ( ' C: \ pr ueba' ) devuel ve Tr ue

Cr eat eDi r ( ' C: \ pr ueba\ document os' ) devuel ve Tr ueCr eat eDi r ( ' C: \ ot r apr ueba\ document os' ) devuel ve Fal se ( y no l o cr ea)

function ForceDirectories( Dir: string ): Boolean;

Esta función es similar a CreateDir salvo que también crea toda la ruta dedirectorios padres.

For ceDi r ect or i es( ' C: \ pr ueba' ) devuel ve Tr ueFor ceDi r ect or i es( ' C: \ pr ueba\ document os' ) devuel ve TrueFor ceDi r ect or i es( ' C: \ otr apr ueba\ document os' ) devuel ve True

procedure ChDir( const S: string ); overload;

Este procedimiento cambia el directorio actual al indicado por el parámetroS. Por ejemplo:

ChDi r ( ' C: \ Wi ndows\ Font s' ) ;

function GetCurrentDir: string;

Nos devuelve el nombre del directorio actual donde estamos posicionados. Por

ejemplo:

Get Cur r ent Di r devuel ve C: \ Wi ndows\ Font s

function SetCurrentDir( const Dir: string ): Boolean;

Establece el directorio actual devolviendo True si lo ha conseguido. Porejemplo:

Set Cur r ent Di r ( ' C: \ Wi ndows\ J ava' ) ;

procedure GetDir( D: Byte; var S: string );

Page 5: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 5/306

Devuelve el directorio actual de una unidad y lo mete en la variable S. Elparámetro D es el número de la unidad siendo:

D Uni dad- - - - - - - - - - - - - - - - - - - - -0 Uni dad por def ecto

1 A:2 B:3 C:. . .

Por ejemplo para leer el directorio actual de la unidad C:

var  sDi r ect or i o: St r i ng;begi n  Get Di r ( 3, sDi r ector i o ) ;  ShowMessage( ' El di r ect ori o act ual de l a uni dad C: es ' +sDi rect or i o ) ;end;

function RemoveDir( const Dir: string ): Boolean;

Elimina un directorio en el caso de que este vacío, devolviendo False si no hapodido hacerlo.

RemoveDi r ( ' C: \ pr ueba\ document os' ) devuel ve TrueRemoveDi r ( ' C: \ pr ueba' ) devuel ve TrueRemoveDi r ( ' C: \ ot r apr ueba' ) devuel ve Fal se porque no est avací o

function DirectoryExists( const Directory: string ): Boolean;

Comprueba si existe el directorio indicado por el parámetro Directory. Porejemplo:

Di r ect or yExi st s( ' C: \ Wi ndows\ Syst em32\ ' ) devuel ve TrueDi r ect or yExi st s( ' C: \ Wi ndows\ Mi sDocument os\ ' ) devuel ve Fal se

function DiskFree( Drive: Byte ): Int64;

Devuelve el número de bytes libres de una unidad de dico indicada por laletra Drive:

Dr i ve Uni dad- - - - - - - - - - - - - - - - -0 Uni dad por def ect o1 A:2 B:3 C:. . .

Por ejemplo vamos a ver el número de bytes libres de la unidad C:

Di skFr ee( 3 ) devuel ve 5579714560

Page 6: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 6/306

function DiskSize( Drive: Byte ): Int64;

Nos dice el tamaño total en bytes de una unidad de disco. Por ejemplo:

Di skSi ze( 3 ) devuel ve 20974428160

BUSCANDO ARCHIVOS DENTRO DE UN DIRECTORIO

Para buscar archivos dentro de un directorio disponemos de las funciones:

function FindFirst( const Path: string; Attr: Integer; var F: TSearchRec ):Integer;

Busca el primer archivo, directorio o unidad que se encuentre dentro de unaruta en concreto. Devuelve un cero si ha encontrado algo. El parámetroTSearchRec es una estructura de datos donde se almacena lo encontrado:

type  TSearchRec = r ecor d  Ti me: I nt eger ;  Si ze: I nt eger ;  At t r : I nt eger;  Name: TFi l eName;  Excl udeAt t r : I nt eger ;  Fi ndHandl e: THandl e;  Fi ndDat a: TWi n32Fi ndDat a;  end;

function FindNext( var F: TSearchRec ): Integer;

Busca el siguiente archivo, directorio o unidad especificado anteriormente porla función FindFirst. Devuelve un cero si ha encontrado algo.

procedure FindClose( var F: TSearchRec );

Este procedimiento cierra la búsqueda comenzada por FindFirst y FindNext.

Veamos un ejemplo donde se utilizan estas funciones. Vamos a hacer unprocedimiento que lista sólo los archivos de un directorio que le pasemos yvuelca su contenido en un StringList:

pr ocedur e TFPr i nci pal . Li st ar ( sDi r ectori o: st r i ng; var Resul t ado: TSt r i ngLi st ) ;var  Busqueda: TSearchRec;  i Resul t ado: I nt eger ;begi n  / / Nos aseguramos que t ermi ne en cont r abar r a

  sDi r ect or i o : = I ncl udeTr ai l i ngBacksl ash( sDi r ectori o ) ;

Page 7: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 7/306

  i Resul t ado : = Fi ndFi r st ( sDi r ector i o + ' *. *' , f aAnyFi l e, Busqueda) ;

  whi l e i Resul t ado = 0 do  begi n  / / ¿Ha encont r ado un archi vo y no es un di r ect or i o?

  i f ( Busqueda. At t r and f aAr chi ve = f aAr chi ve ) and  ( Busqueda. At t r and f aDi r ect ory <> f aDi r ect or y ) t hen  Resul t ado. Add( Busqueda. Name ) ;

  i Resul t ado : = Fi ndNext ( Busqueda ) ;  end;

  Fi ndCl ose( Busqueda ) ;end;

Si listamos el raiz de la unidad C:

var

  Di rect or i o: TSt r i ngLi st ;begi n  Di r ectori o : = TSt r i ngLi st . Cr eat e;  Li s tar ( ' C: ' , Di rector i o ) ;  ShowMessage( Di r ector i o. Text ) ;  Di r ectori o. Free;end;

El resultado sería:

AUTOEXEC. BATBoot f ont . bi nCONFI G. SYSI NSTALL. LOGI O. SYSMSDOS. SYSNTDETECT. COM

Con estas tres funciones se pueden hacer cosas tan importantes como eliminardirectorios, realizar búsquedas de archivos, calcular lo que ocupa undirectorio en bytes, etc.

Pruebas realizadas en Delphi 7.

Mostrando datos en el componenteStringGridAnteriormente vimos como mostrar información en un componente ListViewllegando incluso a cambiar el color de filas y columnas a nuestro antojo. Elúnico inconveniente estaba en que no se podían cambiar los títulos de lascolumnas, ya que venían predeterminadas por los colores de Windows.

Pues bien, el componente de la clase TStringGrid es algo más cutre que elListView, pero permite cambiar al 100% el formato de todas las celdas.Veamos primero como meter información en el mismo. Al igual que ocurría

con el ListView, todos las celdas de un componente StringGrid son de tipostring, siendo nosotros los que le tenemos que dar formato a mano.

Page 8: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 8/306

AÑADIENDO DATOS A LA REJILLA

Vamos a crear una rejilla de datos con las siguiente columnas:

NOMBRE, APELLI DO1, APELLI DO2, NI F, I MPORTE PTE.

Cuando insertamos un componente StringGrid en el formulario nos va a ponerpor defecto la primera columna con celdas fijas (fixed). Vamos a fijar lassiguientes propiedades:

Pr opi edad Val or Descr i pci ón- - - - - - - - - - - - - - - - - - - - - - - - -Col Count 5 5 col umnasRowCount 4 4 f i l asFi xedCol s 0 0 col umnas f i j asFi xedRows 1 1 f i l a f i j aDef aul t RowHei ght 20 al t ur a de l as f i l as a 20 pi xel s

Ahora creamos un procedimiento para completar de datos la rejilla:

pr ocedur e TFor mul ar i o. Rel l enar Tabl a;begi n  wi t h St r i ngGr i d do  begi n  / / Tí t ul o de l as col umnas  Cel l s[ 0, 0] : = ' NOMBRE' ;  Cel l s[ 1, 0] : = ' APELLI DO1' ;

  Cel l s[ 2, 0] : = ' APELLI DO2' ;  Cel l s [3, 0] : = ' NI F' ;  Cel l s[ 4, 0] : = ' I MPORTE PTE. ' ;

  / / Dat os  Cel l s[ 0, 1] : = ' PABLO' ;  Cel l s[ 1, 1] : = ' GARCI A' ;  Cel l s[ 2, 1] : = ' MARTI NEZ' ;  Cel l s[ 3, 1] : = ' 67348321D' ;  Cel l s[ 4, 1] : = ' 1500, 36' ;

  / / Dat os  Cel l s[ 0, 2] : = ' MARI A' ;

  Cel l s[ 1, 2] : = ' SANCHEZ' ;  Cel l s[ 2, 2] : = ' PALAZON' ;  Cel l s[ 3, 2] : = ' 44878234A' ;  Cel l s[ 4, 2] : = ' 635, 21' ;

  / / Dat os  Cel l s[ 0, 3] : = ' CARMEN' ;  Cel l s[ 1, 3] : = ' PEREZ' ;  Cel l s[ 2, 3] : = ' GUI LLEN' ;  Cel l s[ 3, 3] : = ' 76892693L' ;  Cel l s[ 4, 3] : = ' 211, 66' ;  end;end;

Al ejecutar el programa puede apreciarse lo mal que quedan los datos en

Page 9: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 9/306

pantalla, sobre todo la columna del importe pendiente:

DANDO FORMATO A LAS CELDAS DE UN COMPONENTE STRINGGRIND

Lo que vamos a hacer a continuación es lo siguiente:

- La primera fila fija va a ser de color de fondo azul oscuro con fuente blancay además el texto va a ir centrado.

- La columna del importe pendiente va a tener la fuente de color verde y va air alineada a la derecha.

- El resto de columnas tendrán el color de fondo blanco y el texto en negro.

Todo esto hay que hacerlo en el evento OnDrawCell del componenteStringGrid:

pr ocedur e TFor mul ar i o. St r i ngGr i dDr awCel l ( Sender : TObj ect ; ACol ,  ARow: I nt eger; Rect : TRect ; St at e: TGr i dDr awSt ate ) ;var  sTexto: St r i ng; / / Text o que va a i mpr i mi r en l a cel da

act ual  Al i neaci on: TAl i gnment ; / / Al i neaci ón que l e vamos a dar al t ext o  i AnchoTexto: I nt eger ; / / Ancho del t ext o a i mpr i mi r en pi xel sbegi n  wi t h St r i ngGr i d. Canvas do  begi n  / / Lo pr i mero es coger l a f uent e por def ecto que l e hemos asi gnadoal component e  Font . Name : = St r i ngGr i d. Font . Name;  Font . Si ze : = St r i ngGr i d. Font . Si ze;

  i f ARow = 0 t hen  Al i neaci on : = t aCent er

  el se  / / Si es l a col umna del i mpor t e pendi ent e al i neamos el t ext o al a der echa  i f ACol = 4 t hen  Al i neaci on : = t aRi ght J ust i f y  el se  Al i neaci on : = t aLef t J ust i f y;

  / / ¿Es una cel da f i j a de sól o l ectur a?  i f gdFi xed i n St at e t hen  begi n  Br ush. Col or : = cl Navy; / / l e ponemos azul de f ondo  Font . Col or : = cl Whi t e; / / f uent e bl anca

  Font . St yl e : = [ f sBol d] ; / / y negr i t a  end  el se

Page 10: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 10/306

  begi n  / / ¿Est a enf ocada l a cel da?  i f gdFocused i n St at e t hen  begi n  Br ush. Col or : = cl Red; / / f ondo r oj o  Font . Col or : = cl Whi t e; / / f uent e bl anca

  Font . St yl e : = [ f sBol d] ; / / y negr i t a  end  el se  begi n  / / Par a el rest o de cel das el f ondo l o ponemos bl anco  Br ush. Col or : = cl Wi ndow;

  / / ¿Es l a col umna del i mport e pendi ent e?  i f ACol = 4 t hen  begi n  Font . Col or : = cl Gr een; / / l a pi nt amos de azul  Font . St yl e : = [ f sBol d] ; / / y negr i t a  Al i neaci on : = t aRi ght J ust i f y;  end  el se  begi n  Font . Col or : = cl Bl ack;  Font . St yl e : = [ ] ;  end;  end;  end;

  sText o : = St r i ngGr i d. Cel l s[ ACol , ARow] ;  Fi l l Rect( Rect ) ;  i AnchoText o : = Text Wi dt h( sText o ) ;

  case Al i neaci on of   t aLef t J ust i f y: Text Out ( Rect . Lef t + 5, Rect . Top + 2, sTexto ) ;  t aCent er : Text Out ( Rect. Lef t + ( ( Rect . Ri ght - Rect. Lef t ) -i AnchoText o ) di v 2, Rect . Top + 2, sText o ) ;  t aRi ght J ust i f y: Text Out ( Rect . Ri ght - i AnchoText o - 2, Rect . Top+ 2, sText o ) ;  end;  end;end;

Así quedaría al ejecutarlo:

Sólo hay un pequeño inconveniente y es que la rejilla primero se pinta demanera normal y luego nosotros volvemos a pintarla encima con el eventoOnDrawCell con lo cual hace el proceso dos veces. Si queremos que sólo sehaga una vez hay que poner a False la propiedad DefaultDrawing. Quedaríade la siguiente manera:

Page 11: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 11/306

Por lo demás creo que este componente que puede sernos muy útil paramostrar datos por pantalla en formato de sólo lectura. En formato deescritura es algo flojo porque habría que controlar que tipos de datos puedeescribir el usuario según en que columnas esté.

Pruebas realizadas en Delphi 7.

La barra de estadoEs raro encontrar una aplicación que no lleve en alguno de sus formularios labarra de estado. Hasta ahora he utilizado ejemplos sencillos de meter texto

en la barra de estado de manera normal, pero el componente StatusBarpermite meter múltiples paneles dentro de la barra de estado e inclusopodemos cambiar el formato de la fuente en cada uno de ellos.

ESCRIBIENDO TEXTO SIMPLE

El componente de la clase TStatusBar tiene dos estados: uno para escribirtexto simple en un sólo panel y otro para escribir en múltiples paneles.Supongamos que el componente de la barra de estado dentro de nuestroformulario de llama BarraEstado. Para escribir en un sólo panel se hace de lasiguiente manera:

Barr aEst ado. Si mpl ePanel : = True;Barr aEst ado. Si mpl eText : = ' Text o de pr ueba' ;

Se puede escribir tanto texto como longitud tenga la barra de estado, o mejordicho, tanto como sea la longitud del formulario.

ESCRIBIENDO TEXTO EN MULTIPLES PANELES

Para escribir en múltiples paneles dentro de la misma barra de estado hay quecrear un panel por cada apartado. En este ejemplo voy a crear en la barra deestado tres paneles y en cada uno de ellos voy a poner un formato diferente.

begi n  Barr aEst ado. Si mpl ePanel : = Fal se;  Bar r aEst ado. Panel s. Cl ear ;

  wi t h Bar r aEst ado. Panel s. Add do  begi n  Text : = ' x=10' ;  Wi dt h : = 50;  St yl e : = psOwnerDr aw;

  Al i gnment : = t aRi ght J ust i f y;  end;

Page 12: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 12/306

  wi t h Bar r aEst ado. Panel s. Add do  begi n  Text : = ' y=50' ;  Wi dt h : = 50;  St yl e : = psOwnerDr aw;

  Al i gnment : = t aRi ght J ust i f y;  end;

  wi t h Bar r aEst ado. Panel s. Add do  begi n  Text : = ' Texto sel ecci onado' ;  St yl e : = psText ;  Wi dt h : = 50;  end;end;

La propiedad Style de cada panel determina si es psText o psOwnerDraw. Pordefecto todos los paneles que se crean tiene el estilo psText (texto normal).

Si elegimos el estilo psOwnerDraw significa que vamos a ser nosotros losencargados de dibujar el contenido del mismo. Ello se hace en el eventoOnDrawPanel de la barra de estado:

pr ocedur e TFormul ar i o. Barr aEst adoDr awPanel ( StatusBar : TSt atusBar;Panel : TSt at usPanel ; const Rect : TRect ) ;begi n  case Panel . I D of   0: wi t h Barr aEst ado. Canvas do  begi n  Font . Name : = ' Tahoma' ;  Font . Si ze : = 10;

  Font . St yl e : = [ f sBol d] ;  Font . Col or : = cl Navy;  Text Out ( Rect . Lef t + 2, Rect . Top, Panel . Text ) ;

  end;

  1: wi t h Barr aEst ado. Canvas do  begi n  Font . Name : = ' Tahoma' ;  Font . Si ze : = 10;  Font . St yl e : = [ f sBol d] ;  Font . Col or : = cl Red;  Text Out ( Rect . Lef t + 2, Rect . Top, Panel . Text ) ;  end;

  end;end;

Cuando se van creando paneles dentro de una barra de estado, a cada uno deellos se le va asignado la propiedad ID a 0, 1, 2, etc, la cual es de sólolectura. Como puede verse en el evento OnDrawPanel si el ID es 0 lo pinto deazul y si es 1 de rojo. Pero sólo funcionará en aquellos paneles cuyo estilo seapsOwnerDraw. Quedaría de la siguiente manera:

Page 13: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 13/306

También puede cambiarse en cada panel propiedades tales como el marco(bevel), la alineación del texto (alignment) y el ancho (width).

Esto nos permitirá informar mejor al usuario sobre el comportamiento denuestra aplicación en tiempo real y de una manera elegante que no interfiere

con el contenido del resto del formulario.Pruebas realizadas en Delphi 7.

Creando un navegador con el componenteWebBrowserDelphi incorpora dentro de la pestaña Internet el componente de la claseTWebBrowser el cual utiliza el motor de Internet Explorer para añadir unnavegador en nuestras aplicaciones.

Seguro que os preguntareis, ¿que utilidad puede tener esto si ya tengoInternet Explorer, Firefox, Opera, etc.? Pues hay ocasiones en que un clientenos pide que ciertos usuarios sólo puedan entrar a ciertas páginas web. Porejemplo, si un usuario está en el departamento de compras, es lógico que adonde sólo tiene que entrar es a las páginas de sus proveedores y no a otraspara bajarse música MP3 a todo trapo (no os podeis imaginar la pasta quepierden las empresas por este motivo).

Entonces vamos a ver como crear un pequeño navegador con las

funcionalidades mínimas.

Page 14: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 14/306

CREANDO LA VENTANA DE NAVEGACION

Vamos a crear la siguiente ventana:

Incorpora los siguientes componentes:

- Un panel en la parte superior con su propiedad Align fijada a alTop (pegadoarriba).

- Dentro del panel tenemos una etiqueta para la dirección.

- Un componente ComboBox llamado URL para la barra de direcciones.

- Tres botones para ir atrás, adelante y para detener.

- Una barra de progreso para mostrar la carga de la página web.

- Un componente WebBrower ocupando el resto del formulario mediante supropiedad Align en alClient, de tal manera que si maximizamos la ventana serespete el posicionamiento de todos los componentes.

CREANDO LAS FUNCIONES DE NAVEGACION

Una vez hecha la ventana pasemos a crear cada uno de los eventosrelacionados con la navegación. Comencemos con el evento de pulsar Intro

dentro de la barra de direcciones llamada URL:

pr ocedur e TFor mul ar i o. URLKeyDown( Sender : TObj ect ; var Key: Word;Shi f t : TShi f t St at e ) ;begi n  i f key = VK_RETURN t hen  begi n  WebBr owser . Navi gate( URL. Text ) ;  URL. I t ems. Add( URL. Text ) ;  end;end;

Si se pulsa la tecla Intro hacemos el objeto WebBrowser navegue a esa

Page 15: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 15/306

dirección. Además añadimos esa dirección a la lista de direcciones URL por laque hemos navegado para guardar un historial de las mismas.

Cuando se pulse el botón atrás en la barra de navegación hacemos losiguiente:

pr ocedur e TFor mul ar i o. BAt r asCl i ck( Sender: TObj ect ) ;begi n  WebBr owser . GoBack;end;

Lo mismo cuando se pulse el botón adelante:

pr ocedur e TFor mul ar i o. BAdel ant eCl i ck( Sender : TObj ect ) ;begi n  WebBr owser . GoFor war d;end;

Y también si queremos detener la navegación:

pr ocedur e TFor mul ar i o. BDet ener Cl i ck( Sender : TObj ect ) ;begi n  WebBr owser . St op;end;

Hasta aquí tenemos la parte básica de la navegación. Pasemos ahora acontrolar el progreso de la navegación así como que el usuario sólo puedaentrar a una página en concreto.

A la barra de progreso situada a la derecha del botón BDetener la llamaremosProgreso y por defecto estará invisible. Sólo la vamos a hacer aparecercuando comience la navegación. Eso se hace en el eventoOnBeforeNavigate2:

pr ocedur e TFormul ar i o. WebBr owser Bef oreNavi gat e2( Sender : TObj ect ;  const pDi sp: I Di spat ch; var URL, Fl ags, Tar getFr ameName, Post Data,  Headers : Ol eVari ant ; var Cancel : WordBool ) ;begi n  i f Pos( ' t er r a' , URL ) = 0 t hen  Cancel : = True;

  Pr ogr eso. Show;end;

Aquí le hemos dicho al evento que antes de navegar si la URL de la páginaweb no contiene la palabra terra que cancele la navegación. Así evitamos queel usuario se distraiga en otras páginas web.

En el caso de que si pueda navegar entonces mostramos la barra de progresode la carga de la página web. Para controlar el progreso de la navegación seutiliza el evento OnProgressChange:

pr ocedur e TFor mul ar i o. WebBr owser Progr essChange( Sender : TObj ect ;

Pr ogr ess, Pr ogr essMax: I nt eger ) ;begi n

Page 16: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 16/306

  Progr eso. Max : = Progr essMax;  Pr ogr eso. Posi t i on : = Pr ogr ess;end;

Cuando termine la navegación debemos ocultar de nuevo la barra deprogreso. Eso lo hará en el evento OnDocumentComplete:

procedure TFor mul ar i o. WebBr owser Document Compl et e( Sender : TObj ect ;  const pDi sp: I Di spat ch; var URL: Ol eVar i ant ) ;begi n  Progr eso. Hi de;end;

Con esto ya tenemos un sencillo navegador que sólo accede a donde nosotrosle digamos. A partir de aquí podemos ampliarle muchas más característicastales como memorizar las URL favoritas, permitir visualizar a pantallacompleta (FullScreen) e incluso permitir múltiples ventanas de navegación a

través de pestañas (con el componente PageControl).También se pueden bloquear ventanas emergentes utilizando el eventoOnNewWindow2 evitando así las asquerosas ventanas de publicidad:

procedur e TFor mul ar i o. WebBr owser NewWi ndow2(Sender : TObj ect ;  var ppDi sp: I Di spat ch; var Cancel : Wor dBool ) ;begi n  Cancel : = True;end;

Aunque últimamente los publicistas muestran la publicidad en capas mediante

hojas de estilo en cascada, teniendo que utilizar otros programas másavanzados para eliminar publicidad.

Pruebas realizadas en Delphi 7.

Creando consultas SQL con parámetros

En el artículo anterior vimos como realizar consultas SQL para INSERT,DELETE, UPDATE y SELECT utilizando el componente IBSQL que forma partede la paleta de componentes IBExpress.

También quedó muy claro que la velocidad de ejecución de consultas con estecomponente respecto a otros como IBQuery es muy superior. Todo lo quehemos visto esta bien para hacer consultas esporádicas sobre alguna tabla queotra, pero ¿que ocurre si tenemos que realizar miles de consultas SQL de unasola vez?

UTILIZANDO UNA TRANSACCION POR CONSULTA

Page 17: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 17/306

Supongamos que tenemos que modificar el nombre de 1000 registros de latabla CLIENTES:

var

  i : I nteger ;  dwTi empo: DWor d;begi n  wi t h Consul t a do  begi n  / / / / / / / / / / / / / / METODO LENTO / / / / / / / / / / / / / / / /

  dwTi empo : = Ti meGet Ti me;

  f or i : = 1 t o 1000 do  begi n  SQL. Cl ear ;  SQL. Add( ' UPDATE CLI ENTES' ) ;

  SQL. Add( ' SET NOMBRE = ' + Quot edSt r ( ' NOMBRE CLI ENTE Nº ' +I nt ToSt r ( i ) ) ) ;  SQL. Add( ' WHERE I D = ' + I nt ToSt r ( i ) ) ;

  Tr ansact i on. St ar t Tr ansact i on;

  t r y  ExecQuery;  Transact i on. Commi t ;  except  on E: Except i on do  begi n  Appl i cat i on. MessageBox( PChar ( E. Message ) , ' Er r or de SQL' ,

MB_I CONSTOP ) ;  Transacci on. Rol l back;  end;  end;  end;

  ShowMessage( ' Ti empo: ' + I ntToSt r ( Ti meGet Ti me - dwTi empo ) + 'mi l i segundos' ) ;  end;end;

Como puede verse arriba, por cada cliente actualizado he generado una SQLdistinta abriendo y cerrando una transacción para cada registro. He utilizadola función TimeGetTime que se encuentra en la unidad MMSystem paracalcular el tiempo que tarda en actualizarme el nombre de los 1000 clientes.En un PC con Pentium 4 a 3 Ghz, 1 GB de RAM y utilizando el motor de basesde datos Firebird 2.0 me ha tardado 4167 milisegundos.

Aunque las consultas SQL van muy rápidas con los componentes IBSQL aquí elfallo que cometemos es que por cada registro actualizado se abre y se cierrauna transacción. En una base de datos local no se nota mucho pero en una redlocal con muchos usuarios trabajando a la vez le puede pegar fuego alconcentrador.

Lo ideal sería poder modificar la SQL pero sin tener que cerrar la transacción.

Page 18: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 18/306

Como eso no se puede hacer en una consulta que esta abierta entonces hayque utilizar los parámetros. Los parámetros (Params) nos permiten enviar yrecoger información de una consulta SQL que se esta ejecutando sin tener quecerrarla y abrila.

UTILIZANDO PARAMETROS EN LA CONSULTA

Para introducir parámetros en una consulta SQL hay que añadir dos puntosdelante del parámetro. Por ejemplo:

UPDATE CLI ENTESSET NOMBRE = : NOMBREWHERE I D = : I D

Esta consulta tiene dos parámetros: ID y NOMBRE. Los nombres de losparámetros no tienen porque coincidir con el nombre del campo. Bien podríanser así:

UPDATE CLI ENTESSET NOMBRE = : NUEVONOMBREWHERE I D = : I DACTUAL

De este modo se pueden modificar las condiciones de la consulta SQL sin tenerque cerrar la transacción. Después de crear la consulta SQL hay que llamar almétodo Prepare para que prepare la consulta con los futuros parámetros quese le van a suministrar (no es obligatorio pero si recomendable). Veamos elejemplo anterior utilizando parámetros y una sóla transacción para los 1000registros:

var  i : I nteger ;  dwTi empo: DWor d;begi n  wi t h Consul t a do  begi n  / / / / / / / / / / / / / / METODO RÁPI DO / / / / / / / / / / / / / / / /

  dwTi empo : = Ti meGet Ti me;

  Tr ansact i on. St ar t Tr ansact i on;

  SQL. Cl ear ;  SQL. Add( ' UPDATE CLI ENTES' ) ;  SQL. Add( ' SET NOMBRE = : NOMBRE' ) ;  SQL. Add( ' WHERE I D = : I D' ) ;  Pr epare;

  f or i : = 1 t o 1000 do  begi n  Params. ByName( ' NOMBRE' ) . AsSt r i ng : = ' NOMBRE CLI ENTE Nº ' +I ntToSt r ( i ) ;  Par ams. ByName( ' I D' ) . AsI nt eger : = i ;  ExecQuery;  end;

  t r y  Transact i on. Commi t ;

Page 19: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 19/306

  except  on E: Except i on do  begi n  Appl i cat i on. MessageBox( PChar( E. Message ) , ' Er r or de SQL' ,MB_I CONSTOP ) ;  Transacci on. Rol l back;

  end;  end;

  ShowMessage( ' Ti empo: ' + I ntToSt r ( Ti meGet Ti me - dwTi empo ) + 'mi l i segundos' ) ;  end;end;

En esta ocasión me ha tardado sólo 214 milisegundos, es decir, se ha reducidoal 5% del tiempo anterior sin saturar al motor de bases de datos abriendo ycerrando transacciones sin parar.

Este método puede aplicarse también para consultas con INSERT, SELECT yDELETE. En lo único en lo que hay que tener precaución es en no acumularmuchos datos en la transacción, ya que podría ser peor el remedio que laenfermedad.

Si teneis que actualizar cientos de miles de registros de una sola vez,recomiendo realizar un Commit cada 1000 registros para no saturar lamemoria caché de la transacción. Todo depende del número de campos quetengan las tablas así como el número de registros a modificar. Utilizad lafunción TimeGetTime para medir tiempos y sacar conclusiones.

Y si el proceso a realizar va a tardar más de 2 o 3 segundos utilizar barras deprogreso e hilos de ejecución, ya que algunos usuarios neuróticos podríancreer que nuestro programa se ha colgado y empezarían ha hacer clic comoposesos (os aseguro que existe gente así, antes de que termine la consultaSQL ya te están llamando por teléfono echándote los perros).

Pruebas realizadas con Firebird 2.0 y Dephi 7

Creando consultas SQL rápidas con IBSQLCuando se crea un programa para el mantenimiento de tablas de bases dedatos (clientes, artículos, etc.) el método ideal es utilizar componentesClientDataSet como vimos anteriormente.

Pero hay ocasiones en las que es necesario realizar consultas rápidas en elservidor tales como incrementar existencias en almacén, generar recibosautomáticamente o incluso incrementar nuestros contadores de facturas sinutilizar generadores.

En ese caso el componente más rápido para bases de datos Interbase/Firebird

es IBSQL el cual permite realizar consultas SQL sin que estén vinculadas aningún componente visual. Vamos a ver unos ejemplos de inserción,

Page 20: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 20/306

modificación y eliminación de registros utilizando este componente.

INSERTANDO REGISTROS EN UNA TABLA

Aquí tenemos un ejemplo de insertar un registro en una tabla llamada

CLIENTES utilizando un objeto IBSQL llamado Consulta:

  wi t h Consul t a do  begi n  SQL. Cl ear ;  SQL. Add( ' I NSERT I NTO CLI ENTES' ) ;  SQL. Add( ' ( NOMBRE, NI F, I MPORTEPTE ) ' ) ;  SQL. Add( ' VALUES' ) ;  SQL. Add( ' ( ' ' ANTONI O GARCI A LOPEZ' ' , ' ' 46876283D' ' , 140. 23 ) ' ) ;

  Tr ansact i on. St ar t Tr ansact i on;

  t r y  ExecQuery;  Transact i on. Commi t ;  except  on E: Except i on do  begi n  Appl i cat i on. MessageBox( PChar( E. Message ) , ' Er r or de SQL' ,MB_I CONSTOP ) ;  Transacci on. Rol l back;  end;  end;  end;

Como puede apreciarse hemos tenido que abrir nosotros a mano la transacciónantes de ejecutar la consulta ya que el objeto IBSQL no la abreautomáticamente tal como ocurre en los componentes IBQuery.

Una vez ejecutada la consulta, si todo ha funcionado correctamente enviamosla transacción al servidor mediante el método Commit. En el caso de que fallemostramos el error y cancelamos la transacción utilizando el métodoRollBack.

MODIFICANDO LOS REGISTROS DE UNA TABLA

El método para modificar los registros es el mismo que para insertarlos:

  wi t h Consul t a do  begi n  SQL. Cl ear ;  SQL. Add( ' UPDATE CLI ENTES' ) ;  SQL. Add( ' SET NOMBRE = ' ' MARI A GUI LLEN ROJ O' ' , ' ) ;  SQL. Add( ' NI F = ' ' 69236724W' ' , ' ) ;  SQL. Add( ' I MPORTEPTE = 80. 65' ) ;  SQL. Add( ' WHERE I D=21963' ) ;

  Tr ansact i on. St ar t Tr ansact i on;

  t r y

Page 21: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 21/306

  ExecQuery;  Transact i on. Commi t ;  except  on E: Except i on do  begi n  Appl i cat i on. MessageBox( PChar( E. Message ) , ' Er r or de SQL' ,

MB_I CONSTOP ) ;  Transacci on. Rol l back;  end;  end;  end;

ELIMINANDO REGISTROS DE LA TABLA

Al igual que para las SQL para INSERT y UPDATE el procedimiento es el mismo:

  wi t h Consul t a do  begi n  SQL. Cl ear ;

  SQL. Add( ' DELETE FROM CLI ENTES' ) ;  SQL. Add( ' WHERE I D=21964' ) ;

  Tr ansact i on. St ar t Tr ansact i on;

  t r y  ExecQuery;  Transact i on. Commi t ;  except  on E: Except i on do  begi n  Appl i cat i on. MessageBox( PChar( E. Message ) , ' Er r or de SQL' ,MB_I CONSTOP ) ;  Transacci on. Rol l back;  end;  end;  end;

CONSULTANDO LOS REGISTROS DE UNA TABLA

El método de consultar los registros de una tabla mediante SELECT difiere delos que hemos utilizado anteriormente ya que tenemos que dejar latransacción abierta hasta que terminemos de recorrer todos los registros:

  wi t h Consul t a do  begi n  SQL. Cl ear ;  SQL. Add( ' SELECT * FROM CLI ENTES' ) ;  SQL. Add( ' ORDER BY I D' ) ;

  Tr ansact i on. St ar t Tr ansact i on;

  / / Ej ecut ar mos consul t a  t r y  ExecQuery;  except  on E: Except i on do  begi n  Appl i cat i on. MessageBox( PChar( E. Message ) , ' Er r or de SQL' ,MB_I CONSTOP ) ;

Page 22: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 22/306

  Transacci on. Rol l back;  end;  end;

  / / Recor r emos l os r egi st r os  whi l e not Eof do

  begi n  Memo. Li nes. Add( Fi el dByName( ' NOMBRE' ) . AsSt r i ng ) ;  Next ;  end;

  / / Cerr amos l a consul t a y l a t r ansacci ón  Cl ose;  Tr ansact i on. Act i ve : = Fal se;  end;

Aunque pueda parecer un coñazo el componente de la clase TIBSQL respestoa los componentes TIBTable o TIBQuery, su velocidad es muy superior aambos componentes, sobre todo cuando se ejecutan las consultassucesivamente.

En el próximo artículo veremos cómo utilizar parámetros en las consultas.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

Como poner una imagen de fondo en unaaplicación MDIEn un artículo anterior vimos como crear aplicaciones MDI gestionando

múltiples ventanas hijas dentro de la ventana padre.Una de las cosas que más dan vistosidad a una aplicación comercial es tenerun fondo con nuestra marca de fondo de la aplicación (al estilo Contaplus oFacturaplus).

Para introducir una imagen de fondo en la ventana padre MDI hay que hacer losiguiente:

- Introducir en la ventana padre (la que tiene la propiedad FormStyle aMDIForm) un componente de la clase TImage situado en la pestaña

Additional. Al componenente lo vamos a llamar Fondo.

- En dicha imagen vamos a cambiar la propidedad Align a alClient para queocupe todo el fondo del formulario padre.

- Ahora sólo falta cargar la imagen directamente:

Fondo. Pi ct ur e. LoadFromFi l e( ' c: \ i magenes\ f ondo. bmp' ) ;

El único inconveniente que tiene esto es que no podemos utilizar los eventosdel formulario al estar la imagen encima (Drag and Drop, etc).

Page 23: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 23/306

UTILIZANDO EL CANVAS

Otra forma de hacerlo sería poniendo el objeto TImage en medio delformulario pero de manera invisible (sin alClient). Después en el evento

OnPaint del formulario copiamos el contenido de la imagen TImage al fondodel formulario:

pr ocedur e TFormul ar i o. For mPai nt ( Sender: TObj ect ) ;var R: TRect ;begi n  R. Lef t : = 0;  R. Top : = 0;  R. Ri ght : = Fondo. Wi dt h;  R. Bot t om : = Fondo. Hei ght ;  Canvas. CopyRect ( R, Fondo. Canvas, R ) ;end;

Así podemos tener igualmente una imagen de fondo sin renunciar a loseventos del formulario (OnMouseMove, OnClick, etc.).

Pruebas realizadas en Dephi 7.

Leyendo los metadatos de una tabla deInterbase/FirebirdNo hay nada que cause más pereza a un programador que la actualización decampos en las bases de datos de nuestros clientes. Hay muchas maneras de

hacerlo: desde archivos de texto con metadatos, archivos SQL o inclusoactualizaciones por Internet con FTP o correo electrónico.

Yo lo que suelo hacer es tener un archivo GDB o FDB (según sea Interbase oFirebird respectivamente) que contiene todas las bases de datos vacías. Sitengo que ampliar algún campo lo hago sobre esta base de datos.

Después cuando tengo que actualizar al cliente lo que hago es mandarle miGDB vacío y mediante un pequeño programa que tengo hecho compara lasestructuras de la base de datos de mi GDB vacío y las del cliente, creandotablas y campos nuevos según las diferencias sobre ambas bases de datos.

Ahora bien, ¿Cómo podemos leer el tipo de campos de una tabla? Pues enprincipio tenemos que el componente IBDatabase tiene la funciónGetTableNames que devuelve el nombre de las tablas de una base de datos yla función GetFieldNames que nos dice los campos pertenecientes a una tablaen concreto. El problema radica en que no me dice que tipo de campo es(float, string, blob, etc).

LEYENDO LOS CAMPOS DE UNA TABLA

Para leer los campos de una tabla utilizo el componente de la clase TIBQuerysituado en la pestaña Interbase. Cuando este componente abre una tablacarga el nombre de los campos y su tipo en su propiedad FieldDefs. Voy a

Page 24: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 24/306

realizar una aplicación sencilla en la cual seleccionamos una base de datosInterbase o Firebird y cuando se elija una tabla mostrará sus metadatos deesta manera:

Va a contener los siguientes componentes:

- Un componente Edit con el nombre RUTADB que va a guardar la ruta de labase de datos.

- Un componente de la clase TOpenDialog llamado AbrirBaseDatos parabuscar la base de datos Firebird/Interbase.

- Un botón llamado BExaminar.

- Un componente ComboBox llamado TABLAS que guardará el nombre de lastablas de la base de datos.

- Un componente IBDatabase llamado BaseDatos que utilizaremos paraconectar.

- Un componente IBTransaction llamado Transaccion para asociarlo a la base

Page 25: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 25/306

de datos.

- Un componente IBQuery llamado IBQuery que utilizaremos para abrir unatabla.

- Y por último un campo Memo llamado Memo para mostrar la información delos metadatos.

Comenzemos a asignar lo que tiene que hacer cada componente:

- Hacemos doble clic sobre el componente BaseDatos y le damos de usuarioSYSDBA y password masterkey. Pulsamos Ok y desactivamos su propiedadLoginPrompt.

- Asignamos el componente Transaccion a los componentes BaseDatos yIBQuery.

- Asignamos el componente BaseDatos a los componentes Transaccion yIBQuery.

- En la propiedad Filter del componente AbrirBaseDatos ponemos:

Interbase|*.gdb|Firebird|*.fdb

- Al pulsar el botón Examinar hacemos lo siguiente:

pr ocedur e TFormul ar i o. BExami narCl i ck( Sender : TObj ect ) ;begi n  i f Abr i r BaseDat os. Execut e t hen  begi n  RUTADB. Text : = Abr i r BaseDat os. Fi l eName;  BaseDat os. Dat abaseName : = ' 127. 0. 0. 1: ' + RUTADB. Text ;

  t r y  BaseDat os. Open;  except  on E: Except i on do  Appl i cat i on. MessageBox( PChar( E. Message ) , ' Er r or al abr i r

base de datos' ,  MB_I CONSTOP ) ;  end;

  BaseDat os. GetTabl eNames( TABLAS. I t ems, Fal se ) ;  end;end;

Lo que hemos hecho es abrir la base de datos y leer el nombre de las tablasque guardaremos dentro del ComboBox llamado TABLAS.

Cuando el usuario seleccione una tabla y pulse el botón Mostrar hacemos lo

siguiente:

Page 26: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 26/306

pr ocedur e TFor mul ar i o. BMost r ar Cl i ck( Sender : TObj ect ) ;var  i : I nteger ;  sTi po: St r i ng;begi n

  wi t h I BQuer y do  begi n  Memo. Li nes. Add( ' CREATE TABLE ' + TABLAS. Text + ' ( ' ) ;

  SQL. Cl ear ;  SQL. Add( ' SELECT * FROM ' + TABLAS. Text ) ;  Open; 

f or i : = 0 t o Fi el dDef s. Count - 1 do  begi n  sTi po : = ' ' ;

  i f Fi el dDef s. I t ems[ i ] . Fi el dCl ass. Cl assName = ' TI BSt r i ngFi el d't hen  sTi po : = ' VARCHAR( ' + I nt ToSt r ( Fi el dByName(Fi el dDef s. I t ems[ i ] . Name ) . Si ze ) + ' ) ' ;

  i f Fi el dDef s. I t ems[ i ] . Fi el dCl ass. Cl assName = ' TFl oat Fi el d' t hen  sTi po : = ' DOUBLE PRECI SI ON' ; / / Tambi én podr í a ser FLOAT ( 32bi t s) aunque pr ef i ero DOUBLE ( 64 bi t s)

  i f Fi el dDef s. I t ems[ i ] . Fi el dCl ass. Cl assName = ' TI nt eger Fi el d't hen  sTi po : = ' I NTEGER' ;

  i f Fi el dDef s. I t ems[ i ] . Fi el dCl ass. Cl assName = ' TDat eFi el d' t hen  sTi po : = ' DATE' ;

  i f Fi el dDef s. I t ems[ i ] . Fi el dCl ass. Cl assName = ' TTi meFi el d' t hen  sTi po : = ' TI ME' ;

  i f Fi el dDef s. I t ems[ i ] . Fi el dCl ass. Cl assName = ' TDat eTi meFi el d't hen  sTi po : = ' TI MESTAMP' ;

  i f Fi el dDef s. I t ems[ i ] . Fi el dCl ass. Cl assName = ' TBl obFi el d' t hen  sTi po : = ' BLOB' ;

  / / ¿Es un campo obl i gator i o?  i f Fi el dByName( Fi el dDef s. I t ems[ i ] . Name ) . Requi r ed t hen

  sTi po : = sTi po + ' NOT NULL' ;

  Memo. Li nes. Add( ' ' + Fi el dDef s. I t ems[ i ] . Name + ' ' + sTi po ) ;

  / / Si no es el úl t i mo campo añadi mos una coma al f i nal  i f i < Fi el dDef s. Count - 1 t hen  Memo. Li nes[ Memo. Li nes. Count - 1] : = Memo. Li nes[ Memo. Li nes. Count -1] + ' , ' ;  end;

  Memo. Li nes. Add( ' ) ' ) ;

  Cl ose;

  Tr ansact i on. Act i ve : = Fal se;

Page 27: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 27/306

  end;end;

Lo que hace este procedimiento es abrir con el componente IBQuery la tablaseleccionada y según los tipos de campos creamos la SQL de creación de latabla.

Este método también nos podría ser útil para hacer un programa que copiedatos entre tablas Interbase/Firebird.

Pruebas realizadas con Firebird 2.0 y Delphi 7.

Generando números aleatoriosLas funciones de las que disponen los lenguajes de programación para generarnúmeros aleatorios se basan en una pequeña semilla según la fecha delsistema y a partir de ahí se van aplicando una serie de fórmulas se vangenerando números al azar según los milisegundos que lleva el PC arrancado.

Dephi dispone de la función Random para generar números aleatorios entre 0y el parámetro que se le pase. Pero para que la semilla no sea siempre lamisma es conveniente inicializarla utilizando el procedimiento Randomize.

Por ejemplo, si yo quisiera inventarme 10 números del 1 y 100 haría losiguiente:

pr ocedur e TFor mul ar i o. I nventar10Numeros;var  i : I nteger ;begi n  Randomi ze;

  f or i : = 1 t o 10 do  Memo. Li nes. Add( I nt ToSt r ( Random( 100 ) + 1 ) ) ;end;

El resultado lo he volcado a un campo Memo.

INVENTANDO LOS NUMEROS DE LA LOTOSupongamos que quiero hacer el típico programa que generaautomáticamente las combinaciones de la loto. El método es tan simple comohemos visto anteriormente:

pr ocedur e TFor mul ar i o. I nvent ar Lot o;var  i : I nteger ;begi n  Randomi ze;

  f or i : = 1 t o 6 do  Memo. Li nes. Add( I nt ToSt r ( Random( 49 ) + 1 ) ) ;end;

Page 28: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 28/306

Pero así como lo genera es algo chapucero. Primero tenemos el problema deque los números inventados no los ordena y luego podría darse el caso de elordenador se invente un número dos veces.

Para hacerlo como Dios manda vamos a crear la clase TSorteo encargada deinventarse los 6 números de la loto y el complementario. Además lo vamos ahacer como si fuera de verdad, es decir, vamos a crear un bombo, le vamos aintroducir las 49 bolas y el programa las va a agitar y sacará una al azar. Y porúltimo también daremos la posibilidad de excluir ciertos números del bombo(por ejemplo el 1 y 49 son los que menos salen por estadística).

Comencemos creando la clase TSorteo en la sección type:

type  TSor t eo = cl ass  publ i c  Fecha: TDat e;  Numer os: TSt r i ngLi st ;  Compl ement ari o: St r i ng;  Excl ui dos: TSt r i ngLi st ;

  const r uct or Cr eat e;  dest r uct or Dest r oy; over r i de;  pr ocedur e I nvent ar;  pr ocedur e Excl ui r ( sNumero: St r i ng ) ;  end;

Como podemos ver en la clase los números inventados y los excluidos los voy a

meter en un StringList. El constructor de la clase TSorteo va a crear ambosStringList:

const r uct or TSor t eo. Cr eat e;begi n  Numeros : = TSt r i ngLi st . Cr eat e;  Excl ui dos : = TSt r i ngLi st . Cr eat e;end;

Y el destructor los liberará de memoria:

dest r uct or TSort eo. Dest r oy;

begi n  Excl ui dos. Free;  Numer os. Fr ee;end;

Nuestra clase TSorteo también va a incluir un método para excluir del sorteoel número que queramos:

pr ocedur e TSor t eo. Excl ui r ( sNumer o: St r i ng ) ;begi n  / / Ant es de excl ui r l o compr obamos si ya l o est a  i f Excl ui dos. I ndexOf ( sNumer o ) = - 1 t hen  Excl ui dos. Add( sNumero ) ;

end;

Page 29: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 29/306

Y aquí tenemos la función que se inventa el sorteo evitando los excluidos:

pr ocedur e TSor t eo. I nvent ar ;var  Bombo: TSt r i ngLi st ;

  i , i Pos1, i Pos2: I nt eger ;  sNumero, sBol a: St r i ng;begi n  / / Met emos l as 49 bol as en el bombo  Bombo : = TSt r i ngLi st . Cr eat e;  Numeros. Cl ear;

  f or i : = 1 t o 49 do  begi n  sNumero : = Compl et arCodi go( I nt ToSt r ( i ) , 2 ) ;

  i f Excl ui dos. I ndexOf ( sNumero ) = - 1 t hen  Bombo. Add( sNumer o ) ;

  end;

  / / Agi t amos l as bol as con el método de l a bur buj a  i f Bombo. Count > 0 t hen  f or i : = 1 t o 10000 + Random( 10000 ) do  begi n  / / Nos i nventamos dos posi ci ones di st i ntas en el bombo  i Pos1 : = Random( Bombo. Count ) ;  i Pos2 : = Random( Bombo. Count ) ;

  i f ( i Pos1 >= 0 ) and ( i Pos1 <= 49 ) and ( i Pos2 >= 0 ) and (i Pos2 <= 49 ) t hen  begi n

  / / I nt ercambi amos l as bol as en esas dos posi ci ones i nvent adas  sBol a : = Bombo[ i Pos1] ;  Bombo[ i Pos1] : = Bombo[ i Pos2] ;  Bombo[ i Pos2] : = sBol a;  end;  end;

  / / Vamos sacando l as 6 bol as al azar + compl ement ar i o  f or i : = 0 t o 6 do  begi n  i f Bombo. Count > 0 t hen  i Pos1 : = Random( Bombo. Count )  el se

  i Pos1 : = - 1;  i f ( i Pos1 >= 0 ) and ( i Pos1 <= 49 ) t hen  sBol a : = Bombo[ i Pos1]  el se  sBol a : = ' ' ;

  / / ¿Es el compl ement ar i o?  i f i = 6 t hen  / / Lo sacamos apar t e  Compl ement ar i o : = sBol a  el se  / / Lo met emos en l a l i st a de números

  Numeros. Add( sBol a ) ;  / / Sacamos l a bol a ext r ai da del bombo

Page 30: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 30/306

  i f ( i Pos1 >= 0 ) and ( i Pos1 <= 49 ) and ( Bombo. Count > 0 ) t hen  Bombo. Del ete( i Pos1 ) ;  end;

  / / Or denamos l os 6 númer os  Numeros. Sor t ;

  Bombo. Fr ee;end;

El procedimiento Inventar hace lo siguiente:

1º Crea un bombo dentro de un StringList y le mete las 49 bolas.

2º Elimina los número excluidos si los hay (los excluidos hay que meterlos endos cifras, 01, 07, etc.)

3º Agita los números dentro del bombo utilizando del método de la burbujapara que queden todas las bolas desordenadas.

4º Extrae las 6 bolas y el complementario eligiendo a azar dos posiciones delStringList para hacerlo todavía mas rebuscado. Al eliminar la bola extraidaevitamos así números repetidos, tal como si fuera el sorteo real.

5º Una vez inventados los números los deposita en el StringList llamadoNumeros y elimina de memoria el Bombo.

Ahora vamos a utilizar nuestra clase TSorteo para generar una combinación:

pr ocedur e TFor mul ar i o. I nvent ar Sor t eo;var  S: TSor t eo;begi n  Randomi ze;  S : = TSort eo. Cr eat e;  S. I nvent ar ;  Memo. Li nes. Add( S. Numeros. Text ) ;  S. Free;end;

Si quisiera excluir del sorteo los número 1 y 49 haría lo siguiente:

var  S: TSor t eo;begi n  Randomi ze;  S : = TSort eo. Cr eat e;  S. Excl ui r ( ' 01' ) ;  S. Excl ui r ( ' 49' ) ;  S. I nvent ar ;  Memo. Li nes. Add( S. Numeros. Text ) ;  S. Free;end;

Este es un método simple para generar los números de la loto pero lasvariantes que se pueden hacer del mismo son infinitas. Ya depende de la

Page 31: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 31/306

imaginación de cada cual y del uso que le vaya a dar al mismo.

Igualmente sería sencillo realizar algunas modificaciones para inventar otrossorteos tales como el gordo de la primitiva, el sorteo de los euromillones o laquiniela de fútbol.

Moviendo sprites con el teclado y el ratónBasándome en el ejemplo del artículo anterior que mostraba como realizar elmovimiento de sprites con fondo vamos a ver como el usuario puede mover lossprites usando el teclado y el ratón.

CAPTURANDO LOS EVENTOS DEL TECLADO

La clase TForm dispone de dos eventos para controlar las pulsaciones deteclado: OnKeyDown y OnKeyUp. Necesitamos ambos eventos porque no sólo

me interesa saber cuando un usuario ha pulsado una tecla sino tambiéncuando la ha soltado (para controlar las diagonales).

Para hacer esto voy a crear cuatro variables booleanas en la sección private elformulario que me van a informar de cuando están pulsadas las teclas delcursor:

type  pr i vat e  { Pr i vat e decl ar at i ons }

  Spr i t e: TSpr i t e;  Buf f er , Fondo: TI mage;  bDer echa, bI zqui er da, bAr r i ba, bAbaj o: Bool ean;

Estas variables las voy a actualizar en el evento OnKeyDown:

pr ocedur e TFor mul ar i o. FormKeyDown( Sender : TObj ect ; var Key: Word;Shi f t : TShi f t St at e ) ;begi n  case key of   VK_LEFT: bI zqui erda : = True;  VK_DOWN: bAbaj o : = Tr ue;  VK_UP: bAr r i ba : = True;

  VK_RI GHT: bDer echa : = Tr ue;  end;end;

y en el evento OnKeyUp:

pr ocedur e TFormul ar i o. FormKeyUp( Sender : TObj ect ; var Key: Word;Shi f t : TShi f t St at e ) ;begi n  case key of   VK_LEFT: bI zqui erda : = Fal se;  VK_DOWN: bAbaj o : = Fal se;  VK_UP: bAr r i ba : = Fal se;  VK_RI GHT: bDer echa : = Fal se;

Page 32: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 32/306

  end;end;

Al igual que hice con el ejemplo anterior voy a utilizar un temporizador(TTimer) llamado TmpTeclado con un intervalo que va a ser también de 10milisegundos y cuyo evento

OnTimerva a encargarse de dibujar el sprite en

pantalla:

pr ocedur e TFor mul ar i o. TmpTecl adoTi mer ( Sender : TObj ect ) ;begi n  / / ¿Ha pul sado l a t ecl a i zqui er da?  i f bI zqui er da t hen  i f Spr i t e. x > 0 t hen  Dec( Spr i t e. x ) ;

  / / ¿Ha pul sado l a t ecl a ar r i ba?  i f bAr r i ba t hen  i f Spr i t e. y > 0 t hen

  Dec( Spr i t e. y ) ;  / / ¿Ha pul sado l a t ecl a derecha?  i f bDerecha t hen  i f Spr i t e. x + Spr i t e. I magen. Wi dt h < Cl i ent Wi dt h t hen  I nc ( Spr i te. x ) ;

  / / ¿Ha pul sado l a t ecl a abaj o?  i f bAbaj o t hen  i f Spr i t e. y + Spr i t e. I magen. Hei ght < Cl i ent Hei ght t hen  I nc ( Spr i te. y ) ;

  Di buj ar Spr i t e;end;

Este evento comprueba la pulsación de todas las teclas controlando que elsprite no se salga del formulario. El procedimiento de DibujarSprite sería elsiguiente:

pr ocedur e TFor mul ar i o. Di buj arSpr i t e;var  Or i gen, Dest i no: TRect ;begi n  / / Copi amos el f ondo de pant al l a al buf f er  Or i gen. Lef t : = Spr i t e. x;  Or i gen. Top : = Spr i t e. y;  Or i gen. Ri ght : = Spr i t e. x + Spr i t e. I magen. Wi dt h;  Or i gen. Bott om: = Spr i t e. y + Spr i t e. I magen. Hei ght ;  Dest i no. Lef t : = 0;  Dest i no. Top : = 0;  Dest i no. Ri ght : = Spr i t e. I magen. Wi dt h;  Dest i no. Bot t om : = Spr i t e. I magen. Hei ght ;  Buf f er . Canvas. CopyMode : = cmSrcCopy;  Buf f er. Canvas. CopyRect ( Dest i no, Fondo. Canvas, Or i gen ) ;

  / / Di buj amos el spr i t e en el buf f er enci ma del f ondo copi ado  Spr i t e. Di buj ar ( 0, 0, Buf f er . Canvas ) ;

  / / Di buj amos el cont eni do del buf f er a l a pant al l a

  Canvas. Dr aw( Spr i t e. x, Spr i t e. y, Buf f er . Pi ct ur e. Gr aphi c ) ;end;

Page 33: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 33/306

Prácticamente es el mismo visto en el artículo anterior. Ya sólo hace faltaponer el marcha el mecanismo que bien podría ser en el evento OnCreate delformulario:

begi n  TmpTecl ado. Enabl ed : = True;  Spr i t e. x : = 250;  Spr i t e. y : = 150;end;

CAPTURANDO LOS EVENTOS DEL RATON

Para capturar las coordenadas del ratón vamos a utilizar el eventoOnMouseMove del formulario:

pr ocedur e TFor mul ar i o. For mMouseMove( Sender: TObj ect ; Shi f t :

 TShi f t St at e; X, Y: I nt eger ) ;begi n  Spr i t e. x : = X;  Spr i t e. y : = Y;end;

Para controlar los eventos del ratón voy a utilizar un temporizador distinto aldel teclado llamado TmpRaton con un intervalo de 10 milisegundos. Su eventoOnTimer sería sencillo:

pr ocedur e TFormul ar i o. TmpRat onTi mer ( Sender : TObj ect ) ;begi n  Di buj ar Spr i t e;end;

Aquí nos surge un problema importante: como los movimientos del ratón sonmás bruscos que los del teclado volvemos a tener el problema de que el spritenos va dejando manchas en pantalla. Para solucionar el problema tenemosque restaurar el fondo de la posición anterior del sprite antes de dibujarlo enla nueva posición..

Para ello voy a guardar en la clase TSprite las coordenadas anteriores:

type

  TSpr i t e = cl ass  publ i c  x, y, xAnt er i or , yAnt er i or : I nt eger ;  Col or Tr anspar ent e: TCol or;  I magen, Mascar a: TI mage;  const r uct or Cr eat e;  dest r uct or Dest r oy; over r i de;  pr ocedur e Car gar( sI magen: st r i ng ) ;  pr ocedur e Di buj ar ( x, y: I nt eger ; Canvas: TCanvas ) ;  end;

Al procedimiento DibujarSprite le vamos a añadir que restaure el fondo del

sprite de la posición anterior:pr ocedur e TFor mul ar i o. Di buj arSpr i t e;

Page 34: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 34/306

var  Or i gen, Dest i no: TRect ;begi n  / / Rest aur amos el f ondo de l a posi ci ón ant eri or del spr i t e  i f ( Spr i t e. xAnt er i or <> Spr i t e. x ) or ( Spr i t e. yAnt er i or <>Spr i t e. y ) t hen

  begi n  Or i gen. Lef t : = Spr i t e. xAnt er i or ;  Or i gen. Top : = Spr i t e. yAnt er i or ;  Or i gen. Ri ght : = Spr i t e. xAnt er i or + Spr i t e. I magen. Wi dt h;  Or i gen. Bot t om : = Spr i t e. yAnt er i or + Spr i t e. I magen. Hei ght ;  Dest i no : = Or i gen;  Canvas. CopyMode : = cmSr cCopy;  Canvas. CopyRect ( Dest i no, Fondo. Canvas, Or i gen ) ;  end;

  / / Copi amos el f ondo de pant al l a al buf f er  Or i gen. Lef t : = Spr i t e. x;  Or i gen. Top : = Spr i t e. y;  Or i gen. Ri ght : = Spr i t e. x + Spr i t e. I magen. Wi dt h;  Or i gen. Bott om: = Spr i t e. y + Spr i t e. I magen. Hei ght ;  Dest i no. Lef t : = 0;  Dest i no. Top : = 0;  Dest i no. Ri ght : = Spr i t e. I magen. Wi dt h;  Dest i no. Bot t om : = Spr i t e. I magen. Hei ght ;  Buf f er . Canvas. CopyMode : = cmSrcCopy;  Buf f er. Canvas. CopyRect ( Dest i no, Fondo. Canvas, Or i gen ) ;

  / / Di buj amos el spr i t e en el buf f er enci ma del f ondo copi ado  Spr i t e. Di buj ar ( 0, 0, Buf f er . Canvas ) ;

  / / Di buj amos el cont eni do del buf f er a l a pant al l a  Canvas. Dr aw( Spr i t e. x, Spr i t e. y, Buf f er . Pi ct ur e. Gr aphi c ) ;

  Spr i t e. xAnt er i or : = Spr i t e. x;  Spr i t e. yAnt er i or : = Spr i t e. y;end;

Y finalmente activamos el temporizador que controla el ratón y ocultamos elcursor del ratón para que no se superponga encima de nuestro sprite:

begi n  TmpRat on. Enabl ed : = Tr ue;  Spr i t e. x : = 250;  Spr i t e. y : = 150;

  ShowCur sor ( Fal se ) ;end;

Al ejecutar el programa podeis ver como se mueve el sprite como si fuera elcursor del ratón.

Aunque se pueden hacer cosas bonitas utilizando el Canvas no os hagaismuchas ilusiones ya que si por algo destaca la librería GDI de Windows (elCanvas) es por su lentitud y por la diferencia de velocidad entre ordenadores.

Para hacer cosas serías habría que irse a la librerías SDL (mi favorita),

OpenGL o DirectX ( aunque hay decenas de motores gráficos 2D y 3D paraDelphi en Internet que simplifican el trabajo).

Page 35: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 35/306

Pruebas realizadas en Delphi 7.

Mover sprites con doble bufferEn el artículo anterior creamos la clase TSprite encargada de dibujar figurasen pantalla. Hoy vamos a reutilizarla para mover sprites, pero antes vamos ahacer una pequeña modificación:

type  TSpr i t e = cl ass  publ i c  x, y: I nt eger ;  Col or Tr anspar ent e: TCol or;

  I magen, Mascar a: TI mage;  const r uct or Cr eat e;  dest r uct or Dest r oy; over r i de;  pr ocedur e Car gar( sI magen: st r i ng ) ;  pr ocedur e Di buj ar ( x, y: I nt eger ; Canvas: TCanvas ) ;  end;

Sólo hemos modificado el evento Dibujar añadiendo las coordenadas de dondese va a dibujar (independientemente de las que tenga el sprite). Laimplementación de toda la clase TSprite quedaría de esta manera:

{ TSpr i t e }

const r uct or TSpr i t e. Cr eat e;begi n  i nher i t ed;  I magen : = TI mage. Cr eat e( ni l ) ;  I magen. AutoSi ze : = True;  Mascar a : = TI mage. Cr eat e( ni l ) ;  Col orTranspar ent e : = RGB( 255, 0, 255 ) ;end;

dest r uct or TSpr i t e. Dest r oy;begi n  Mascara. Fr ee;

  I magen. Fr ee;  i nher i t ed;end;

pr ocedur e TSpr i t e. Car gar( sI magen: st r i ng ) ;var  i , j : I nt eger ;begi n  I magen. Pi ct ur e. LoadFr omFi l e( sI magen ) ;  Mascar a. Wi dth : = I magen. Wi dth;  Mascara. Hei ght : = I magen. Hei ght ;

  f or j : = 0 t o I magen. Hei ght - 1 do

  f or i : = 0 t o I magen. Wi dt h - 1 do  i f I magen. Canvas. Pi xel s[ i , j ] = Col or Tr anspar ent e t hen  begi n

Page 36: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 36/306

  I magen. Canvas. Pi xel s[ i , j ] : = 0;  Mascar a. Canvas. Pi xel s[ i , j ] : = RGB( 255, 255, 255 ) ;  end  el se  Mascar a. Canvas. Pi xel s[ i , j ] : = RGB( 0, 0, 0 ) ;end;

pr ocedur e TSpr i t e. Di buj ar ( x, y: I nt eger ; Canvas: TCanvas ) ;begi n  Canvas. CopyMode : = cmSrcAnd;  Canvas. Dr aw( x, y, Mascara. Pi ct ur e. Gr aphi c ) ;  Canvas. CopyMode : = cmSrcPai nt ;  Canvas. Dr aw( x, y, I magen. Pi ct ur e. Gr aphi c ) ;end;

CREANDO EL DOBLE BUFFER 

Cuando se mueven figuras gráficas en un formulario aparte de producirse

parpadeos en el sprite se van dejando rastros de las posiciones anteriores.Sucede algo como esto:

Para evitarlo hay muchísimas técnicas tales como el doble o triple buffer.Aquí vamos a ver como realizar un doble buffer para mover sprites. Elformulario va a tener el siguiente fondo:

El fondo tiene unas dimensiones de 500x300 pixels. Para ajustar el fondo alformulario configuramos las siguientes propiedades en el inspector de objetos:

For mul ari o. Cl i ent Wi dt h = 500For mul ar i o. Cl i ent Hei ght = 300

Se llama doble buffer porque vamos a crear dos imágenes:

Fondo, Buf f er: TI mage;

El Fondo guarda la imagen de fondo mostrada anteriormente y el Buffer va aencargarse de mezclar el sprite con el fondo antes de llevarlo a la pantalla.

Page 37: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 37/306

Los pasos para dibujar el sprite serían los siguientes:

1º Se copia un trozo del fondo al buffer.

2º Se copia el sprite sin fondo encima del buffer.

3º Se lleva el contenido del buffer a pantalla.

Lo primero que vamos a hacer es declarar en la sección private del formulariolos objetos:

  pr i vat e  { Pr i vat e decl ar at i ons }  Spr i t e: TSpr i t e;  Buf f er , Fondo: TI mage;

Después los creamos en el evento OnCreate del formulario:

pr ocedur e TFor mul ar i o. For mCr eat e( Sender : TObj ect ) ;begi n  Spr i t e : = TSpr i t e. Cr eat e;  Spr i t e. Car gar ( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) + ' spr i t e. bmp') ;  Buf f er : = TI mage. Cr eat e( ni l ) ;  Buf f er . Wi dt h : = Spr i t e. I magen. Wi dt h;  Buf f er . Hei ght : = Spr i t e. I magen. Hei ght ;  Fondo : = TI mage. Cr eat e( ni l ) ;  Fondo. Pi ct ur e. LoadFromFi l e( Extr actFi l ePat h( Appl i cat i on. ExeName ) +

' f ondo. bmp' ) ;end;

Page 38: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 38/306

El fondo también lo he creado como imagen BMP en vez de JPG para poderutilizar la función CopyRect del Canvas. Nos aseguramos de que en el eventoOnDestroy del formulario se liberen de memoria:

pr ocedur e TFor mul ar i o. For mDest r oy( Sender : TObj ect ) ;begi n  Spr i t e. Fr ee;  Buf f er . Free;  Fondo. Fr ee;end;

Aunque podemos mover el sprite utilizando un bucle for esto podría dejarnuestro programa algo pillado. Lo mejor es moverlo utilizando un objeto de laclase TTimer. Lo introducimos en nuestro formulario con el nombreTemporizador. Por defecto hay que dejarlo desactivado (Enabled = False) yvamos a hacer que se mueva el sprite cada 10 milisegundos (Invertal = 10).

En el evento OnTimer hacemos que se mueva el sprite utilizando los pasosmencionados:

pr ocedur e TFSpr i t es. Tempor i zadorTi mer ( Sender : TObj ect ) ;var  Or i gen, Dest i no: TRect ;begi n  i f Spr i t e. x < 400 t hen  begi n  I nc( Spr i t e. x ) ;

  / / Copi amos el f ondo de pant al l a al buf f er  Or i gen. Lef t : = Spr i t e. x;  Or i gen. Top : = Spr i t e. y;  Or i gen. Ri ght : = Spr i t e. x + Spr i t e. I magen. Wi dt h;  Or i gen. Bot t om : = Spr i t e. y + Spr i t e. I magen. Hei ght ;  Dest i no. Lef t : = 0;  Dest i no. Top : = 0;  Dest i no. Ri ght : = Spr i t e. I magen. Wi dt h;  Dest i no. Bot t om : = Spr i t e. I magen. Hei ght ;  Buf f er . Canvas. CopyMode : = cmSrcCopy;  Buf f er . Canvas. CopyRect ( Dest i no, Fondo. Canvas, Or i gen ) ;

  / / Di buj amos el spr i t e en el buf f er enci ma del f ondo copi ado  Spr i t e. Di buj ar ( 0, 0, Buf f er . Canvas ) ;

  / / Di buj amos el cont eni do del buf f er a l a pant al l a  Canvas. Dr aw( Spr i t e. x, Spr i t e. y, Buf f er . Pi cture. Gr aphi c ) ;  end  el se  Tempor i zador . Enabl ed : = Fal se;end;

En el evento OnPaint del formulario tenemos que hacer que se dibuje elfondo:

pr ocedur e TFormul ar i o. For mPai nt ( Sender: TObj ect ) ;

begi n  Canvas. Dr aw( 0, 0, Fondo. Pi ct ur e. Gr aphi c ) ;end;

Page 39: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 39/306

Esto es necesario por si el usuario minimiza y vuelve a mostrar la aplicación,ya que sólo se refresca la zona por donde está moviéndose el sprite.

Por fin hemos conseguido el efecto deseado:

Pruebas realizadas en Delphi 7.

Como dibujar sprites transparentesUn Sprite es una figura gráfica móvil utilizada en los videojuegos de dosdimensiones. Por ejemplo, un videojuego de naves espaciales consta de lossprites de la nave, los meteoritos, los enemigos, etc., es decir, todo lo quesea móvil en pantalla y que no tenga que ver con los paisajes de fondo.

Anteriormente vimos como copiar imágenes de una superficie a otrautilizando los métodos Draw o CopyRect que se encuentran en la claseTCanvas. También se vió como modificar el modo de copiar mediante lapropiedad CopyMode la cual permitia los valores cmSrcCopy, smMergePaint,

etc.

El problema radica en que por mucho que nos empeñemos en dibujar unaimagen transparente ningún tipo de copia funciona: o la hace muytransparente o se estropea el fondo.

DIBUJAR SPRITES MEDIANTE MASCARAS

Para dibujar sprites transparentes hay que tener dos imágenes: la originalcuyo color de fondo le debemos dar alguno como común (como el negro) y laimagen de la máscara que es igual que la original pero como si fuera un

negativo.

Supongamos que quiero dibujar este sprite (archivo BMP):

Es conveniente utilizar de color transparente un color de uso como común. Eneste caso he elegido el color rosa cuyos componentes RGB son (255,0,255). Lamáscara de esta imagen sería la siguiente:

Page 40: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 40/306

Esta máscara no es necesario crearla en ningún programa de dibujo ya que lavamos a crear nosotros internamente. Vamos a encapsular la creación del

sprite en la siguiente clase:

type  TSpr i t e = cl ass  publ i c  x, y: I nt eger ;  Col or Tr anspar ent e: TCol or;  I magen, Mascar a: TI mage;  const r uct or Cr eat e;  dest r uct or Dest r oy; over r i de;  pr ocedur e Car gar( sI magen: st r i ng ) ;

  pr ocedur e Di buj ar( Canvas: TCanvas ) ;  end;

Esta clase consta de las coordenadas del sprite, el color que definimos comotransparente y las imágenes a dibujar incluyendo su máscara. En elconstructor de la clase creo dos objetos de la clase TImage en memoria:

const r uct or TSpr i t e. Cr eat e;begi n  i nher i t ed;  I magen : = TI mage. Cr eat e( ni l ) ;  I magen. AutoSi ze : = True;  Mascar a : = TI mage. Cr eat e( ni l ) ;  Col orTranspar ent e : = RGB( 255, 0, 255 ) ;end;

También he definido el color rosa como color transparente. Como puedeapreciarse he utilizado el procedimiento RGB que convierte los trescomponentes del color al formato de TColor que los guarda al revés BGR . Asípodemos darle a Delphi cualquier color utilizando estos tres componentescopiados de cualquier programa de dibujo.

En el destructor de la clase nos aseguramos de que se liberen de memoriaambos objetos:

dest r uct or TSpr i t e. Dest r oy;begi n  Mascara. Fr ee;  I magen. Fr ee;  i nher i t ed;end;

Ahora implementamos la función que carga de un archivo la imagen BMP y acontinuación crea su máscara:

pr ocedur e TSpr i t e. Car gar( sI magen: st r i ng ) ;

var  i , j : I nt eger ;begi n

Page 41: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 41/306

  I magen. Pi ct ur e. LoadFr omFi l e( sI magen ) ;  Mascar a. Wi dth : = I magen. Wi dth;  Mascara. Hei ght : = I magen. Hei ght ;

  f or j : = 0 t o I magen. Hei ght - 1 do  f or i : = 0 t o I magen. Wi dt h - 1 do

  i f I magen. Canvas. Pi xel s[ i , j ] = Col or Tr anspar ent e t hen  begi n  I magen. Canvas. Pi xel s[ i , j ] : = 0;  Mascar a. Canvas. Pi xel s[ i , j ] : = RGB( 255, 255, 255 ) ;  end  el se  Mascar a. Canvas. Pi xel s[ i , j ] : = RGB( 0, 0, 0 ) ;end;

Aquí nos encargamos de dejar la máscara en negativo a partir de la imagenoriginal.

Para dibujar sprites recomiendo utilizar archivos BMP en lugar de JPG debidoa que este último tipo de imágenes pierden calidad y podrían afectar alresultado de la máscara, dando la sensación de que el sprite tiene manchas.Hay otras librerías para Delphi que permiten cargar imágenes PNG que sonideales para la creación de videojuegos, pero esto lo veremos en otra ocasión.

Una vez que ya tenemos nuestro sprite y nuestra máscara asociada al mismopodemos crear el procedimiento encargado de dibujarlo:

pr ocedur e TSpr i t e. Di buj ar ( Canvas: TCanvas ) ;begi n  Canvas. CopyMode : = cmSrcAnd;

  Canvas. Dr aw( x, y, Mascara. Pi ct ur e. Gr aphi c )  Canvas. CopyMode : = cmSrcPai nt ;  Canvas. Dr aw( x, y, I magen. Pi ct ur e. Gr aphi c ) ; }end;

El único parámetro que tiene es el Canvas sobre el que se va a dibujar elsprite. Primero utilizamos la máscara para limpiar el terreno y despuésdibujamos el sprite sin el fondo. Vamos a ver como utilizar nuestra clase paradibujar un sprite en el formulario:

pr ocedur e TFor mul ar i o. Di buj arCoche;var

  Spr i t e: TSpr i t e;begi n  Spr i t e : = TSpr i t e. Cr eat e;  Spr i t e. x : = 100;  Spr i t e. y : = 100;  Spr i t e. Car gar ( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) + ' spr i t e. bmp') ;  Spr i t e. Di buj ar ( Canvas ) ;  Spr i t e. Fr ee;end;

Este sería el resultado en el formulario:

Page 42: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 42/306

Si el sprite lo vamos a dibujar muchas veces no es necesario crearlo y

destruirlo cada vez. Deberíamos crearlo en el evento OnCreate del formularioy en su evento OnDestroy liberarlo (Sprite.Free).

En el próximo artículo veremos como mover el sprite en pantalla utilizandouna técnica de doble buffer para evitar parpadeos en el movimiento.

Pruebas realizadas en Delphi 7.

Creando tablas de memoria conClientDataSet

Una de las cosas que

más se necesitan en un programa de gestión es la posibilidad crear tablas dememoria para procesar datos temporalmente, sobre todo cuando los datosorigen vienen de tablas distintas.

Es muy común utilizar componentes de tablas de memoria tales como los quellevan los componentes RX (TRxMemoryData) o el componentekbmMemTable. Pues veamos como hacer tablas de memoria utilizando elcomponente de la clase TClientDataSet sin tener que utilizar ningúncomponente externo a Delphi.

DEFINIENDO LA TABLA

Lo primero es añadir a nuestro proyecto un componente ClientDataSet ya seaen un formulario o en un módulo de datos. Como vamos a crear una tabla derecibos lo vamos a llamar TRecibos.

Ahora vamos a definir los campos de los que se compone la tabla. Para ellopulsamos el botón [...] en la propiedad FieldDefs. En la ventana que se abrepulsamos el botón Add New y vamos creando los campos:

Name Dat aTpe Si ze- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Page 43: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 43/306

NUMERO f t I nteger 0CLI ENTE f t St r i ng 80I MPORTE f t Fl oat 0PAGADO f t Fl oat 0PENDI ENTE f t Fl oat 0

Para crear la tabla de memoria pulsamos el componente TClientDataSet conel botón derecho del ratón y seleccionamos Create DataSet. Una vez creadosólo nos falta hacer que los campos sean persistentes. Eso se consiguehaciendo doble clic sobre el componente y pulsando la combinación de teclasCTRL + A.

Con estos sencillos pasos ya hemos creado una tabla de memoria y yapodemos abrirla para introducir datos. No es necesario abrir la tabla ya queestas tablas de memoria hay que dejarlas activas por defecto.

DANDO FORMATO A LOS CAMPOS

Como tenemos tres campos de tipo real vamos a dar formato a los mismos delsiguiente modo:

1. Hacemos doble clic sobre el componente ClientDataSet.

2. Seleccionamos los campos IMPORTE, PAGADO y PENDIENTE.

3. Activamos en el inspector de objetos su propiedad Currency.

Con esto ya tenemos los campos en formato moneda y con decimales.REALIZANDO CALCULOS AUTOMATICAMENTE

A nuestra tabla de recibos le vamos a hacer que calcule automáticamente elimporte pendiente. Esto lo vamos a hacer antes de que se ejecute el Post, enel evento BeforePost:

pr ocedur e TFormul ar i o. TReci bosBef orePost ( Dat aSet : TDat aSet ) ;begi n  TReci bosPENDI ENTE. AsFl oat : = TReci bosI MPORTE. AsFl oat -

 TReci bosPAGADO. AsFl oat ;

end;

De este modo, tanto si insertamos un nuevo registro como si lo modificamosrealizará el cálculo del importe pendiente antes de guardar el registro.

AÑADIENDO, EDITANDO Y ELIMINANDO REGISTROS DE LA TABLA

Insertamos tres registros:

begi n  TReci bos. Append;  TReci bosNUMERO. AsI nteger : = 1;  TReci bosCLI ENTE. AsSt r i ng : = ' TRANSPORTES PALAZON, S. L. ' ;  TReci bosI MPORTE. AsFl oat : = 1500;

Page 44: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 44/306

  TReci bosPAGADO. AsFl oat : = 500;  TReci bos. Post ;

  TReci bos. Append;  TReci bosNUMERO. AsI nteger : = 2;  TReci bosCLI ENTE. AsSt r i ng : = ' TALLERES CHAPI NET, S. L. ' ;

  TReci bosI MPORTE. AsFl oat : = 200;  TReci bosPAGADO. AsFl oat : = 200;  TReci bos. Post ;

  TReci bos. Append;  TReci bosNUMERO. AsI nteger : = 3;  TReci bosCLI ENTE. AsSt r i ng : = ' GRUAS MARTI NEZ, S. L. ' ;  TReci bosI MPORTE. AsFl oat : = 625;  TReci bosPAGADO. AsFl oat : = 350;  TReci bos. Post ;end;

Si queremos modificar el primer registro:

begi n  TReci bos. Fi r st;  TReci bos. Edi t ;  TReci bosCLI ENTE. AsSt r i ng : = ' ANTONI O PEREZ BERNAL' ;  TReci bosI MPORTE. AsFl oat : = 100;  TReci bosPAGADO. AsFl oat : = 55;  TReci bos. Post ;end;

Y para eliminarlo:

begi n  TReci bos. Fi r st;  TReci bos. Del et e;end;

MODIFICANDO LOS CAMPOS DE LA TABLA

Si intentamos añadir un nuevo campo a la tabla en FieldDefs y luego pulsamosCTRL + A para hacer el campo persistente veremos que desaparece de ladefinición de campos. Para hacerlo correctamente hay que hacer lo siguiente:

1. Pulsamos el componente ClientDataSet con el botón derecho del ratón.

2. Seleccionamos Clear Data.

3. Añadimos el nuevo campo en FieldDefs.

4. Volvemos a pulsar el el botón derecho del ratón el componente yseleccionamos Create DataSet.

5. Pulsamos CTRL + A para hacer persistente el nuevo campo.

Estos son los pasos que hay que seguir si se crean, modifican o eliminancampos en la tabla.

Page 45: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 45/306

CREANDO CAMPOS VIRTUALES PARA SUMAR COLUMNAS

Vamos a crear tres campos virtuales que sumen automáticamente el valor delas columnas IMPORTE, PAGADO y PENDIENTE para totalizarlos. Comencemoscon el cálculo del importe total:

1. Pulsamos el componente ClientDataSet con el botón derecho del ratón.

2. Seleccionamos Clear Data.

3. Hacemos doble clic en el componente ClientDataSet.

4. Pulsamos la combinación de teclas CTRL + N para añadir un nuevo campo:

Name: TOTALIMPORTEFieldType: Agregate

5. Pulsamos Ok. Seleccionamos el campo creado escribimos en su propiedadExpression:

SUM( I MPORTE)

y activamos su propiedad Active. También activamos su propiedad Currency.

6. Creamos en el formulario un campo de tipo DBText y asociamos en nuevocampo creado:

DataSource: TRecibosDataField: TOTALIMPORTE

7. Volvemos a pulsar el el botón derecho del ratón el componente yseleccionamos Create DataSet.

8. Activamos en el componente TClientDataSet la propiedadAggregatesActive.

Igualmente habría que crear dos campos más para sumar las columnas delimporte pagado y el importe pendiente.

Utilizar ClientDataSet para crear tablas de memoria es ideal para procesarlistados en tablas temporales sin tener que volcar el resultado en ningunabase de datos. Además podemos importar y exportar datos a XML usando elmenú contextual de este componente.

Pruebas realizadas en Delphi 7.

Cómo crear un hilo de ejecuciónHay ocasiones en que necesitamos que nuestro programa realize

paralelamente algún proceso secundario que no interfiera en la aplicaciónprincipal, ya que si nos metemos en bucles cerrados o procesos pesados

Page 46: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 46/306

(traspaso de ficheros, datos, etc.) nuestra aplicación se queda medio muerta(no se puede ni mover la ventana, minimizarla y menos cerrarla).

Para ello lo que hacemos es crear un hilo de ejecución heredando de la claseTThread del siguiente modo:

 THi l o = cl ass( TThread )  Ej ecut ar : pr ocedur e of obj ect ;  pr ocedur e Execut e; overr i de;

end;

La definición anterior hay que colocarla dentro del apartado Type de nuestraunidad (en la sección interface). Le he añadido el procedimiento Ejecutarpara poder mandarle que procedimiento queremos que se ejecuteparalelamente.

En el apartado implementation de nuestra unidad redifinimos elprocedimiento de la clase TThread para que llame a nuestro procedimientoEjecutar:

pr ocedur e THi l o. Execut e;begi n  Ej ecut ar ;  Termi nat e;

end;

Con esto ya tenemos nuestra clase THilo para crear todos los hilos deejecución que nos de la gana. Ahora vamos a ver como se crea un hilo y sepone en marcha:

varHi l o: THi l o; / / var i abl e gl obal o públ i ca

pr ocedur e Cr earHi l o;begi n  Hi l o. Ej ecut ar : = Pr ocesar Dat os;  Hi l o. Pr i or i t y : = t pNor mal ;  Hi l o. Resume;

end;

pr ocedur e Pr ocesar Datos;begi n  / / Est e es el pr ocedi mi ent o que ej ecut ar á nuest r o hi l o  / / Cui dado con hacer pr ocesos cr í t i cos aquí   / / El pr ocesami ent o paral el o de XP no es el de Li nux  / / Se puede i r por l as pat as abaj o. . .

end;

Si en cualquier momento queremos detener la ejecución del hilo:

Hi l o. Ter mi nat e;FreeAndNi l ( Hi l o ) ;

Los hilos de ejecución sólo conviene utilizarlos en procesos críticos e

Page 47: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 47/306

importantes. No es conveniente utilizarlos así como así ya que se puedencomer al procesador por los piés.

Pruebas r eali zadas en Delphi 7 

Conectando a pelo con INTERBASE oFIREBIRDAunque Delphi contiene componentes para mostrar directamente datos de unatabla, en ocasiones nos obligan a mostrar el contenido de una base de datosen una página web o en una presentación multimedia con SDL, OPENGL óDIRECTX. En este caso, los componentes de la pestaña DATA CONTROLS nonos sirven de nada. Nos los tenemos que currar a mano.

Voy a mostraros un ejemplo de conexión con una base de datos de INTERBASE

o FIREBIRD mostrando el resultado directamente dentro de un componenteListView, aunque con unas modificaciones se puede lanzar el resultado a unarchivo de texto, página web, XML o lo que sea.

Lo primero es conectar con la base de datos:

f unct i on ConectarBaseDatos( sBaseDat os: St r i ng ) : TI BDatabase;var DB: TI BDat abase;begi n DB : = TI BDat abase. Cr eat e( ni l ) ;  DB. Name : = ' I B' ;  DB. Dat abaseName : = ' 127. 0. 0. 1: ' + sBaseDat os;  DB. Params. Add( ' user _name=SYSDBA' ) ;

  DB. Par ams. Add( ' password=mast erkey' ) ;  DB. SQLDi al ect : = 3;  DB. Logi nPr ompt : = Fal se;  t r y

  DB. Open;  except

  r ai se Except i on. Cr eat e( ' No puedo conect ar con I NTERBASE/ FI REBI RD. '+ #13 + #13 + ' Consul t e con el admi ni st r ador del progr ama. ' ) ;  end;

  Resul t : = DB;end;

Si nos fijamos en el procedimiento, primero se crea en tiempo real uncomponente de conexión a bases de datos TIBDatabase. Después le decimoscon que IP va a conectar (en principio en nuestra misma máquina) y la ruta dela base de datos que es la que se le pasa al procedimiento.

Más adelante le damos el usuario y password por defecto y desactivamos enLogin. Finalmente contectamos con la base de datos controlando la excepciónsi casca.

Un ejemplo de conexión sería:

var DB: TI BDat abase;

Page 48: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 48/306

DB : = ConectarBaseDatos( ' c: \ bases\ bases. gdb' ) ; / / PARA I NTERBASE ÓDB : = Conect ar BaseDat os( ' c: \ bases\ bases. f db' ) ; / / PARA FI REBI RD

i f DB = ni l t hen  Exi t ;

Una vez conectados a la base de datos vamos a ver como listar los registros deuna tabla dentro de un ListView:

pr ocedur e Li st ar Tabl a( DB: TI BDat abase; sTabl a: St r i ng; Li st ado: TLi st Vi ew ) ;var Campos: TSt r i ngLi st ;  i : I nt eger ;  Consul t a: TI BSQL;  Tr ansacci on: TI BTransact i on;

begi n  i f DB = ni l t hen Exi t ;

  / / Cr eamos un st r i ngl i st par a meter l os campos de l a t abl a

  Campos : = TSt r i ngLi st . Cr eat e;  DB. Get Fi el dNames( sTabl a, Campos ) ;

  / / Cr eamos una t r ansacci ón para l a consul t a  Tr ansacci on : = TI BTr ansact i on. Cr eat e( ni l ) ;  Transacci on. Def aul t Database : = DB;

  / / Cr eamos una consul t a  Consul t a : = TI BSQL. Cr eat e( ni l ) ;  Consul t a. Tr ansact i on : = Tr ansacci on;  Consul t a. SQL. Add( ' SELECT * FROM ' + sTabl a ) ;  Transacci on. St ar t Tr ansact i on;  t r y

  Consul t a. ExecQuer y;  except

  Tr ansacci on. Rol l back;  r ai se;  end;

  / / Cr eamos en el l i st vi ew una col umna por cada campo  Li st ado. Col umns. Cl ear;  Li st ado. Col umns. Add;  Li st ado. Col umns[ 0] . Wi dt h : = 0;  f or i : = 0 to Campos. Count - 1 do  begi n

  Li st ado. Col umns. Add;

  Li st ado. Col umns[ i +1] . Capt i on : = Campos[ i ] ;  Li st ado. Col umns[ i +1] . Wi dt h : = 100;  end;

  / / Li st amos l os r egi str os  Li st ado. Cl ear ;  whi l e not Consul t a. Eof do  begi n

  Li st ado. I t ems. Add;

  f or i : = 0 t o Campos. Count - 1 do  Li st ado. I t ems[ Li st ado. I t ems. Count - 1] . SubI t ems. Add(Consul t a. Fi el dByName(

Campos[ i ] ) . AsSt r i ng ) ;

  Consul t a. Next ;

Page 49: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 49/306

  end;

  / / Una vez hemos t ermi nado l i beramos l os obj et os cr eados  FreeAndNi l ( Campos ) ;  FreeAndNi l ( Consul t a ) ;  FreeAndNi l ( Tr ansacci on ) ;

end;

Por supuesto, todo este proceso se puede mejorar refactorizando código ydividiendo las partes más importantes en clases más pequeñas. Haciendomuchas pruebas con objetos TIBSQL y TIBQuery me he dado cuenta que paraoperaciones donde se requiere velocidad los objetos TIBSQL con mucho másrápidos que los TIBQuery, aunque estos últimos son mucho más completos.

Pruebas r eali zadas en Delphi 7 

Efectos de animación en las ventanasEn este artículo explicaré de forma detallada cómo crear animaciones para lasventanas de delphi con los mismos efectos que disponen los sistemasoperativos Windows, y aclararé cuándo aplicarlos y los problemas que tienen.

La función encargada de animar ventanas es la siguiente (api de windows):

AnimateWindow

Y los parámetros que la definen son los siguientes:

hWnd - Manejador o Handle de la ventana, a la cuál se aplica el efecto.dwTime - Velocidad para reproducir el efecto. A más tiempo, más suave y conmás lentitud es el efecto.dwFlags - Parámetros que definen el tipo de efecto, la orientación y laactivación de la ventana.Se pueden combinar varios parámetros para conseguir efectos personalizados.

Dentro del parámetro dwFlags, se pueden realizar los efectos de animaciónque detallo:

Tipos de efectos AW_SLIDE 

Esta es una animación de deslizamiento. Este parámetro es ignorado si seutiliza la bandera AW_CENTER. De forma predeterminada, y si no se indicaeste parámetro, todas las ventanas utilizan el efecto de persiana, oenrollamiento.

 AW_BLEND

Aplica un efecto de aparición gradual. Recuerde utilizar este parámetro si laventana tiene prioridad sobre las demás. Este efecto sólo funciona con

Page 50: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 50/306

Windows 2000 y Windows XP.

 AW_HIDE 

Oculta la ventana, sin animación. Hay que combinar con otro parámetro para

que la ocultación muestre animación. Por ejemplo con AW_SLIDE  o AW_BLEND.

 AW_CENTER

Este efecto provoca que la ventana aparezca desde el centro de la pantalla oescritorio. Para que funcione, debe ser combinado con el parámetro AW_HIDE para mostrar la ventana, o no utilizar AW_HIDE  para ocultarla.

Orientación al mostrar u ocultar

 AW_HOR_POSITIVE 

Animar la ventana de izquierda a derecha. Este parámetro puede sercombinado con las animaciones de deslizamiento o persiana. Si utiliza

 AW_CENTER o AW_BLEND, no tendrá efecto.

 AW_HOR_NEGATIVE 

Animar la ventana de derecha a izquierda. Este parámetro puede sercombinado con las animaciones de deslizamiento o persiana. Si utiliza

 AW_CENTER o AW_BLEND, no tendrá efecto.

 AW_VER_POSITIVE 

Animar la ventana de arriba hacia abajo. Este parámetro puede sercombinado con las animaciones de deslizamiento o persiana. Si utiliza

 AW_CENTER o AW_BLEND, no tendrá efecto.

AW_VER_NEGATIVE

Animar la ventana de abajo hacia arriba. Este parámetro puede ser

combinado con las animaciones de deslizamiento o persiana. Si utiliza AW_CENTER o AW_BLEND, no tendrá efecto.

Otros parámetros

 AW_ACTIVATE 

Este parámetro traspasa el foco de activación a la ventana antes de aplicar elefecto. Recomiendo utilizarlo, sobre todo cuando las ventanas contiene algún

tema de Windows XP. No utilizar con la bandera AW_HIDE .

Page 51: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 51/306

Utilizando la función en Delphi

¿En qué evento utilizar esta función?

Normalmente, y a nivel personal y por experiencias negativas, siempre lautilizo en el evento FormShow de la ventana a la cuál aplicar el efecto. Unejemplo sería el siguiente:

procedure TFForm1. FormShow( Sender : TObj ect ) ;begi n  Ani mat eWi ndow( Handl e, 400, AW_ACTI VATE or AW_SLI DE orAW_VER_POSI TI VE ) ;end;

(Este efecto va mostrando la ventana de arriba hacia abajo condeslizamiento).

Problemas con los temas de Windows XP y las ventanas deDelphi

Naturalmente, no todo es una maravilla, y entre los problemas que puedensurgir al crear estos efectos, están los siguientes:

- Temas visuales de Windows XP:

Cuando un efecto de animación es mostrado, a veces ciertos controles de laventana, cómo los TEdit, ComboBox, etc, no terminan de actualizarse,quedando con el aspecto antiguo de Windows 98. Para solucionar esteproblema, hay que escribir la función "RedrawWindow" a continuación deAnimateWindow:

procedure TFForm1. FormShow( Sender : TObj ect ) ;begi n  Ani mat eWi ndow( Handl e, 400, AW_ACTI VATE or AW_SLI DE orAW_VER_POSI TI VE ) ;  RedrawWi ndow( Handl e, ni l , 0, RDW_ERASE or RDW_FRAME orRDW_I NVALI DATE or RDW_ALLCHI LDREN ) ;end;

- Ocultando ventanas entre ventanas de Delphi:

Por un problema desconocido de delphi (por lo menos desconozco si Delphi

Page 52: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 52/306

2006 lo hace), ocultar una ventana con animación, teniendo otras ventanas dedelphi (de tu aplicación) detrás, produce un efecto de redibujado fatal, quedesmerece totalmente el efecto realizado. Esto no pasa si las ventanas queaparecen detrás no son de Delphi, o de tu propia aplicación. Por ese motivo,personalmente nunca utilizo este efecto de ocultación de ventanas.

Últimos consejos

Por último, permitidme daros un consejo. Estos efectos también son válidosen Windows 2000, pero los efectos pueden no ser tan fluidos como enWindows XP. Por ello, no estaría mal que estos efectos sean una opción deconfiguración de vuestra aplicación, es decir, permitir al usuario activarlos odesactivarlos.

También recomiendo no abusar de estos efectos, al final terminan siendo unpoco molestos. Realizarlos en las ventanas principales es mejor que en todaslas ventanas.

Espero que el artículo sea de utilidad, y dé un toque de elegancia a vuestrasaplicaciones.

Pr uebas r eali zadas en Delphi 7.

Guardando y cargando opcionesHay ocasiones en que nos interesa que las opciones de nuestro programapermanezcan en el mismo después de terminar su ejecución. Principalmentese suelen utilizar cuatro maneras de guardar las opciones:

1º En un archivo de texto plano.

2º En un archivo binario.

3º En un archivo INI.

4º En el registro del sistema de Windows.

Vamos a suponer que tenemos un formulario de opciones con campos de tipostring, integer, boolean, date, time y real.

Los archivos de opciones se crearán en el mismo directorio en el que seejecuta nuestra aplicación.

GUARDANDO OPCIONES EN TEXTO PLANO

Page 53: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 53/306

Para ello utilizamos un archivo de tipo TextFile para guardar la información:

pr ocedur e TFPr i nci pal . Guar darTexto;

var F: Text Fi l e;begi n  / / Asi gnamos el archi vo de opci ones al punt ero F  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones. t xt ' ) ;

  / / Abr i mos el ar chi vo en modo creaci ón/ escr i t ur a  Rewr i t e( F ) ;

  / / Guardamos l as opci ones  Wr i t eLn( F, I MPRESORA. Text ) ;  Wr i t eLn( F, I nt ToSt r ( COPI AS. Val ue ) ) ;

  i f VI STAPREVI A. Checked t hen  Wr i t eLn( F, ' CON VI STA PREVI A' )  el se  Wr i t eLn( F, ' SI N VI STA PREVI A' ) ;

  Wr i t eLn( F, DateToSt r ( FECHA. Date ) ) ;  Wr i t eLn( F, HORA. Text ) ;  Wr i t eLn( F, For matFl oat ( ' ###0. 00' , MARGEN. Val ue ) ) ;

  Cl oseFi l e( F ) ;end;

CARGANDO OPCIONES DESDE TEXTO PLANO

Antes de abrir el archivo comprobamos si existe:

pr ocedur e TFPr i nci pal . Car garTexto;var F: Text Fi l e;  sLi nea: St r i ng;begi n  / / Si no exi st e el archi vo de opci ones no hacemos nada  i f not Fi l eExi st s( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones. t xt ' ) t hen  Exi t ;

  / / Asi gnamos el archi vo de opci ones al punt ero F  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones. t xt ' ) ;

  / / Abr i mos el archi vo en modo l ect ur a  Reset ( F ) ;

  / / Cargamos l as opci ones  ReadLn( F, s Li nea ) ;  I MPRESORA. Text : = sLi nea;

  ReadLn( F, s Li nea ) ;  COPI AS. Val ue : = St r ToI nt ( sLi nea ) ;

  ReadLn( F, s Li nea ) ;  VI STAPREVI A. Checked : = sLi nea = ' CON VI STA PREVI A' ;

Page 54: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 54/306

  ReadLn( F, s Li nea ) ;  FECHA. Dat e : = St r ToDat e( sLi nea ) ;

  ReadLn( F, s Li nea ) ;  HORA. Text : = sLi nea;

  ReadLn( F, s Li nea ) ;  MARGEN. Val ue : = St r ToFl oat ( sLi nea ) ;

  Cl oseFi l e( F ) ;end;

GUARDANDO OPCIONES EN UN ARCHIVO BINARIO

Lo que hacemos en esta ocación es crear un registro (record) que contengalas opciones de nuestro programa. Después volcamos todo el contenido del

registro en un archivo binario del mismo tipo.

En la interfaz de nuestra unidad definimos:

type  TOpci ones = r ecord  sI mpr esor a: St r i ng[ 100] ;  i Copi as: I nt eger ;  bVi st aPrevi a: Bool ean;  dFecha: TDat e;  t Hora: TTi me;  r Margen: Real ;  end;

Y creamos el procemimiento que lo graba:

pr ocedur e TFPr i nci pal . Guar dar Bi nar i o;var  / / Cr eamos un r egi st r o y un f i chero para el mi smo  Opci ones: TOpci ones;  F: f i l e of TOpci ones;begi n  / / Met emos l as opci ones del f or mul ar i o en el r egi st r o  Opci ones. sI mpresor a : = I MPRESORA. Text ;  Opci ones. i Copi as : = COPI AS. Val ue;

  Opci ones. bVi st aPr evi a : = VI STAPREVI A. Checked;  Opci ones. dFecha : = FECHA. Dat e;  Opci ones. t Hora : = St r ToTi me( HORA. Text ) ;  Opci ones. r Mar gen : = MARGEN. Val ue;

  / / Asi gnamos el archi vo de opci ones al punt ero F  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones. dat ' ) ;

  / / Abr i mos el ar chi vo en modo creaci ón/ escr i t ur a  Rewr i t e( F ) ;

  / / Guardamos de gol pe t odas l as opci ones

  Wr i t e( F, Opci ones ) ;  / / Cer r amos el f i cher o

Page 55: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 55/306

  Cl oseFi l e( F ) ;end;

CARGANDO OPCIONES DESDE UN ARCHIVO BINARIO

Utilizamos el registro creado anteriormente:

pr ocedur e TFPr i nci pal . Car gar Bi nar i o;var  / / Cr eamos un r egi st r o y un f i chero para el mi smo  Opci ones: TOpci ones;  F: f i l e of TOpci ones;begi n  / / Asi gnamos el archi vo de opci ones al punt ero F  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones. dat ' ) ;

  / / Abr i mos el ar chi vo en modo creaci ón/ escr i t ur a  Reset ( F ) ;

  / / Guardamos de gol pe t odas l as opci ones  Read( F, Opci ones ) ;

  / / Cer r amos el f i cher o  Cl oseFi l e( F ) ;

  / / Copi amos l as opci ones del r egi st r o en el f or mul ar i o de opci ones  I MPRESORA. Text : = Opci ones. sI mpresor a;  COPI AS. Val ue : = Opci ones. i Copi as;  VI STAPREVI A. Checked : = Opci ones. bVi st aPr evi a;  FECHA. Dat e : = Opci ones. dFecha;  HORA. Text : = Ti meToSt r ( Opci ones. t Hora ) ;  MARGEN. Val ue : = Opci ones. r Mar gen;end;

GUARDANDO OPCIONES EN UN ARCHIVO INI

Los dos casos anteriores tienen un defecto: si ampliamos el número deopciones e intentamos cargar las opciones con el formato antiguo se puedeprovocar un error de E/S debido a que los formatos de texto o binario hancambiado.

Lo más flexible en este claso es utilizar un archivo INI, el cual permite agruparopciones y asignar un nombre a cada una:

pr ocedur e TFPr i nci pal . Guar dar I NI ;var I NI : TI ni Fi l e;begi n  / / Cr eamos el ar chi vo I NI  I NI : = TI NI Fi l e. Cr eat e( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones . i ni ' ) ;

  / / Guardamos l as opci ones  I NI . Wr i t eSt r i ng( ' OPCI ONES' , ' I MPRESORA' , I MPRESORA. Text ) ;  I NI . Wr i t eI nt eger ( ' OPCI ONES' , ' COPI AS' , COPI AS. Val ue ) ;  I NI . Wr i t eBool ( ' OPCI ONES' , ' VI STAPREVI A' , VI STAPREVI A. Checked ) ;  I NI . Wr i t eDat e( ' OPCI ONES' , ' FECHA' , FECHA. Dat e ) ;  I NI . Wr i t eTi me( ' OPCI ONES' , ' HORA' , St r ToTi me( HORA. Text ) ) ;

Page 56: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 56/306

  I NI . Wr i t eFl oat ( ' OPCI ONES' , ' MARGEN' , MARGEN. Val ue ) ;

  / / Al l i ber ar el ar chi vo I NI se ci er r a el ar chi vo opci ones. i ni  I NI . Free;end;

CARGANDO OPCIONES DE UN ARCHIVO INI

Aunque aquí comprobamos si existe el archivo, no es necesario, ya quecargaría las opciones por defecto:

pr ocedur e TFPr i nci pal . Car gar I NI ;var I NI : TI ni Fi l e;begi n  / / Si no exi st e el archi vo no hacemos nada  i f not Fi l eExi st s( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones. i ni ' ) t hen  Exi t ;

  / / Cr eamos el ar chi vo I NI  I NI : = TI NI Fi l e. Cr eat e( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) +' opci ones . i ni ' ) ;

  / / Guardamos l as opci ones  I MPRESORA. Text : = I NI . ReadSt r i ng( ' OPCI ONES' , ' I MPRESORA' , ' ' ) ;  COPI AS. Val ue : = I NI . ReadI nt eger ( ' OPCI ONES' , ' COPI AS' , 0 ) ;  VI STAPREVI A. Checked : = I NI . ReadBool ( ' OPCI ONES' , ' VI STAPREVI A' ,Fal se ) ;  FECHA. Date : = I NI . ReadDat e( ' OPCI ONES' , ' FECHA' , Dat e ) ;  HORA. Text : = Ti meToSt r ( I NI . ReadTi me( ' OPCI ONES' , ' HORA' , Ti me ) ) ;  MARGEN. Val ue : = I NI . ReadFl oat ( ' OPCI ONES' , ' MARGEN' , 0. 00 ) ;

  / / Al l i ber ar el ar chi vo I NI se ci er r a el ar chi vo opci ones. i ni  I NI . Free;end;

GUARDANDO OPCIONES EN EL REGISTRO DEL SISTEMA

Si queremos que nadie nos toque las opciones del programa podemosguardarlo todo en el registro de Windows:

pr ocedur e TFPr i nci pal . Guar darRegi st r oSi st ema;var Reg: TRegi st r y;

begi n  / / Cr eamos un obj et o par a manej ar el r egi st r o  Reg : = TRegi st r y. Cr eat e;

  / / Guardamos l as opci ones  t r y  Reg. RootKey : = HKEY_LOCAL_MACHI NE;  i f Reg. OpenKey( ' \ Sof t ware\ Mi Pr ogr ama' , True ) t hen  begi n  Reg. Wr i t eSt r i ng( ' I MPRESORA' , I MPRESORA. Text ) ;  Reg. Wr i t eI nt eger ( ' COPI AS' , COPI AS. Val ue ) ;  Reg. Wr i t eBool ( ' VI STAPREVI A' , VI STAPREVI A. Checked ) ;

  Reg. Wr i t eDat e( ' FECHA' , FECHA. Date ) ;  Reg. Wr i t eTi me( ' HORA' , St r ToTi me( HORA. Text ) ) ;  Reg. Wr i t eFl oat ( ' MARGEN' , MARGEN. Val ue ) ;  Reg. Cl oseKey;

Page 57: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 57/306

  end;  f i n a l l y  Reg. Fr ee;  end;end;

Para probar si se ha guardado la información pulsa el botón INICIO y opciónEJECUTAR: REGEDIT. Las opciones se habrán guardado en la carpeta:

 \HKEY_LOCAL_MACHINE\SOFTWARE\MiPrograma

CARGANDO OPCIONES DESDE EL REGISTRO DEL SISTEMA

Antes de cargar las opciones comprueba si existe la clave:

pr ocedur e TFPr i nci pal . Car garRegi st r oSi st ema;

var Reg: TRegi st r y;begi n  / / Cr eamos un obj et o par a manej ar el r egi st r o  Reg : = TRegi st r y. Cr eat e;

  / / Guardamos l as opci ones  t r y  Reg. RootKey : = HKEY_LOCAL_MACHI NE;  i f Reg. OpenKey( ' \ Sof t ware\ Mi Pr ogr ama' , True ) t hen  begi n  I MPRESORA. Text : = Reg. ReadStr i ng( ' I MPRESORA' ) ;  COPI AS. Val ue : = Reg. ReadI nt eger ( ' COPI AS' ) ;  VI STAPREVI A. Checked : = Reg. ReadBool ( ' VI STAPREVI A' ) ;

  FECHA. Dat e : = Reg. ReadDat e( ' FECHA' ) ;  HORA. Text : = Ti meToSt r ( Reg. ReadTi me( ' HORA' ) ) ;  MARGEN. Val ue : = Reg. ReadFl oat ( ' MARGEN' ) ;  Reg. Cl oseKey;  end;  f i n a l l y  Reg. Fr ee;  end;end;

Pruebas r eali zadas en Delphi 7 

Minimizar en la bandeja del sistemaUna de las características mas utilizadas en los programas P2P es la deminimizar nuestra aplicación en la bandeja del sistema (al lado del reloj deWindows en la barra de tareas).

Voy a mostraros como modificar el formulario principal de vuestra aplicaciónpara que se minimize en la bandeja del sistema y una vez minimizado cuandose pulse sobre el icono se restaure. También vamos a añadir la posibilidad depulsar dicho icono con el botón derecho del ratón y que muestre un menucontextual (popup) con la opción Mostrar.

Lo primero de todo es añadir un menu contextual a nuestro formularioprincipal (PopupMenu) con el nombre MenuBandeja. Añadimos una sola

Page 58: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 58/306

opción llamada Mostrar. A continuación añadimos en la sección uses delformulario principal la unidad ShellAPI:

uses

  Wi ndows, Messages, . . . . , Shel l API ;

Después en la sección private insertamos la variable:

  I conDat a: TNot i f yI conDat a;

En la misma sección private añadimos los procedimientos:

  pr ocedur e WMSysCommand( var Msg: TWMSysCommand ) ; messageWM_SYSCOMMAND;  procedure Rest aur ar ( var Msg: TMessage ) ; message WM_USER+1;

Cuya implementación sería la siguiente:

pr ocedur e TFPr i nci pal . WMSysCommand( var Msg: TWMSysCommand ) ;begi n  i f Msg. CmdType = SC_MI NI MI ZE t hen  Mi ni mi zar  el se  Def aul t Handl er ( Msg ) ;end;

pr ocedur e TFPr i nci pal . Rest aur ar ( var Msg: TMessage ) ;var p: TPoi nt ;begi n

  / / ¿Ha pul sado el bot ón i zqui er do del r at ón?  i f Msg. l Par am = WM_LBUTTONDOWN t hen  Mostr ar Cl i ck( Sel f ) ;

  / / ¿Ha pul sado en l a bandej a del si st ema con el botón derecho delratón?  i f Msg. l Par am = WM_RBUTTONDOWN t hen  begi n  Set For egr oundWi ndow( Handl e ) ;  GetCursorPos( p ) ;  MenuBandej a. Popup( p. x, p. y ) ;  Post Message( Handl e, WM_NULL, 0, 0 ) ;  end;

end;

El procedimiento WMSysCommand es el encargado de interceptar losmensajes del sistema que manda Windows a nuestra aplicación. En el caso deque el mensaje enviado sea SC_MINIMIZE minimizamos la ventana en labandeja del sistema. Si es otro mensaje dejamos que Windows lo maneje(DefaultHandler).

El procedimiento Restaurar comprueba si ha pulsado el botón izquierdo delratón sobre el icono de la bandeja del sistema para volver a mostrar nuestraventana. Si pulsa el botón derecho llamará a nuestro menu contextualMenuBandeja.

Page 59: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 59/306

Ahora creamos el procedimiento encargado de minimizar la ventana:

pr ocedur e TFPr i nci pal . Mi ni mi zar ;begi n  wi t h I conDat a do

  begi n  cbSi ze : = si zeof ( I conDat a ) ;  Wnd : = Handl e;  uI D : = 100;  uFl ags : = NI F_MESSAGE + NI F_ I CON + NI F_TI P;  uCal l backMessage : = WM_USER + 1;

  / / Usamos de i cono el mi smo de l a apl i caci ón  hI con : = Appl i cat i on. I con. Handl e;

  / / Como Hi nt del i cono, el nombr e de l a apl i caci ón  St r PCopy( szTi p, Appl i cat i on. Ti t l e ) ;  end;

  / / Ponemos el i cono al l ado del r el oj  Shel l _Not i f yI con( NI M_ADD, @I conDat a ) ;

  / / Ocul t amos el f or mul ar i o  Hi de;end;

Y por último el evento al pulsar la opción Mostrar en el menú contextual:

pr ocedur e TFPr i nci pal . Most r ar Cl i ck( Sender : TObj ect ) ;begi n  / / Vol vemos a most r ar de nuevo el f ormul ar i o  FPr i nci pal . Show;  ShowWi ndow( Appl i cat i on. Handl e, SW_SHOW ) ;

  / / El i mi namos el i cono de l a bandej a del si st ema  Shel l _Not i f yI con( NI M_DELETE, @I conData ) ;  I conDat a. Wnd : = 0;end;

Aunque pueda parecer algo engorroso creo que es mas limpio que tener queinstalar componentes para que realicen esto. Al fin y al cabo sólo hay quehacerlo sólo en el formulario principal.

Pruebas realizadas en Delphi 7.

Cómo ocultar una aplicaciónVamos a ver como hacer que una aplicación cualquiera hecha en Delphi quedeoculta de la barra de tareas de Windows y del escritorio. Sólo podrá verseejecutando el administrador de tareas en la pestaña Procesos.

Para ello vamos a añadir en el evento OnCreate del formulario principal denuestra aplicación lo siguiente:

pr ocedur e TFPr i nci pal . For mCr eat e( Sender : TObj ect ) ;

Page 60: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 60/306

begi n  / / Hacemos que el f or mul ari o sea i nvi si bl e poni endol o en l a  / / esqui na super i or i zqui er da, t amaño cer o y apl i caci ón i nvi si bl e  Border St yl e : = bsNone;  Lef t : = 0;  Top : = 0;

  Wi dt h : = 0;  Hei ght : = 0;  Vi si bl e : = Fal se;  Appl i cat i on. Ti t l e : = ' ' ;  Appl i cat i on. ShowMai nFor m : = Fal se;

  / / Lo ocul t amos de l a barr a de t areas  ShowWi ndow( Appl i cat i on. Handl e, SW_HI DE ) ;  Set Wi ndowLong( Appl i cat i on. Handl e, GWL_EXSTYLE,  Get Wi ndowLong( Appl i cat i on. Handl e, GWL_EXSTYLE) or  WS_EX_TOOLWI NDOW and not WS_EX_APPWI NDOW) ;end;

Esto nos puede ser util para crear programas residentes ocultos al usuariopara administración de copias de seguridad, reparación automática de basesde datos y envío de mailing automátizado.

Capturar el teclado en WindowsHay ocasiones en las cuales nos interesa saber si una tecla de Windows ha sidopulsada aunque estemos en otra aplicación que no sea la nuestra.

Por ejemplo en el artículo anterior mostré como capturar la pantalla. Seríainteresante que si pulsamos F8 estando en cualquier aplicación nos capture la

pantalla (incluso si nuestra aplicación esta minimizada).Para ello vamos a utilizar la función de la API de Windows GetAsyncKeyStatela cual acepta como parámetro la tecla pulsada (VK_RETURN, VK_ESCAPE,VK_F8, etc) y nos devuelve -32767 si la tecla ha sido pulsada.

Como el teclado hay que leerlo constantemente y no conviene dejar un buclecerrado consumiendo mucho procesador, lo que vamos a hacer es meter anuestro formulario un temporizador TTimer activado cada 10 milisegundos(Inverval) y con el evento OnTimer definido de la siguiente manera:

pr ocedur e TFormul ar i o. Tempor i zadorTi mer( Sender : TObj ect ) ;begi n  / / ¿Ha pul sado una t ecl a?  i f GetAsyncKeySt at e( VK_F8 ) = - 32767 t hen  Capt ur ar Pant al l a;end;

Para capturar números o letras se hace con la función ord:

i f Get AsyncKeySt at e( Or d( ' A' ) ) t hen . . .i f Get AsyncKeySt at e( Or d( ' 5' ) ) t hen . . .

Si es una letra hay que pasarla a mayúsculas.

Page 61: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 61/306

Sólo con esto podemos interceptar cualquier tecla del buffer de Windows. Porejemplo se podría hacer una aplicación que al pulsar F10 minimize todas lasventanas de Windows.

Pruebas realizadas en Delphi 7.

Capturar la pantalla de WindowsVamos a crear un procedimiento que captura un trozo de la pantalla deWindows y la guarda en un bitmap:

pr ocedur e Capt ur ar Pant al l a( x, y, i Ancho, i Al t o: I nt eger ; I magen: TBi t map ) ;var  DC: HDC;  l pPal : PLOGPALETTE;begi n  i f ( i Ancho = 0 ) OR ( i Al t o = 0 ) t hen  Exi t ;

  I magen. Wi dth : = i Ancho;  I magen. Hei ght : = i Al t o;  DC : = Get Dc( 0 ) ;

  i f ( DC = 0 ) t hen  Exi t ;

  i f ( Get Devi ceCaps( dc, RASTERCAPS) and RC_PALETTE = RC_PALETTE )t hen  begi n  Get Mem( l pPal , Si zeOf ( TLOGPALETTE ) + ( 255 * Si zeOf (

 TPALETTEENTRY ) ) ) ;  Fi l l Char( l pPal ,̂ Si zeOf ( TLOGPALETTE ) + ( 255 * Si zeOf (

 TPALETTEENTRY ) ) , #0 ) ;  l pPal .̂ pal Vers i on : = $300;  l pPal .̂ pal NumEnt r i es : = GetSyst emPal et t eEnt r i es( DC, 0, 256,l pPal .̂ pal Pal Ent r y ) ;

  i f ( l pPal .̂ Pal NumEnt r i es <> 0) t hen  I magen. Pal et t e : = Cr eat ePal et t e( l pPal ^ ) ;

  Fr eeMem( l pPal , Si zeOf ( TLOGPALETTE ) + ( 255 * Si zeOf ( TPALETTEENTRY ) ) ) ;  end;

  Bi t Bl t ( I magen. Canvas. Handl e, 0, 0, i Ancho, i Al t o, DC, x, y, SRCCOPY) ;  Rel easeDc( 0, DC ) ;end;

Resumiendo a grandes rasgos lo que hace el procedimiento es crear undispositivo de contexto donde según el número de bits por pixel reserva una

Page 62: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 62/306

zona de memoria para capturar el escritorio. Después mediante la funciónBitBlt vuelca la imagen capturada al Canvas de la imagen que le pasamos.

Para capturar toda la pantalla de Windows utilizando este procedimientohacemos lo siguiente:

var I magen: TBi t map;begi n  I magen : = TBi t map. Cr eate;  Capt ur arPant al l a( 0, 0, Scr een. Wi dt h, Screen. Hei ght , I magen ) ;  I magen. SaveToFi l e( Ext r act Fi l ePat h( Appl i cati on. ExeName ) +' capt ur a. bmp' ) ;  I magen. Fr ee;end;

La pantalla capturada la guarda en el archivo captura.bmp al lado de nuestroejecutable. Sólo faltaría el poder capturar una tecla de Windows desde

cualquier aplicación para activar nuestro capturador de pantalla (para que nose capture a si mismo).

Pruebas realizadas en Delphi 7.

Obtener los favoritos de Internet ExplorerEl siguiente procedimiento recursivo obtiene la lista de los enlaces favoritosde Internet Explorer. Puede ser de mucha utilidad en el caso de formatear elequipo y salvar la lista de favoritos a un archivo de texto o a una base dedatos.

Lo primero es añadir en uses:

uses  Wi ndows, Di al ogs, . . . , Shl Obj ;

Y este sería el procedimiento:

f uncti on Obt ener Favor i t osI E( sRut aFavor i t os: St r i ng ) : TSt r i ngs;var  Busqueda: TSear chr ec;  Li st aFavor i t os: TSt r i ngs;  sRut a, sDi r ector i o, sAr chi vo: St r i ng;  Buf f er : arr ay[ 0. . 2047] of Char ;

  i Encont r ado: I nt eger ;begi n  Li st aFavor i t os : = TSt r i ngLi st . Cr eat e;

Page 63: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 63/306

  t r y  sRut a : = sRut aFavor i t os + ' \ *. ur l ' ;  sDi r ectori o : = Ext r actFi l epat h( sRut a ) ;  i Encont r ado : = Fi ndFi r st ( sRut a, f aAnyFi l e, Busqueda ) ;

  whi l e i Encont r ado = 0 do

  begi n  Set St r i ng( sAr chi vo, Buf f er ,  Get Pr i vat ePr of i l eSt r i ng( ' I nt er net Shor t cut ' ,  PChar( ' URL' ) , ni l , Buf f er , Si zeOf ( Buf f er ) ,  PChar( sDi r ect or i o + Busqueda. Name ) ) ) ;  Li st aFavor i t os. Add( sAr chi vo ) ;  i Encont r ado : = Fi ndNext ( Busqueda ) ;  end;

  i Encont r ado : = Fi ndFi r st ( sDi r ectori o + ' \ *. * ' , f aAnyFi l e,Busqueda ) ;

  whi l e i Encont r ado = 0 do  begi n  i f ( ( Busqueda. At t r and f aDi r ect or y ) > 0 ) and (Busqueda. Name[ 1] <> ' . ' ) t hen  Li st aFavor i t os. AddSt r i ngs( Obt ener Favor i t osI E( sDi r ector i o +' \ ' + Busqueda. name ) ) ;

  i Encont r ado : = Fi ndNext ( Busqueda ) ;  end;

  Fi ndCl ose( Busqueda ) ;  f i n a l l y  Resul t : = Li st aFavor i t os;  end;end;

Para utilizar el procedimiento supongamos que en el formulario tenemos uncomponente ListBox (FAVORITOS) y un botón (BFavoritos) que al pulsarlo nostrae todos los favoritos a dicha lista:

pr ocedur e TFPr i nci pal . BFavor i t osCl i ck( Sender : TObj ect ) ;var  pi dl : PI t emI DLi st ;  sRut aFavor i t os: ar r ay[ 0. . MAX_PATH] of Char ;begi n  SHGetSpeci al Fol der Locat i on( Handl e, CSI DL_FAVORI TES, pi dl ) ;  SHGetPathFromI DLi st ( pi dl , sRut aFavor i t os ) ;

  FAVORI TOS. I t ems : = Obt enerFavor i t osI E( St r Pas( sRut aFavor i t os ) ) ;end;

Pruebas realizadas en Delphi 7.

Crear un acceso directoAquí tenemos un pequeño pero interesante procedimiento para crear accesosdirectos en Windows. Antes de implementarlo hay que añadir en uses unaserie de unidades externas:

Page 64: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 64/306

uses  Wi ndows, Di al ogs, . . . , Shl Obj , Act i veX, St dCt r l s, Regi st r y, ComObj ;

Este sería el procedimiento:

pr ocedur e Cr ear AccesoDi r ecto( sExe, sAr gument os, sDi r Trabaj o,sNombr eLnk, sDi r Dest i no: st r i ng ) ;var  Obj et o: I Unknown;  UnSl i nk: I Shel l Li nk;  Fi cheroP: I Pers i s tFi l e;  WFi chero: Wi deSt r i ng;begi n  Obj eto : = Cr eat eComObj ect ( CLSI D_Shel l Li nk ) ;  UnSl i nk : = Obj et o as I Shel l Li nk;  Fi cher oP : = Obj et o as I Per si st Fi l e;

  wi t h UnSl i nk do

  begi n  Set Ar gument s( PChar ( sArgument os ) ) ;  SetPath( PChar ( sExe ) ) ;  SetWorki ngDi r ector y( PChar ( sDi r Tr abaj o ) ) ;  end;

  WFi cher o : = sDi r Dest i no + ' \ ' + sNombreLnk;  Fi cheroP. Save( PWChar( WFi chero ) , Fal se ) ;end;

Y estos son sus parámetros:

sExe - > Rut a que apunt a al ej ecut abl e o archi vo a cr ear elacceso di r ect osAr gument os - > Par ámet r os que l e mandamos al EXEsDi r Tr abaj o - > Rut a al di r ector i o de t r abaj o del ej ecut abl esNombreLnk - > Nombre del acceso di r ect osDi r Dest i no - > Rut a dest i no donde se crear á el acceso di r ect o

Aquí os muestro un ejemplo de cómo crear un acceso directo de lacalculadora de Windows al escritorio:

pr ocedur e Cr ear AccesoCal cul adora;var  sEscr i t or i o: St r i ng;

  Regi st r o: TRegi st r y;begi n  Regi st r o : = TRegi st r y. Cr eat e;

  / / Leemos l a r ut a del escri t or i o  t r y  Regi st r o. Root Key : = HKEY_CURRENT_USER;

  i f Regi st r o. OpenKey(' \ Sof t war e\ Mi crosof t \ Wi ndows\ Cur r ent Ver si on\ expl or er \ Shel l Fol der s' ,

 Tr ue ) t hen  sEscri t or i o : = Regi st r o. ReadSt r i ng( ' Deskt op' ) ;  f i n a l l y

  Regi st r o. Cl oseKey;  Regi st r o. Free;  i nher i t ed;

Page 65: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 65/306

  end;

  Cr ear AccesoDi r ect o( ' C: \ Wi ndows\ System32\ cal c. exe' , ' ' ,  ' C: \ Wi ndows\ Syst em32\ ' , ' Cal cul ador a. l nk' ,sEscr i tor i o ) ;end;

Pruebas realizadas en Delphi 7.

Averiguar la versión de WindowsLa siguiente función nos devuelve la versión de Windows donde se estáejecutando nuestro programa:

f unct i on Obt ener Versi on: St r i ng;var

  osVer I nf o: TOSVersi onI nf o;  Versi onMayor , Versi onMenor : I nt eger;begi n  Resul t : = ' Desconoci da' ;  osVer I nf o. dwOSVersi onI nf oSi ze : = Si zeOf ( TOSVersi onI nf o ) ;

  i f Get Ver si onEx( osVer I nf o ) t hen  begi n  Vers i onMenor : = osVer I nf o. dwMi nor Versi on;  Vers i onMayor : = osVer I nf o. dwMaj orVersi on;

  case osVerI nf o. dwPl at f ormI d of   VER_PLATFORM_WI N32_NT:  begi n  i f Ver si onMayor <= 4 t hen  Resul t : = ' Wi ndows NT'  el se  i f ( Ver si onMayor = 5 ) and ( Versi onMenor = 0 ) t hen  Resul t : = ' Wi ndows 2000'  el se  i f ( Versi onMayor = 5 ) and ( Ver si onMenor = 1 ) t hen  Resul t : = ' Wi ndows XP'  el se  i f ( Ver si onMayor = 6 ) t hen  Resul t : = ' Wi ndows Vi st a' ;  end;

  VER_PLATFORM_WI N32_ WI NDOWS:  begi n  i f ( Versi onMayor = 4 ) and ( Versi onMenor = 0 ) t hen  Resul t : = ' Wi ndows 95'  el se  i f ( Ver si onMayor = 4 ) and ( Versi onMenor = 10 ) t hen  begi n  i f osVer I nf o. szCSDVer si on[ 1] = ' A' t hen  Resul t : = ' Wi ndows 98 Second Edi t i on'  el se  Resul t : = ' Wi ndows 98' ;  end  el se

  i f ( Versi onMayor = 4 ) and ( Ver si onMenor = 90 ) t hen  Resul t : = ' Wi ndows Mi l l eni um'

Page 66: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 66/306

  el se  Resul t : = ' Desconoci da' ;  end;  end;  end;end;

Primero averigua de que plataforma se trata. Si es Win32 los sistemasoperativos pueden ser: Windows 95, Windows 98, Windows 98 Second Editiony Windows Millenium. Por otro lado, si se trata de la plataforma NT entonceslas versiones son: Windows NT, Windows 2000, Windows XP y Windows Vista.

Esta función puede ser de utilidad para saber que librerias DLL tieneinstaladas, que comandos del sistema podemos ejecutar o para saber sitenemos que habilitar o deshabilitar ciertas funciones de nuestra aplicación.

Pruebas realizadas en Delphi 7.

Recorrer un árbol de directoriosEl procedimiento que voy a mostrar a continuación recorre el contenido dedirectorios, subdirectorios y archivos volcando la información en un campomemo (TMemo).

Modificando su comportamiento puede ser utilizado para realizar copias deseguridad, calcular el tamaño de un directorio o borrar el contenido de losmismos.

El procedimiento RecorrerDirectorio toma como primer parámetro la rutaque desea recorrer y como segundo parámetro si deseamos que busquetambién en subdirectorios:

pr ocedur e TFBuscar . Recor r er Di r ect or i o( sRut a: St r i ng;bI ncl ui r Subdi r ector i os: Bool ean ) ;var  Di r ect ori o: TSearchRec;  i Resul t ado: I nt eger ;begi n  / / Si l a r ut a no t ermi na en cont r abarr a se l a ponemos  i f sRut a[ Lengt h( sRut a) ] <> ' \ ' t hen  sRut a : = sRut a + ' \ ' ;

  / / ¿No exi st e el di r ect or i o que vamos a r ecor r er ?  i f not Di r ectoryExi st s( sRut a ) t hen  begi n  Appl i cat i on. MessageBox( PChar ( ' No exi st e el di r ect or i o: ' + #13 +#13 + sRuta ) , ' Er r or ' , MB_I CONSTOP ) ;  Exi t ;  end;

  i Resul t ado : = Fi ndFi r st( sRut a + ' *. * ' , FaAnyf i l e, Di r ector i o ) ;  whi l e i Resul t ado = 0 do  begi n

Page 67: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 67/306

  / / ¿Es un di r ect or i o y hay que ent r ar en él ?  i f ( Di r ectori o. At t r and f aDi r ect or y = f aDi r ectory ) andbI ncl ui r Subdi r ector i os t hen  begi n  i f ( Di r ect ori o. Name <> ' . ' ) and ( Di r ect or i o. Name <> ' . . ' )t hen

  Recor r er Di r ect ori o( sRut a + Di r ect or i o. Name, True ) ;  end  el se  / / ¿No es el nombr e de una uni dad ni un di r ect or i o?  i f ( Di r ect or i o. At t r and f aVol umeI d <> f aVol umeI D ) t hen  Ar chi vos. Li nes. Add( sRut a + Di r ect or i o. Name ) ;

  i Resul t ado : = Fi ndNext ( Di r ect or i o ) ;  end;

  SysUt i l s. Fi ndCl ose( Di r ectori o ) ;end;

Antes de comenzar a buscar directorios se asegura de que la ruta que lepasemos termine en contrabarra y en el caso de que no sea así se la pone alfinal.

Para recorrer un directorio utiliza la estructura de datos TSearchRec la cualse utiliza para depositar en ella la información del contenido de un directoriomediante las funciones FindFirst y FindNext.

TSearchRec no contiene la información de todo el directorio sino que es unpuntero al directorio o archivo actual. Sólo mirando los atributos mediante lapropiedad Attr podemos saber si lo que estamos leyendo es un directorio,

archivo o unidad.

También se cuida de saltarse los directorios '.' y '..' ya que sino elprocedimiento recursivo RecorrerDirectorio se volvería loco hasta reventar lapila.

Realizar modificaciones para cambiar su comportamiento puede ser peligrososi no lleváis cuidado ya que la recursividad puede de dejar sin memoria laaplicación. Al realizar tareas como borrar subdirectorios mucho cuidado nodarle la ruta C:\. Mejor hacer ensayos volcando el contenido en un Memohasta tener el resultado deseado.

Pruebas realizadas en Delphi 7.

Ejecutar un programa al arrancar WindowsPara ejecutar automáticamente nuestra aplicación al arrancar Windows vamosa utilizar la siguiente clave del registro del sistema:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run\

Todos los programas que se introduzcan dentro de esa clave (antivirus,monitores del sistema, etc.) arrancarán al iniciar Windows.

Page 68: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 68/306

Para ello vamos a utilizar el objeto TRegistry. Para ello hay que añadir suunidad correspondiente en uses:

uses Wi ndows, Messages, . . . , Regi st r y;

Ahora vamos con el procedimiento encargado de poner nuestro programaal inicio de Windows:

pr ocedur e TFPr i nci pal . PonerPr ogr amaI ni ci o;var Regi st r o: TRegi st r y;begi n  Regi st r o : = TRegi st r y. Cr eat e;  Regi st r o. Root Key : = HKEY_LOCAL_MACHI NE;

  i f Regi st r o. OpenKey(' Sof t war e\ Mi cr osof t \ Wi ndows\ Cur r ent Ver si on\ Run' , FALSE ) t hen

  begi n  Regi st r o. Wr i t eSt r i ng( Extr act Fi l eName( Appl i cat i on. ExeName ) ,Appl i cat i on. ExeName ) ;  Regi st r o. Cl oseKey;  end;

  Regi st r o. Free;end;

El método WriteString toma como primer parámetro la el nombre del valor enel registro y como segúndo parámetro la ruta donde se encuentra el programaa ejecutar. En nuestro caso como nombre del valor le he dado el nombre de

nuestro ejecutable y como segundo la ruta desde donde estamos ejecutandoel programa en este mismo instante.

Si en un futuro deseamos quitar el programa entonces sólo hay que eliminar laclave:

pr ocedur e TFPr i nci pal . Qui t ar Pr ogr amaI ni ci o;var Regi st r o: TRegi st r y;begi n  Regi st r o : = TRegi st r y. Cr eat e;  Regi st r o. Root Key : = HKEY_LOCAL_MACHI NE;

  i f Regi st r o. OpenKey(' Sof t war e\ Mi cr osof t \ Wi ndows\ Cur r ent Ver si on\ Run' , FALSE ) t hen  begi n  / / ¿Exi st e el val or que vamos a bor r ar ?  i f Regi st r o. Val ueExi st s( Extr act Fi l eName( Appl i cat i on. ExeName ) )t hen  Regi st r o. Del eteVal ue( Ext r act Fi l eName( Appl i cat i on. ExeName ) ) ;

  Regi st r o. Cl oseKey;  end;

  Regi st r o. Free;end;

Hay ciertos antivirus como el NOD32 que saltan nada más compilar nuestro

Page 69: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 69/306

programa por el simple hecho de tocar la clave Run. Habrá que decirle anuestro antivirus que nuestro programa no es maligno.

Pruebas realizadas en Delphi 7.

Listar los programas instalados en WindowsWindows almacena la lista de programas instalados (Agregar/Quitarprogramas) en la clave de registro:

HKEY_LOCAL_MACHI NE\ SOFTWARE\ Mi cr osof t \ Wi ndows\ Curr ent Versi on\ Uni nst al l\

En esa clave hay tantas subclaves como programas instalados. Pero lo que nosinteresa a nosotros no es el nombre de la clave del programa instalado sino el

nombre del programa que muestra Windows en Agregar/Quitar programas.Para ello entramos en cada clave y leemos el valor DisplayName.

Lo primero añadimos la unidad:

uses  Wi ndows, Messages, . . . , Regi st r y;

Y aquí tenemos un procedimiento al cual le pasamos un ListBox y nos lorellena con la lista de programas instalados en Windows:

pr ocedur e Li st ar Apl i caci ones( Li st a: TLi st Box ) ;

const  I NSTALADOS = ' \ SOFTWARE\ Mi cr osof t \ Wi ndows\ Cur r ent Versi on\ Uni nst al l ' ;var  Regi st r o: TRegi st r y;  Li sta1 : TSt r i ngLi st ;  Li sta2 : TSt r i ngLi st ;  j , n : i nteger ;begi n  Regi st r o : = TRegi st r y. Cr eat e;  Li st a1 : = TSt r i ngLi st . Cr eat e;  Li st a2 : = TSt r i ngLi st . Cr eat e;

  / / Guar damos t odas l as cl aves en l a l i st a 1

  wi t h Regi st r o do  begi n  Root Key : = HKEY_LOCAL_ MACHI NE;  OpenKey( I NSTALADOS, Fal se ) ;  Get KeyNames( Li st a1 ) ;  end;

  / / Recor r emos l a l i st a 1 y l eemos el nombr e del pr ogr ama i nst al ado  f or j : = 0 t o Li st a1. Count - 1 do  begi n  Regi st r o. OpenKey( I NSTALADOS + ' \ ' + Li st a1. St r i ngs[ j ] , Fal se ) ;  Regi st r o. GetVal ueNames( Li st a2 ) ;

  / / Most r amos el progr ama i nst al ado sól o si t i ene Di spl ayName  n : = Li st a2. I ndexOf ( ' Di spl ayName' ) ;  i f ( n <> - 1 ) and ( Li st a2. I ndexOf ( ' Uni nst al l St r i ng' ) <> - 1 )

Page 70: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 70/306

t hen  Li st a. I t ems. Add( ( Regi st r o. ReadSt r i ng( Li st a2. St r i ngs[ n] ) ) ) ;  end;

  Li st a. Sor t ed : = Tr ue; / / Or denamos l a l i st a al f abét i cament e  Li st a1. Fr ee;

  Li st a2. Fr ee;  Regi st r o. Cl oseKey;  Regi st r o. Dest r oy;end;

Con esto se podría hacer un programa que eliminara de Agregar/Quitarprogramas aquellas claves de programas mal desinstalados.

Pruebas realizadas en Delphi 7.

Ejecutar un programa y esperar a quetermineUno de los problemas habituales con los que se enfrenta un programador esque su cliente le pida algo que o bien no sabe como programarlo o no disponedel componente o librería necesaria para llevar tu tarea a cabo.

Un ejemplo puede ser realizar una copia de seguridad en formatos ZIP, RAR,7Z, etc., convertir de un formato de video o sonido a otro e incluso llamar a

comandos del sistema para realizar procesos criticos en un servidor. Entoncessólo se nos ocurre llamar a un programa externo que realice la tarea pornosostros (y que soporte parámetros).

Sé lo que estáis pensando (la función WinExec), pero en este caso no me valeya que el programa tiene que esperar a que termine de ejecutarse antes depasar al siguiente proceso.

Aquí os muestro un procedimiento que ejecuta un programa y se quedaesperando a que termine:

f uncti on Ej ecut ar YEsper ar ( sPr ogr ama: St r i ng; Vi si bi l i dad: I nt eger ) :I nt eger ;var  sApl i caci on: ar r ay[ 0. . 512] of char ;  Di r ectori oActual : ar r ay[ 0. . 255] of char ;  Di r ectori oTr abaj o: St r i ng;  I nf or maci onI ni ci al : TSt ar t upI nf o;  I nf ormaci onProceso: TPr ocessI nf or mat i on;  i Resul t ado, i Codi goSal i da: DWor d;begi n  St r PCopy( sApl i caci on, sProgr ama ) ;

  Get Di r ( 0, Di r ect or i oTr abaj o ) ;  St r PCopy( Di r ect or i oActual , Di r ect or i oTr abaj o ) ;  Fi l l Char( I nf ormaci onI ni c i al , Si zeof ( I nf or maci onI ni ci al ) , #0 ) ;

Page 71: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 71/306

  I nf or maci onI ni ci al . cb : = Si zeof ( I nf or maci onI ni ci al ) ;

  I nf or maci onI ni ci al . dwFl ags : = STARTF_USESHOWWI NDOW;  I nf ormaci onI ni ci al . wShowWi ndow : = Vi si bi l i dad;  Creat eProcess( ni l , sApl i caci on, ni l , ni l , Fal se,  CREATE_NEW_CONSOLE or NORMAL_ PRI ORI TY_CLASS,

  ni l , ni l , I nf ormaci onI ni ci al , I nf or maci onProceso ) ;  / / Espera hasta que t er mi na l a ej ecuci ón  r epeat  i Codi goSal i da : = Wai t For Si ngl eObj ect ( I nf or maci onProceso. hProcess,1000 ) ;  Appl i cat i on. Pr ocessMessages;  unt i l ( i Codi goSal i da <> WAI T_TI MEOUT ) ;

  Get Exi t CodeProcess( I nf ormaci onPr oceso. hPr ocess, i Resul t ado ) ;  MessageBeep( 0 ) ;  Cl oseHandl e( I nf ormaci onProceso. hProcess ) ;  Resul t : = i Resul t ado;end;

El parámetro iVisibilidad puede ser:

SW_SHOWNORMAL - > Lo nor malSW_SHOWMI NI MI ZED - > Mi ni mi zado ( vent anas MS- DOS o vent anas nomodal es)SW_HI DE - > Ocul t o ( vent anas MS- DOS o vent anas nomodal es)

La función devuelve un cero si la ejecución terminó correctamente.

Por ejemplo para ejecutar la calculadora de Windows y esperar a quetermine:

pr ocedur e Ej ecut ar Cal cul adora;begi n  i f Ej ecut arYEsperar ( ' C: \ Wi ndows\ Syst em32\ Cal c. exe' , SW_SHOWNORMAL )= 0 t hen  ShowMessage( ' Ej ecuci ón t ermi nada con éxi t o. ' )  el se  ShowMessage( ' Ej ecuci ón no t ermi nada cor r ectament e. ' ) ;end;

Pruebas realizadas en Delphi 7.

Obtener modos de videoA la hora de realizar un programa hay que tener muy presente la resoluciónde la pantalla y el área de trabajo donde se pueden colocar las ventanas.

Para ello tenemos el objeto TScreen que nos devuelve no sólo la resoluciónactual de video sino que además nos da el tamaño del escritorio y el área detrabajo del mismo (si esta fija la barra de tareas hay que respetar su espacio yprocurar que nuestra ventana no se superponga a la misma).

Si utilizamos las propiedades Position o WindowsState del formulario no hayque preocuparse por esto, pero si hemos creado nuestra propia piel y pasamos

Page 72: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 72/306

de las ventanas normales de Windows hay que andarse con ojo y no dejar queel usuario se crea que ha desaparecido la barra de tareas.

El siguiente procedimiento vuelca la información de la pantalla actual en unobjeto Memo llamado PANTALLA:

pr ocedur e TFI nf or maci on. I nf oPant al l a;begi n  PANTALLA. Li nes. Cl ear ;  PANTALLA. Li nes. Add( For mat ( ' Resol uci ón: %dx%d ' , [ Scr een. Wi dth,Screen. Hei ght ] ) ) ;  PANTALLA. Li nes. Add( For mat ( ' Escr i t ori o: x: %d y: %d Ancho: %d Al t o:%d' ,  [ Scr een. Deskt opLef t , Scr een. Deskt opTop,Scr een. Deskt opWi dt h, Scr een. DesktopHei ght ] ) ) ;  PANTALLA. Li nes. Add( Format ( ' Ar ea de t r abaj o: x: %d y: %d Ancho: %d

Al t o: %d' ,  [ Scr een. WorkAr eaLef t , Scr een. WorkAr eaTop,Scr een. WorkAr eaWi dt h, Screen. WorkAr eaHei ght ] ) ) ;end;

Otra información interesante sería saber que resoluciones posibles tienenuestra tarjeta de video. Vamos a mostrar todos los modos de video posibleen un ListBox llamado MODOS:

pr ocedur e TFI nf ormaci on. I nf oModosVi deo;var i : I nt eger ;  ModoVi deo: TDevMode;begi n  i : = 0;  MODOS. Cl ear ;  whi l e EnumDi spl aySet t i ngs( ni l , i , ModoVi deo ) do  begi n  wi t h ModoVi deo do  MODOS. I t ems. Add( Format ( ' %dx%d %d Col or es' , [ dmPel sWi dth,dmPel sHei ght , I nt 64( 1) shl dmBi t sper Pel ] ) ) ;

  I nc( i ) ;  end;end;

Esta es la típica información que suelen mostrar los programas tipo TuneUp.Pruebas realizadas en Delphi 7.

Utilizar una fuente TTF sin instalarlaUno de los primeros inconvenientes al distribuir nuestras aplicaciones es verque en otros Windows aparecen nuestras etiquetas y campos desplazadosdebido a que la fuente que utilizan no es la que tenemos nosotros en nuestroequipo.

O bien utilizamos fuentes estandar tales como Tahoma, Arial, etc. o podemosutilizar el siguiente truco que consiste en añadir la fuente utilizada al lado denuestro ejecutable y cargarla al arrancar nuestra aplicación.

Page 73: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 73/306

El procedimiento para cargar una fuente es:

pr ocedur e Car gar Fuent e( sFuent e: St r i ng ) ;

begi n  AddFont Resour ce( PChar( Ext r actFi l ePath( Appl i cat i on. ExeName ) +sFuent e ) ) ;  SendMessage( HWND_BROADCAST, WM_FONTCHANGE, 0, 0 ) ;end;

Y en el procedimiento OnCreate de nuestro formulario cargamos la fuente:

pr ocedur e TFPr i nci pal . For mCr eat e( Sender : TObj ect ) ;begi n  Car gar Fuent e( ' Di ner . t t f ' ) ;  Et i quet a. Font . Name : = ' Di ner ' ;end;

Donde se supone que el archivo Diner.ttf  está al lado de nuestro ejecutable.

Antes de cerrar nuestra aplicación debemos liberar de memoria la fuenteutilizada con el procedimiento:

pr ocedur e El i mi nar Fuent e( sFuent e: St r i ng ) ;begi n  RemoveFontResour ce( PChar ( Ext r act Fi l ePath( Appl i cat i on. ExeName ) +sFuent e ) ) ;  SendMessage( HWND_BROADCAST, WM_FONTCHANGE, 0, 0 ) ;end;

Este prodecimiento sería llamado en el evento OnDestroy del formulario:

pr ocedur e TFPr i nci pal . For mDest r oy( Sender: TObj ect ) ;begi n  El i mi narFuent e( ' Di ner. t t f ' ) ;end;

Es recomendable hacer esto una sola vez en el formulario principal de laaplicación y no en cada formulario del programa, a menos que tengamos unformulario que utiliza exclusivamente una fuente en concreto.

Pruebas realizadas en Delphi 7.

Convertir un icono en imagen BMPAunque hay cientos de librerías de iconos por la red que suele utilizar todo elmundo para sus programas, lo ideal sería diseñar nuestros propios iconosutilizando como plantilla los que hay por Internet.

Se que hay decenas de programas de diseño que convierten entre distintosformatos, pero lo ideal sería tener nuestro propio conversor. Para ello

tenemos el siguiente procedimiento que convierte un archivo ICO en unaimagen BMP para que luego podamos utilizarla en nuestras aplicaciones:

Page 74: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 74/306

pr ocedur e Conver t i r I magen( sI cono, sBMP: St r i ng ) ;var  Bi t map: TBi t map;  I magen: TI mage;begi n

  I magen : = TI mage. Cr eat e( ni l ) ;  I magen. Pi ct ur e. LoadFromFi l e( sI cono ) ;  Bi t map : = TBi t Map. Cr eat e;

  wi t h Bi t map do  begi n  Pi xel For mat : = pf 24bi t ;  Hei ght : = Appl i cat i on. I con. Hei ght ;  Wi dt h : = Appl i cat i on. I con. Wi dt h;  Canvas. Dr aw( 0, 0, I magen. Pi ct ur e. Gr aphi c ) ;  end;

  Bi t map. SavetoFi l e( sBMP ) ;

  I magen. Fr ee;end;

El primer parámetro es el icono y el segundo la imagen BMP resultante. Loque hace el procedimiento es cargar el icono en un objeto TImage, paradespués copiar su contenido en un bitmap antes de guardarlo en un archivo.

Pruebas realizadas en Delphi 7.

Borrar archivos temporales de Internet

Uno de los componentes más útiles de Delphi hoy en día es WebBrowser elcual nos permite crear dentro de nuestros programas un navedador webutilizando el motor de Internet Explorer.

El único inconveniente es que al finalizar nuestro programa tenemos que ir aInternet Explorer y vaciar la caché, evitando que se llene el disco duro debasura.

Pues vamos a ver un procedimiento que elimina los archivos temporales deInternet Explorer, no sin antes añadir la unidad WinINet:

uses  Wi ndows, Messages, . . . , Wi nI net ;

pr ocedur e Borr arCacheI E;var  l pEnt r yI nf o: PI nt er net CacheEnt r yI nf o;  hCacheDi r : LongWor d;  dwEnt r ySi ze: LongWord;begi n  dwEnt r ySi ze : = 0;  Fi ndFi r st Ur l CacheEnt r y( ni l , TI nt er net CacheEnt r yI nf o( ni l ^ ) ,

dwEnt r ySi ze ) ;  GetMem( l pEnt r yI nf o, dwEnt r ySi ze ) ;

Page 75: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 75/306

  i f dwEntr ySi ze > 0 t hen  l pEnt r yI nf o .̂ dwSt r uct Si ze : = dwEnt r ySi ze;

  hCacheDi r : = Fi ndFi r st Ur l CacheEnt r y( ni l , l pEnt r yI nf o ,̂ dwEnt r ySi ze) ;

  i f hCacheDi r <> 0 t hen  begi n  r epeat  Del eteUr l CacheEnt r y( l pEnt r yI nf o .̂ l pszSour ceUr l Name ) ;  Fr eeMem( l pEnt r yI nf o, dwEnt r ySi ze ) ;  dwEnt r ySi ze : = 0;  Fi ndNextUr l CacheEnt r y( hCacheDi r , TI nt ernet CacheEnt r yI nf o( ni l ^) , dwEnt r ySi ze ) ;  Get Mem( l pEnt r yI nf o, dwEnt r ySi ze ) ;

  i f dwEnt r ySi ze > 0 t hen  l pEnt r yI nf o .̂ dwSt r uct Si ze : = dwEnt r ySi ze;

  unt i l not Fi ndNextUr l CacheEnt r y( hCacheDi r , l pEnt r yI nf o ,̂dwEnt r ySi ze ) ;  end;

  FreeMem( l pEnt r yI nf o, dwEnt r ySi ze ) ;  Fi ndCl oseUr l Cache( hCacheDi r ) ;end;

Este procedimiento habría que ejecutarlo al cerrar nuestro programa dejandoel sistema limpio.

Pruebas realizadas en Delphi 7.

Deshabilitar el cortafuegos de Windows XPUna de las tareas más frecuentes a las que se enfrenta un programador es lade crear aplicaciones que automaticen procesos de nuestra aplicación talescomo subir datos por FTP, conectar con otro motor de bases de datos paraenviar, etc.

Y si hay algún programa que pueda interrumpir el proceso de cara a lascomunicaciones TCP/IP es el cortafuegos de Windows. Primero añadimos auses:

uses  Wi ndows, Messages, . . . , Wi nSvc, Shel l Api ;

Este sería un el procedimiento que detiene el servicio:

pr ocedur e Deshabi l i t arCort af uegosXP;var  SCM, hServi ce: LongWor d;  sSt at us: TSer vi ceSt at us;

begi n  SCM : = OpenSCManager ( ni l , ni l , SC_MANAGER_ALL_ ACCESS ) ;  hServi ce : = OpenServi ce( SCM, PChar ( ' SharedAccess' ) ,

Page 76: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 76/306

SERVI CE_ALL_ACCESS ) ;  Cont r ol Servi ce( hServi ce, SERVI CE_CONTROL_STOP, sStat us ) ;  Cl oseServi ceHandl e( hSer vi ce ) ;end;

Para volver a activarlo sólo hay que ir al panel de control y ponerlo en marcha

de nuevo. Esto no vale para otros cortafuegos (Panda, Norton, etc.)

Pruebas realizadas en Delphi 7.

Leer el número de serie de una unidadCuando se vende un programa generalmente se suele poner el precio según elnúmero de equipos donde se va a instalar (licencia). Proteger nuestraaplicación contra copias implica leer algo característico en el PC que lo hagaúnico.

Pues bien, cuando se formatea una unidad de disco Windows le asigna unnúmero de serie que no cambiará hasta que vuelva a ser formateada. Lo quevamos a hacer es una función que toma como parámetro la unidad de discoque le pasemos (C:, D:, ...) y nos devolverá su número de serie:

f unct i on Leer Ser i eDi sco( cUni dad: Char ) : St r i ng;var  dwLongi t udMaxi ma, Vol Fl ags, dwSer i e: DWord;begi n  i f Get Vol umeI nf ormat i on( PChar ( cUni dad + ' : \ ' ) , ni l , 0,  @dwSeri e, dwLongi t udMaxi ma, Vol Fl ags, ni l ,0) t hen  begi n  / / devol vemos el número de ser i e en hexadeci mal  Resul t : = I nt ToHex( dwSeri e, 8 ) ;

  I nser t ( ' - ' , Resul t , 5 ) ;

  end  el se  Resul t : = ' ' ;end;

Nos devolverá algo como esto:

D4BD-0EC7

Con ese número ya podemos crear nuestro propio keygen alterando las letras,el orden o utilizando el algoritmo de encriptación que nos apetezca.

El único inconveniente es que si el usuario vuelve a formatear esa unidad

Page 77: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 77/306

entonces nos tiene que volver a pedir el número de serie. Hay otrosprogramadores que prefieren leer el número de la BIOS o de la tarjeta devideo, ya depende del nivel de protección que se desee.

Pruebas realizadas en Delphi 7.

Leer los archivos del portapapelesEl siguiente procedimiento lee el nombre de archivos o directorios delportapapales capturados por el usuario con CTRL + C ó CTRL + X y losmuestra en un ListBox que le pasamos como parámetro:

pr ocedur e Leer Ar chi vosPor t apapel es( Li st a: TLi st Box ) ;var  HPor t apapel es: THandl e; / / Handl e del por t apapel es  i NumAr c, i : I nt eger ; / / Nº de ar chi vos  Ar chi vo: ar r ay [ 0. . MAX_PATH - 1] of char ;begi n  i f Cl i pBoar d. HasFormat ( CF_HDROP ) t hen  begi n  HPort apapel es : = Cl i pBoard. Get AsHandl e( CF_HDROP ) ;  i NumAr c : = Dr agQuer yFi l e( HPor t apapel es, $FFFFFFFF, ni l , 0) ;

  f or i : = 0 t o i NumAr c - 1 do  begi n  Dr agQueryFi l e( HPor t apapel es, i , @Ar chi vo, MAX_PATH ) ;  Li st a. I t ems. Add( Ar chi vo ) ;  end;  end;end;

Para poder compilarlo hay que añadir las unidades externas:

uses  Wi ndows, Messages, . . . , Cl i pBr d, Shel l API ;

Sólo mostrará archivos o directorios y no imágenes o cualquier otro archivocapturado dentro de un programa. Puede sernos de utilidad para realizarprogramas de copia de seguridad, conversiones de archivo, etc.

Pruebas realizadas en Delphi 7.

Averiguar los datos del usuario de WindowsUna de las mejores cosas que se pueden hacer en un programa cuando da unerror es que nos envíe automáticamente los datos por correo electrónico.Pero es importante saber que usuario ha enviado el error dentro de la redlocal.

A continuación vamos a ver cuatro procedimientos que nos van a dar elnombre del usuario de Windows, el nombre de su PC en la red, su IP local y su

IP pública.

Page 78: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 78/306

Lo primero como siempre es añadir las unidades:

uses

  Wi ndows, Messages, . . . , Wi nSock, I dHt t p, Wi nI net;

Esta función nos devuelve el nombre del usuario:

f unct i on Leer Usuar i oWi ndows: st r i ng;var  sNombr eUsuar i o: St r i ng;  dwLongi t udNombre: DWor d;begi n  dwLongi t udNombre : = 255;  Set Lengt h( sNombreUsuar i o, dwLongi t udNombre ) ;

  i f Get User Name( PChar ( sNombreUsuar i o ) , dwLongi t udNombre ) Then  Resul t : = Copy( sNombreUsuar i o, 1, dwLongi t udNombre - 1 )  el se  Resul t : = ' Desconoci do' ;end;

Y esta otra nos da el nombre del PC en la red:

f unct i on Leer Nombr ePC: st r i ng;var  Buf f er : arr ay[ 0. . 255] of char ;  dwLongi t ud: DWor d;begi n

  dwLongi t ud : = 256;  i f GetComput erName( Buf f er , dwLongi t ud ) t hen  Resul t : = Buf f er  el se  Resul t : = ' 'end;

La siguiente nos da la IP Local en la red:

f uncti on I PLocal : St r i ng;var  p: PHost Ent ;

  s: ar r ay[ 0. . 128] of char ;  p2: pchar ;  wVer si onRequest ed: WORD;  wsaDat a: TWSADat a;begi n  / / Ar r anca l a l i br er í a Wi nSock  wVer si onRequest ed : = MAKEWORD( 1, 1 ) ;  WSAStar t up( wVer si onRequest ed, wsaDat a ) ;

  / / Obt i ene el nombr e del PC  Get Host Name( @s, 128 ) ;  p : = Get Host ByName( @s ) ;

  / / Obt i ene l a di r ecci ón I P y l i ber a l a l i br er í a Wi nSock  p2 : = i Net_ nt oa( PI nAddr ( p .̂ h_addr _l i st ^ ) ^ ) ;

Page 79: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 79/306

  Resul t : = Resul t + p2;  WSACl eanup;end;

Y esta última lo que hace es decirnos nuestra IP pública conectando con elservidor dyndns.org y utiliza el componente Indy HTTP el cual leer elcontenido del HTML:

f uncti on I P_Publ i ca: st r i ng;

  f unct i on EsNumer i co( S: st r i ng ) : Bool ean;  begi n  Resul t : = f al se;  i f ( Lengt h( S ) > 0 ) t hen  case S[ 1] of   ' 0' . . ' 9' : Resul t : = True;  end;  end;var  HTMLBody: st r i ng;  i : I nteger ;  I dHTTP: TI dHTTP;begi n  Resul t : = ' ' ;

  / / ¿Est amos conect ados a I nt ernet?  i f Wi nI net . I nt er net Get ConnectedSt at e( ni l , 0 ) t hen  begi n  I dHTTP : = TI dHTTP. Cr eate( Appl i cat i on ) ;

  t r y  HTMLBody : = I dHTTP. Get ( ' ht t p: / / checki p. dyndns. org/ ' ) ;

  f or i : = 0 t o Lengt h( HTMLBody ) - 1 do  begi n  i f EsNumer i co( HTMLBody[ i ] ) or ( HTMLBody[ i ] = ' . ' ) t hen  Resul t : = Resul t + HTMLBody[ i ] ;  end;

  f i nal l y  I dHTTP. Free;  end;  end;end;

Pruebas realizadas en Delphi 7.

Leer la cabecera PE de un programa¿Queréis verle las tripas a un archivo EXE? El siguiente procedimiento que voya mostrar lee la cabecera PE de los archivos ejecutables y nos informa delpunto de entrada del programa, el estado de los registros, la pila, etc.

Un archivo ejecutable se compone de distintas cabeceras dentro del mismo,ya sea si se va a ejecutar dentro del antiguo sistema operativo MS-DOS o encualquier versión de Windows.

El siguiente procedimiento toma como parámetro un archivo ejecutable y lo

Page 80: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 80/306

guarda en un supuesto campo memo llamado INFORMACION que se encuentraen el formulario FPrincipal:

pr ocedur e TFPr i nci pal . Exami nar EXE( sAr chi vo: St r i ng ) ;

var  FS: TFi l est r eam;  Fi r ma: DWORD;  Cabecer a_dos: I MAGE_DOS_HEADER;  Cabecer a_pe: I MAGE_FI LE_HEADER;  Cabecer a_opc: I MAGE_OPTI ONAL_ HEADER;begi n  I NFORMACI ON. Cl ear ;

  FS : = TFi l est r eam. Cr eat e( sAr chi vo, f mOpenr ead or f mShareDenyNone ) ;

  t r y  FS. Read( Cabecer a_dos, Si zeOf ( Cabecer a_dos ) ) ;

  i f Cabecer a_dos. e_magi c <> I MAGE_DOS_SI GNATURE t hen  begi n  I NFORMACI ON. Li nes. Add( ' Cabecera DOS i nvál i da' ) ;  Exi t ;  end; 

LeerCabeceraDOS( Cabecera_dos, I NFORMACI ON. Li nes ) ;

  FS. Seek( Cabecer a_dos. _l f anew, soFromBegi nni ng ) ;  FS. Read( Fi r ma, Si zeOf ( Fi r ma ) ) ;

  i f Fi r ma <> I MAGE_NT_SI GNATURE t hen

  begi n  I NFORMACI ON. Li nes. Add( ' Cabecer a PE i nvál i da' ) ;  Exi t ;  end;

  FS. Read( Cabecer a_pe, Si zeOf ( Cabecera_pe ) ) ;  LeerCabeceraPE( Cabecera_pe, I NFORMACI ON. Li nes ) ;

  i f Cabecer a_pe. Si zeOf Opt i onal Header > 0 t hen  begi n  FS. Read( Cabecer a_opc, Si zeOf ( Cabecera_opc ) ) ;  Leer Cabecer aOpci onal ( Cabecer a_opc, I NFORMACI ON. Li nes ) ;  end;

  f i n a l l y  FS. Fr ee;  end;end;

Éste a su vez llama a cada uno de los procedimientos que leen las cabecerasDOS, PE y opcional dentro del mismo EXE:

procedur e LeerCabecer aDOS( const h: I MAGE_DOS_HEADER; Memo: TSt r i ngs) ;begi n  Memo. Add( ' Cabecera DOS del ar chi vo' ) ;  Memo. Add( Format ( ' Número mági co: %d' , [ h. e_magi c] ) ) ;  Memo. Add( For mat ( ' Byes de l a úl t i ma pági na del archi vo: %d' ,[ h. e_cbl p] ) ) ;

Page 81: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 81/306

  Memo. Add( For mat ( ' Pági nas en archi vo: %d' , [ h. e_cp] ) ) ;  Memo. Add( Format ( ' Rel ocal i zaci ones: %d' , [ h. e_cr l c] ) ) ;  Memo. Add( Format ( ' Tamaño de l a cabecer a en pár r af os: %d' ,[ h. e_cpar hdr ] ) ) ;  Memo. Add( Format ( ' Mí ni mo número de pár r af os que necesi t a: %d' ,[ h. e_mi nal l oc] ) ) ;

  Memo. Add( Format ( ' Máxi mo número de pár r af os que necesi t a: %d' ,[ h. e_maxal l oc] ) ) ;  Memo. Add( For mat ( ' Val or i ni ci al ( r el at i vo) SS: %d' , [ h. e_ss] ) ) ;  Memo. Add( For mat ( ' Val or i ni ci al SP: %d' , [ h. e_sp] ) ) ;  Memo. Add( For mat ( ' Checksum: %d' , [ h. e_csum] ) ) ;  Memo. Add( For mat ( ' Val or i ni ci al I P: %d' , [ h. e_i p] ) ) ;  Memo. Add( For mat ( ' Val or i ni ci al ( r el at i vo) CS: %d' , [ h. e_cs] ) ) ;  Memo. Add( For mat ( ' Di r ecci ón del archi vo de l a t abl a derel ocal i zaci ón: %d' , [ h. e_ l f ar l c] ) ) ;  Memo. Add( For mat ( ' Número over l ay: %d' , [ h. e_ovno] ) ) ;  Memo. Add( For mat ( ' I dent i f i cador OEM ( par a e_oemi nf o) : %d' ,[ h. e_oemi d] ) ) ;  Memo. Add( Format ( ' I nf ormaci ón OEM; especí f i co e_oemi d: %d' ,[ h. e_oemi nf o] ) ) ;  Memo. Add( Format ( ' Di r ecci ón de l a nueva cabecer a exe: %d' ,[ h. _ l f anew] ) ) ;  Memo. Add( ' ' ) ;end;

procedure LeerCabeceraPE( const h: I MAGE_FI LE_HEADER; Memo: TSt r i ngs) ;var  Fecha: TDat eTi me;begi n  Memo. Add( ' Cabecera PE del archi vo' ) ;  Memo. Add( Format ( ' Máqui na: %4x' , [ h. Machi ne] ) ) ;

  case h. Machi ne of   I MAGE_FI LE_MACHI NE_UNKNOWN : Memo. Add( ' Máqui na desconoci da ' ) ;  I MAGE_FI LE_MACHI NE_I 386: Memo. Add( ' I nt el 386. ' ) ;  I MAGE_FI LE_MACHI NE_R3000: Memo. Add( ' MI PS l i t t l e- endi an, 0x160bi g- endi an ' ) ;  I MAGE_FI LE_MACHI NE_R4000: Memo. Add( ' MI PS l i t t l e- endi an ' ) ;  I MAGE_FI LE_MACHI NE_R10000: Memo. Add( ' MI PS l i t t l e- endi an ' ) ;  I MAGE_FI LE_MACHI NE_ALPHA: Memo. Add( ' Al pha_AXP ' ) ;  I MAGE_FI LE_MACHI NE_POWERPC: Memo. Add( ' I BM PowerPC Li t t l e- Endi an' ) ;  $14D: Memo. Add( ' I nt el i 860' ) ;  $268: Memo. Add( ' Mot or ol a 68000' ) ;  $290: Memo. Add( ' PA RI SC' ) ;

  el se  Memo. Add( ' t i po de máqui na desconoci da' ) ;  end;

  Memo. Add( Format ( ' Número de secci ones: %d' , [ h. Number Of Sect i ons] )) ;  Memo. Add( Format ( ' Fecha y hora: %d' , [ h. Ti meDat eSt amp] ) ) ;  Fecha : = EncodeDat e( 1970, 1, 1 ) + h. Ti medat est amp / SecsPer Day;  Memo. Add( Format DateTi me( ' c' , Fecha ) ) ;

  Memo. Add( For mat ( ' Punt ero a l a t abl a de sí mbol os: %d' ,[ h. Poi nt erToSymbol Tabl e] ) ) ;  Memo. Add( Format ( ' Número de sí mbol os: %d' , [ h. Number Of Symbol s] ) ) ;

  Memo. Add( Format ( ' Tamaño de l a cabecer a opci onal : %d' ,[ h. Si zeOf Opt i onal Header ] ) ) ;

Page 82: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 82/306

  Memo. Add( For mat ( ' Car act er í st i cas: %d' , [ h. Char acter i st i cs] ) ) ;

  i f ( I MAGE_FI LE_DLL and h. Charact eri st i cs ) <> 0 t hen  Memo. Add( ' el ar chi vo es una' )  el se  i f ( I MAGE_FI LE_EXECUTABLE_I MAGE and h. Char act er i st i cs) <> 0 t hen

  Memo. Add( ' el archi vo es un progr ama' ) ;  Memo. Add( ' ' ) ;end;

procedur e Leer Cabecer aOpci onal ( const h: I MAGE_OPTI ONAL_HEADER; Memo: TSt r i ngs ) ;begi n  Memo. Add( ' I nf ormaci ón sobre l a cabecer a PE de un ar chi vo ej ecut abl eEXE' ) ;  Memo. Add( For mat ( ' Magi c: %d' , [ h. Magi c] ) ) ;

  case h. Magi c of   $107: Memo. Add( ' I magen de ROM' ) ;  $10b: Memo. Add( ' I magen de ej ecut abl e' ) ;  el se  Memo. Add( ' Ti po de i magen desconoci do' ) ;  end;

  Memo. Add( Format ( ' Versi ón mayor del enl azador : %d' ,[ h. Maj or Li nker Ver si on] ) ) ;  Memo. Add( Format ( ' Versi ón menor del enl azador : %d' ,[ h. Mi nor Li nker Ver si on] ) ) ;  Memo. Add( Format ( ' Tamaño del códi go: %d' , [ h. Si zeOf Code] ) ) ;  Memo. Add( For mat ( ' Tamaño de l os dat os i ni ci al i zados: %d' ,[ h. Si zeOf I ni t i al i zedDat a] ) ) ;  Memo. Add( For mat ( ' Tamaño de l os dat os si n i ni ci al i zar: %d' ,[ h. Si zeOf Uni ni t i al i zedDat a] ) ) ;  Memo. Add( Format ( ' Di r ecci ón del punt o de ent r ada: %d' ,[ h. Addr essOf Ent r yPoi nt ] ) ) ;  Memo. Add( Format ( ' Base de códi go: %d' , [ h. BaseOf Code] ) ) ;  Memo. Add( For mat ( ' Base de dat os: %d' , [ h. BaseOf Dat a] ) ) ;  Memo. Add( Format ( ' I magen base: %d' , [ h. I mageBase] ) ) ;  Memo. Add( For mat ( ' Al i neami ent o de l a secci ón: %d' ,[ h. Sect i onAl i gnment ] ) ) ;  Memo. Add( For mat ( ' Al i neami ent o del archi vo: %d' ,[ h. Fi l eAl i gnment ] ) ) ;  Memo. Add( Format ( ' Versi ón mayor del si st ema operat i vo: %d' ,[ h. Maj or Operat i ngSyst emVer si on] ) ) ;  Memo. Add( Format ( ' Versi ón mayor del si st ema operat i vo: %d' ,

[ h. Mi nor Operat i ngSyst emVer si on] ) ) ;  Memo. Add( Format ( ' Ver si ón mayor de l a i magen: %d' ,[ h. Maj or I mageVer si on] ) ) ;  Memo. Add( Format ( ' Ver si ón menor de l a i magen: %d' ,[ h. Mi nor I mageVer si on] ) ) ;  Memo. Add( Format ( ' Versi ón mayor del subsi st ema: %d' ,[ h. Maj or Subsyst emVersi on] ) ) ;  Memo. Add( Format ( ' Versi ón menor del subsi st ema: %d' ,[ h. Mi nor Subsyst emVersi on] ) ) ;  Memo. Add( Format ( ' Val or de l a ver si ón Wi n32: %d' ,[ h. Wi n32Ver si onVal ue] ) ) ;  Memo. Add( Format ( ' Tamaño de l a i magen: %d' , [ h. Si zeOf I mage] ) ) ;  Memo. Add( Format ( ' Tamaño de l as cabeceras: %d' ,

[ h. Si zeOf Header s] ) ) ;  Memo. Add( For mat ( ' CheckSum: %d' , [ h. CheckSum] ) ) ;

Page 83: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 83/306

  Memo. Add( For mat ( ' Subsi st ema: %d' , [ h. Subsyst em] ) ) ;

  case h. Subsyst em of   I MAGE_SUBSYSTEM_NATI VE:  Memo. Add( ' La i magen no r equi ere un subsi st ema. ' ) ;

  I MAGE_SUBSYSTEM_WI NDOWS_GUI :  Memo. Add( ' La i magen se cor r e en un subsi st ema GUI de Wi ndows.' ) ;

  I MAGE_SUBSYSTEM_WI NDOWS_CUI :  Memo. Add( ' La i magen cor r e en un subsi st ema t er mi nal deWi ndows. ' ) ;

  I MAGE_SUBSYSTEM_OS2_CUI :  Memo. Add( ' La i magen cor r e sobr e un subsi st ema t ermi nal deOS/ 2. ' ) ;

  I MAGE_SUBSYSTEM_POSI X_CUI :  Memo. Add( ' La i magen cor r e sobre un subsi st ema t ermi nal Posi x.' ) ;  el se  Memo. Add( ' Subsi st ema desconoci do. ' )  end;

  Memo. Add( For mat ( ' Car act er í st i cas DLL: %d' , [ h. Dl l Char act er i st i cs] )) ;  Memo. Add( Format ( ' Tamaño de r eserva de l a pi l a: %d' ,[ h. Si zeOf St ackReserve] ) ) ;  Memo. Add( Format ( ' Tamaño de t r abaj o de l a pi l a: %d' ,[ h. Si zeOf St ackCommi t ] ) ) ;  Memo. Add( For mat ( ' Tamaño del Heap de r eser va: %d' ,[ h. Si zeOf HeapReserve] ) ) ;  Memo. Add( Format ( ' Tamaño de t r abaj o del Heap: %d' ,[ h. Si zeOf HeapCommi t ] ) ) ;  Memo. Add( For mat ( ' Banderas de carga: %d' , [ h. LoaderFl ags] ) ) ;  Memo. Add( For mat ( ' Numer os RVA y t amaño: %d' ,[ h. NumberOf RvaAndSi zes] ) ) ;end;

Espero que os sea de utilidad si os gusta programar herramientas deadministración de sistemas operativos Windows.

Pruebas realizadas en Delphi 7.

Leer las dimensiones de imágenes JPG, PNGy GIFSi estáis pensando en crear un visor de fotografías aquí os traigo tresprocedimientos que leen al ancho y alto de imagenes con extensión JPG, PNGy GIF leyendo los bytes de su cabecera. No hay para BMP ya que se puedehacer con un componente TImage.

Antes de nada hay que incluir una función que lee los enteros almacenados enformato del procesador motorola, que guarda los formatos enteros en

memoria al contrario de los procesadores Intel/AMD:

Page 84: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 84/306

f unct i on Leer Pal abr aMot or ol a( F: TFi l eSt r eam ) : Wor d;type  TPal abr aMotorol a = r ecor d  case Byt e of   0: ( Val ue: Wor d ) ;

  1: ( Byt e1, Byt e2: Byte ) ;  end;var  MW: TPal abr aMot or ol a;begi n  F. Read( MW. Byte2, Si zeOf ( Byt e ) ) ;  F. Read( MW. Byte1, Si zeOf ( Byt e ) ) ;  Resul t : = MW. Val ue;end;

El siguiente procedimiento toma como parámetros la ruta y nombre de unaimagen JPG, y dos variales enteras donde se almacenará el ancho y alto de laimagen:

pr ocedur e Di mensi onJ PG( sAr chi vo: st r i ng; var wAncho, wAl t o: Word ) ;const  Val i dSi g: ar r ay[ 0. . 1] of Byt e = ( $FF, $D8) ;  Par amet er l ess = [ $01, $D0, $D1, $D2, $D3, $D4, $D5, $D6, $D7] ;var  Si g: ar r ay[ 0. . 1] of byt e;  F: TFi l eSt r eam;  x: i nt eger ;  Seg: byt e;  Dummy: ar r ay[ 0. . 15] of byt e;  Len: wor d;  i Longi t udLi nea: LongI nt ;begi n  Fi l l Char( Si g, Si zeOf ( Si g ) , #0 ) ;

  F : = TFi l eSt r eam. Cr eat e( sAr chi vo, f mOpenRead ) ;  t r y  i Longi t udLi nea : = F. Read( Si g[ 0] , Si zeOf ( Si g ) ) ;

  f or x : = Low( Si g ) t o Hi gh( Si g ) do  i f Si g[ x] <> Val i dSi g[ x] t hen  i Longi t udLi nea : = 0;

  i f i Longi t udLi nea > 0 t hen  begi n

  i Longi t udLi nea : = F. Read( Seg, 1 ) ;

  whi l e ( Seg = $FF ) and ( i Longi t udLi nea > 0 ) do  begi n  i Longi t udLi nea : = F. Read( Seg, 1 ) ;

  i f Seg <> $FF t hen  begi n  i f ( Seg = $C0 ) or ( Seg = $C1 ) t hen  begi n  i Longi t udLi nea : = F. Read( Dummy[ 0] , 3 ) ; / / Nos sal t amosest os byt es  wAl t o : = Leer Pal abr aMot orol a( F ) ;

  wAncho : = Leer Pal abr aMot orol a( F ) ;  end

Page 85: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 85/306

  el se  begi n  i f not ( Seg i n Par amet erl ess ) t hen  begi n  Len : = Leer Pal abr aMot orol a( F ) ;  F. Seek( Len - 2, 1 ) ;

  F. r ead( Seg, 1 ) ;  end  el se  Seg : = $FF; { Fake i t t o keep l oopi ng. }  end;  end;  end;  end;  f i n a l l y  F. Free;  end;end;

Lo mismo para una imagen PNG:pr ocedur e Di mensi onPNG( sAr chi vo: st r i ng; var wAncho, wAl t o: Word ) ;type  TPNGSi g = arr ay[ 0. . 7] of Byte;const  Val i dSi g: TPNGSi g = ( 137, 80, 78, 71, 13, 10, 26, 10) ;var  Si g: TPNGSi g;  F: TFi l eSt r eam;  x: I nt eger ;begi n  Fi l l Char( Si g, Si zeOf ( Si g ) , #0 ) ;

  F : = TFi l eSt r eam. Cr eat e( sAr chi vo, f mOpenRead ) ;  t r y  F. read( Si g[ 0] , Si zeOf ( Si g ) ) ;

  f or x : = Low( Si g ) t o Hi gh( Si g ) do  i f Si g[ x] <> Val i dSi g[ x] t hen  Exi t ;

  F. Seek( 18, 0 ) ;  wAncho : = Leer Pal abr aMot orol a( F ) ;  F. Seek( 22, 0 ) ;  wAl t o : = Leer Pal abr aMotorol a( F ) ;  f i n a l l y

  F. Free;  end;end;

Y para una imagen GIF:

pr ocedur e Di mensi onGI F( sAr chi vo: st r i ng; var wAncho, wAl t o: Word ) ;type  TCabeceraGI F = r ecord  Si g: ar r ay[ 0. . 5] of char ;  Scr eenWi dt h, ScreenHei ght : Word;  Fl ags, Backgr ound, Aspect : Byte;  end;

  TBl oqueI magenGI F = r ecor d

Page 86: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 86/306

  Lef t , Top, Wi dt h, Hei ght : Wor d;  Fl ags: Byt e;  end;var  F: f i l e;  Cabecera: TCabeceraGI F;

  Bl oqueI magen: TBl oqueI magenGI F;  i Resul t ado: I nt eger ;  x: I nt eger ;  c: char ;  bEncont r adasDi mensi ones: Bool ean;begi n  wAncho : = 0;  wAl t o : = 0;

  i f sAr chi vo = ' ' t hen  Exi t ;

  {$I - }  Fi l eMode : = 0; / / Sól o l ectura  Assi gnFi l e( F, sAr chi vo ) ;  Reset ( F, 1) ;  i f I OResul t <> 0 t hen  Exi t ;

  / / Lee l a cabecera y se asegur a de que sea un archi vo vál i do  Bl ockRead( F, Cabecer a, Si zeOf ( TCabecer aGI F ) , i Resul t ado ) ;

  i f ( i Resul t ado <> Si zeOf ( TCabecer aGI F ) ) or ( I OResul t <> 0 ) or  ( St r LComp( ' GI F' , Cabecer a. Si g, 3 ) <> 0 ) then  begi n  Cl ose( F ) ;  Exi t ;  end;

  { Ski p col or map, i f t her e i s one }  i f ( Cabecer a. Fl ags and $80 ) > 0 t hen  begi n  x : = 3 * ( 1 shl ( ( Cabecer a. Fl ags and 7 ) + 1 ) ) ;  Seek( F, x ) ;  i f I OResul t <> 0 t hen  begi n  Cl ose( F ) ;  Exi t ;

  end;  end;

  bEncont r adasDi mensi ones : = Fal se;  Fi l l Char ( Bl oqueI magen, Si zeOf ( TBl oqueI magenGI F ) , #0 ) ;

  Bl ockRead( F, c, 1, i Resul t ado ) ;  whi l e ( not EOF( F ) ) and ( not bEncont r adasDi mensi ones ) do  begi n  case c of   ' , ' : / / Encont r ada i magen  begi n  Bl ockRead( F, Bl oqueI magen, Si zeOf ( TBl oqueI magenGI F ) ,i Resul t ado ) ;  i f i Resul t ado <> Si zeOf ( TBl oqueI magenGI F ) t hen

  begi n  Cl ose( F ) ;

Page 87: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 87/306

  Exi t ;  end;

  wAncho : = Bl oqueI magen. Wi dt h;  wAl t o : = Bl oqueI magen. Hei ght ;  bEncontr adasDi mensi ones : = True;

  end; ' ÿ' : / / esqui var esto

  begi n  / / Nada  end;

  / / No hacer nada, i gnorar  end;

  Bl ockRead( F, c, 1, i Resul t ado ) ;  end;

  Cl ose( F ) ;  {$I +}end;

Así no será necesario cargar toda la imagen para averiguar sus dimensiones.

Pruebas realizadas en Delphi 7.

Descargar un archivo de Internet sin utilizarcomponentesAñadiendo a nuestro formulario la librería WinINet se pueden descargararchivos por HTTP con la siguiente función:

f unct i on Descar gar Ar chi vo( sURL, sAr chi voLocal : St r i ng ) : bool ean;const Buf f erSi ze = 1024;var  hSessi on, hURL: HI nt ernet ;  Buf f er : arr ay[ 1. . Buf f er Si ze] of Byt e;  Longi t udBuf f er : DWORD;  F: Fi l e;  sMi Progr ama: St r i ng;begi n  sMi Progr ama : = Ext r act Fi l eName( Appl i cat i on. ExeName ) ;  hSessi on : = I nt ernetOpen( PChar( sMi Progr ama ) ,I NTERNET_OPEN_TYPE_PRECONFI G, ni l , ni l , 0 ) ;

  t r y  hURL : = I nt er netOpenURL( hSessi on, PChar ( sURL ) , ni l , 0, 0, 0 ) ;

  t r y  Assi gnFi l e( F, sAr chi voLocal ) ;

Page 88: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 88/306

  Rewr i t e( F, 1 ) ;

  r epeat  I nt er net ReadFi l e( hURL, @Buf f er , Si zeOf ( Buf f er ) ,Longi t udBuf f er ) ;  Bl ockWr i t e( F, Buf f er , Longi t udBuf f er ) ;

  unt i l Longi t udBuf f er = 0;  Cl oseFi l e( F ) ;  Resul t : = True;  f i nal l y  I nt ernetCl oseHandl e( hURL ) ;  end  f i n a l l y  I nt er netCl oseHandl e( hSessi on ) ;  endend;

El primer parámetro es la URL completa del archivo a descargar y el segundo

la ruta y nombre del archivo donde se va a guardar en nuestro disco duro. Unejemplo de llamada a la función sería:

Descar gar Ar chi vo( ' ht t p: \ \ mi web. com\ i magen. j pg' , ' C: \ Mi sdocument os\ i magen. j pg' ) ;

Averiguar el nombre del procesador y suvelocidadEl registro de Windows suele almacenar gran cantidad de información no sólode la configuración de los programas instalados, sino también el estado realdel hardware de nuestro PC.

En esta ocasión vamos a leer el nombre del procesador y su velocidad desdenuestro programa. Antes de nada añadimos a uses:

uses  Wi ndows, Messages, . . . , Regi st r y;

La siguiente función nos devuelve el nombre del procesador:

f unct i on Nombr ePr ocesador: st r i ng;var  Regi st r o: TRegi st r y;begi n  Resul t : = ' ' ;

  Regi st r o : = TRegi st r y. Cr eat e;  t r y

Page 89: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 89/306

  Regi st r o. Root Key : = HKEY_LOCAL_MACHI NE;

  i f Regi st r o. OpenKey(' \ Har dwar e\ Descr i pt i on\ Syst em\ Cent r al Pr ocessor\ 0' , Fal se ) t hen  Resul t : = Regi str o. ReadSt r i ng( ' I dent i f i er ' ) ;  f i n a l l y

  Regi st r o. Free;  end;end;

Y esta otra nos da su velocidad (según la BIOS y el fabricante):

f unct i on Vel oci dadPr ocesador : st r i ng;var  Regi st r o: TRegi st r y;begi n  Regi st r o : = TRegi st r y. Cr eat e;  t r y  Regi st r o. Root Key : = HKEY_LOCAL_MACHI NE;

  i f Regi st r o. OpenKey(' Har dwar e\ Descr i pt i on\ Syst em\ Cent r al Pr ocessor \ 0' , Fal se ) t hen  begi n  Resul t : = I nt ToSt r ( Regi st r o. ReadI nt eger( ' ~MHz' ) ) + ' MHz' ;  Regi st r o. Cl oseKey;  end;  f i n a l l y  Regi st r o. Free;  end;end;

Hay veces que dependiendo del procesador y del multiplicador de la BIOS casinunca coincide la velocidad real que nos da Windows con la de verdad (sobretodo en procesadores AMD). Aquí tenemos otra función que calcula en unsegundo la velocidad real del procesador con una pequeña rutina enensamblador:

f unct i on Cal cul ar Vel oci dadProcesador : Doubl e;const  Ret ar do = 500;var  Ti mer Hi , Ti mer Lo: DWORD;  Cl asePr i or i dad, Pr i or i dad: I nt eger ;begi n

  Cl asePr i or i dad : = Get Pr i or i t yCl ass( Get Cur r ent Pr ocess ) ;  Pr i ori dad : = Get Thr eadPr i ori t y( GetCur r ent Thr ead ) ;

  SetPr i or i t yCl ass( GetCur r ent Process, REALTI ME_PRI ORI TY_CLASS ) ;  SetThr eadPr i or i t y( Get Curr ent Thr ead, THREAD_PRI ORI TY_TI ME_CRI TI CAL) ;

  Sl eep( 10 ) ;

  asm  dw 310Fh  mov Ti mer Lo, eax  mov Ti merHi , edx

  end;

Page 90: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 90/306

  Sl eep( Retar do ) ;

  asm  dw 310Fh  sub eax, Ti merLo  sbb edx, Ti mer Hi

  mov Ti mer Lo, eax  mov Ti merHi , edx  end;

  Set Thr eadPr i or i t y( Get Cur r ent Thr ead, Pri or i dad ) ;  Set Pr i or i t yCl ass( Get Cur r ent Pr ocess, Cl asePr i or i dad ) ;

  Resul t : = Ti merLo / ( 1000 * Retardo ) ;end;

Nos devuelve el resultado en una variable double, donde que podríamos sacarla información en pantalla de la siguiente manera:

ShowMessage( Format ( ' Vel oci dad cal cul ada: %f MHz' ,[ Cal cul ar Vel oci dadPr ocesador ] ) ) ;

Pruebas realizadas en Delphi 7.

Generar claves aleatoriasEl siguiente procedimiento que voy a mostrar genera una clave aleatoriasegún el número de sílabas y números que le indiquemos. Por ejemplo:

Generar Cl ave( 3, 2 )

puede devolver:

cat amo56

di l ema12cat oye97. . .

Aquí tenemos el generador de claves:

f unct i on Gener ar Cl ave( i Si l abas, i Numer os: Byte ) : st r i ng;const  Consonant e: ar r ay [ 0. . 19] of Char = ( ' b' , ' c' , ' d' , ' f ' , ' g' , ' h' ,' j ' ,  ' k' , ' l ' , ' m' , ' n' , ' p' , ' r ' ,' s ' ,  ' t ' , ' v' , ' w' , ' x' , ' y' , ' z'

) ;

Page 91: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 91/306

  Vocal : ar r ay [0. . 4] of Char = ( ' a' , ' e' , ' i ' , ' o' , ' u' ) ;

  f uncti on Repet i r ( sCar acter: St r i ng; i Veces: I nt eger ) : str i ng;  var  i : I nteger ;  begi n

  Resul t : = ' ' ;  f or i : = 1 t o i Veces do  Resul t : = Resul t + sCaracter;  end;

var  i : I nteger ;  s i , sf : Longi nt ;  n: str i ng;begi n  Resul t : = ' ' ;  Randomi ze;

  i f i Si l abas <> 0 t hen  f or i : = 1 t o i Si l abas do  begi n  Resul t : = Resul t + Consonante[ Random( 19) ] ;  Resul t : = Resul t + Vocal [ Random( 4) ] ;

  end;

  i f i Numeros = 1 t hen  Resul t : = Resul t + I nt ToSt r ( Random( 9) )  el se  i f i Numeros >= 2 t hen  begi n  i f i Numeros > 9 t hen  i Numer os : = 9;

  si : = St r ToI nt ( ' 1' + Repet i r ( ' 0' , i Numer os - 1 ) ) ;  sf : = St r ToI nt ( Repet i r ( ' 9' , i Numer os ) ) ;  n : = Fl oat ToSt r ( si + Random( sf ) ) ;  Resul t : = Resul t + Copy( n, 0, i Numeros ) ;  end;end;

Suele utilizarse en la creación de cuentas de usuario y departamentos enprogramas de gestión, dejando que posteriormente el usuario puedacambiarse la clave.

Pruebas realizadas en Delphi 7.

Meter recursos dentro de un ejecutableUna de las cosas que mas hacen engordar el tamaño de un ejecutable esmeter los mismos botones con imagenes en distintos formularios. Si diseñamosnuestros propios botones Aceptar y Cancelar y los vamos replicando en cadaformulario el tamaño de nuestro programa puede crecer considerablemente.

Para evitar esto lo que vamos a hacer es meter la imagen dentro de unrecurso compilado y posteriormente accederemos a la misma dentro del

programa. La ventaja de utilizar este método es que la imagen sólo esta unavez en el programa independientemente del número de formularios donde

Page 92: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 92/306

vaya a aparecer.

Supongamos que vamos a meter la imagen MiBoton.jpg dentro de nuestroprograma. Creamos al lado de la misma el archivo imagenes.rc el cualcontiene:

1 RCDATA Mi Bot on. j pg

Se pueden meter en un archivo de recursos tantas imagenes como se desee,así como otros tipos de archivo (sonidos, animaciones flash, etc.). Lassiguientes líneas serían:

2 RCDATA i magen2. j pg3 RCDATA i magen3. j pg. . .

Ahora abrimos una ventana de símbolo del sistema dentro del mismodirectorio donde este la imagen y compilamos el recurso:

c: \ i magenes\ br c32 - r - v i magenes. r c

Lo cual nos creará el archivo imagenes.res.

Ahora para utilizar el recurso dentro de nuestro programa hay que añadirdebajo de implementation (del formulario principal de la aplicación) ladirectiva {$R imagenes.res}:

i mpl ement at i on

{$R *. df m}{$R i magenes. r es}

Esto lo que hace es unir todos los recursos de imagenes.res con nuestroejecutable. Para cargar la imagen dentro de un objeto TImage hacemos losiguiente:

pr ocedur e TFPr i nci pal . For mCr eat e( Sender : TObj ect ) ;var  Recur sos: TResourceSt r eam;  I magen: TJ PegI mage;begi n  I magen : = TJ PegI mage. Cr eat e;  Recur sos : = TResour ceSt r eam. Cr eat e( hI nst ance, ' #1' , RT_RCDATA ) ;  Recur sos. Seek( 0, soFromBegi nni ng ) ;  I magen. LoadFr omSt r eam( Recursos ) ;  I magen1. Canvas. Dr aw( 0, 0, I magen ) ;  Recur sos. Free;  I magen. Fr ee;end;

Donde Imagen1 es un objeto TImage. Aunque pueda parecer algo molesto

tiene las siguientes ventajas:

Page 93: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 93/306

- Evita que un usuario cualquiera utilice nuestras imagenes (a menos claro quesepa utilizar un editor de recursos).

- Facilita la distribución de nuestro programa (va todo en el exe).

- Se puede añadir cualquier tipo de archivo o dato dentro del ejecutable.

- Ahorramos mucha memoria al cargar una sola vez el recurso.

Pruebas realizadas en Delphi 7.

Dibujar un gradiente en un formularioCon un sencillo procedimiento podemos hacer que el fondo de nuestrosformularios quede con un aspecto profesional con un degradado de color. Paraello escribimos en el evento OnPaint del formulario:

pr ocedur e TFormul ar i o. For mPai nt ( Sender: TObj ect ) ;var  wFi l a, wVer t i cal : Wor d;  i Roj o: I nt eger ;begi n  i Roj o : = 200;  wVer t i cal : = ( Cl i ent Hei ght + 512 ) di v 256;

  f or wFi l a : = 0 t o 512 do  begi n  wi t h Canvas do  begi n  Br ush. Col or : = RGB( i Roj o, 0, wFi l a ) ;  Fi l l Rect( Rect( 0, wFi l a * wVer t i cal , Cl i ent Wi dt h, ( wFi l a + 1 )* wVer t i cal ) ) ;  Dec( i Roj o ) ;  end;  end;end;

Lo que hace es crear un barrido vertical según el ancho y alto de nuestroformulario y va restando de la paleta RGB el componente rojo. Si queremoscambiar el color sólo hay que jugar con los componentes RGB hasta conseguir

el efecto deseado.

Al maximizar o cambiar el ancho y alto de la ventana se quedará el degradadocortado. Para evitar esto le decimos en el evento OnResize que vuelva apintar la ventana:

pr ocedur e TFor mul ar i o. For mResi ze( Sender : TObj ect ) ;begi n  Repai nt ;end;

Este efecto suele utilizarse mucho instalaciones o en CD-ROM interactivos,catálogos, etc.

Page 94: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 94/306

Pruebas realizadas en Delphi 7.

Trocear y unir archivosUna de las utilidades más famosas que se han asociado a la descarga dearchivos es el programa Hacha, el cual trocea archivos a un cierto tamañopara luego poder unirlos de nuevo.

El siguiente procedimiento parte un archivo a la longitud en bytes que lepasemos:

pr ocedur e Trocear Ar chi vo( sAr chi vo: TFi l eName; i Longi t udTrozo: I nt eger) ;var  i : Wor d;  FS, St r eam: TFi l eSt r eam;

  sAr chi voPar t i do: St r i ng;begi n  FS : = TFi l eSt r eam. Cr eat e( sAr chi vo, f mOpenRead or f mShareDenyWr i t e) ;

  t r y  f or i : = 1 t o Tr unc( FS. Si ze / i Longi t udTrozo ) + 1 do  begi n  sAr chi voPar t i do : = ChangeFi l eExt ( sAr chi vo, ' . ' + For mat Fl oat (' 000' , i ) ) ;  St r eam: = TFi l eSt r eam. Cr eat e( sAr chi voPar t i do, f mCr eat e orf mShareExcl usi ve ) ;

  t r y  i f f s. Si ze - f s. Posi t i on < i Longi t udTr ozo t hen  i Longi t udTr ozo : = FS. Si ze - FS. Posi t i on;

  St r eam. CopyFrom( FS, i Longi t udTrozo ) ;  f i n a l l y  St r eam. Fr ee;  end;  end;  f i n a l l y  FS. Fr ee;  end;end;

Si por ejemplo le pasamos el archivo Documentos.zip creará los archivos:

Documentos.001Documentos.002....

Para volver a unirlo tenemos otro procedimiento donde le pasamos el primertrozo y el nombre del archivo original:

pr ocedur e Uni r Ar chi vo( sTrozo, sAr chi voOr i gi nal : TFi l eName ) ;

var  i : i nteger ;

Page 95: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 95/306

  FS, St r eam: TFi l eSt r eam;begi n  i : = 1;  FS : = TFi l eSt r eam. Cr eat e( sAr chi voOr i gi nal , f mCr eate orf mShareExcl usi ve ) ;

  t r y  whi l e Fi l eExi st s( sTr ozo ) do  begi n  St r eam : = TFi l eSt r eam. Cr eat e( sTrozo, f mOpenRead orf mShar eDenyWr i t e ) ;

  t r y  FS. CopyFrom( St r eam, 0 ) ;  f i n a l l y  St r eam. Fr ee;  end;

  I nc( i ) ;  sTr ozo : = ChangeFi l eExt ( sTr ozo, ' . ' + For mat Fl oat ( ' 000' , i )) ;  end;  f i n a l l y  FS. Fr ee;  end;end;

Una ampliación interesante a estos procedimientos sería meter el nombre delarchivo original en el primer o último trozo, así como un hash (MD4, MD5,SHA, etc.) para saber si algún trozo está defectuoso.

Pruebas realizadas en Delphi 7.Mover componentes en tiempo de ejecuciónPara darle un toque profesional a un programa no esta mal añadir un editorque permita al usuario personalizar sus formularios, informes o listados. Porejemplo en un programa de facturación sería interesante que el usuariopudiera personalizar el formato de su factura.

Antes de nada hay que crear en la sección private del formulario tresvariables encargadas de guardar las coordenadas del componente que se estamoviendo así como una variable booleana que nos dice si en este momento seesta moviendo un componente:

  pr i vat e  { Pr i vat e decl ar at i ons }  i ComponenteX, i Component eY: I nteger ;  bMovi endo: Bool ean;

Creamos dentro de type la definición de un control movible:

t ype TMovi bl e = cl ass ( TCont r ol ) ;

Page 96: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 96/306

Los siguientes procedimientos hay que asignarlos a un componente para quepuedan ser movidos por todo el formulario al ejecutar el programa:

pr ocedur e TFPr i nci pal . Cont r ol MouseDown( Sender : TObj ect ; But t on: TMouseBut t on; Shi f t : TShi f t St at e; X, Y: I nt eger ) ;begi n  i Component eX : = X;  i Component eY : = Y;  bMovi endo : = True;  TMovi bl e( Sender ) . MouseCapt ure : = True;end;

pr ocedur e TFPr i nci pal . Cont r ol MouseMove( Sender: TObj ect ; Shi f t : TShi f t St at e; X, Y: I nt eger ) ;begi n  i f bMovi endo t hen  wi t h Sender as TCont r ol do  begi n

  Lef t : = X - i Component eX + Lef t ;  Top : = Y - i Component eY + Top;  end;end;

pr ocedur e TFPr i nci pal . Cont r ol MouseUp( Sender : TObj ect ; But t on: TMouseBut t on; Shi f t : TShi f t St at e; X, Y: I nt eger ) ;begi n  i f bMovi endo t hen  begi n  bMovi endo : = Fal se;  TMovi bl e( Sender ) . MouseCapt ur e : = Fal se;  end;

end;

Por ejemplo, si queremos mover una etiqueta por el formulario habría queasignar los eventos:

Label 1. OnMouseDown : = Cont r ol MouseDown;Label 1. OnMouseUp : = Cont r ol MouseUp;Label 1. OnMouseMove : = Cont r ol MouseMove;

Con esto ya podemos crear nuestro propio editor de formularios sin tener queutilizar las propiedades DragKing y DragMode de los formularios que resultan

algo engorrosas.Pruebas realizadas en Delphi 7.

Trabajando con arrays dinámicosUna de las ventajas de los arrays dinámicos respecto a los estáticos es quepueden modificar su tamaño en tiempo de ejecución y no es necesaria lagestión de memoria para los mismos, ya que se liberan automáticamente alterminar el procedimiento, función o clase donde están alojados. También sonideales para enviar un número indeterminado de parámetros a funciones oprocedimientos.

Page 97: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 97/306

Para crear una array dinámico lo que hacemos es declarar el array pero sinespecificar su tamaño:

  publ i c

  Cl i ent es: ar r ay of str i ng;

Antes de meter un elemento al array hay que especificar su tamaño con lafunción SetLength. En este ejemplo creamos tres elementos:

  Set Lengt h( Cl i ent es, 3 ) ;

Y ahora introducimos los datos en el mismo:

  Cl i ent es[ 0] : = ' J UAN' ;  Cl i ent es[ 1] : = ' CARLOS' ;  Cl i ent es[2] : = ' MARI A' ;

A diferencia de los arrays estáticos el primer elemento es el cero y noel uno. Como he dicho antes no es necesario liberarlos de memoria, perosi aún así deseamos hacerlo sólo es necesario hacer lo siguiente:

  Cl i ent es : = ni l ;

con lo cual el array queda inicializado para que pueda volver a ser utilizado.

Pruebas realizadas en Delphi 7.

Clonar las propiedades de un controlCuantas veces hemos deseado en tiempo de ejecución copiar lascaracterísticas de un control a otro según las acciones del usuario. Porejemplo si tenemos nuestro propio editor de informes se podrían copiar lascarácterísticas de las etiquetas seleccionas por el usuario al resto delformulario (font, color, width, etc.)

Para ello tenemos que añadir la unidad TypInfo:

uses  Wi ndows, Di al ogs, . . . , TypI nf o;

A esta función que voy a mostrar hay que pasarle el control origen y el destino(la copia) así como que propiedades deseamos copiar:

f unct i on Cl onarPropi edades( Or i gen, Dest i no: TObj ect ;  Pr opi edades: ar r ay of st r i ng ) : Bool ean;var

Page 98: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 98/306

  i : I nteger ;begi n  Resul t : = True;  t r y  f or i : = Low( Propi edades ) t o Hi gh( Pr opi edades ) do  begi n

  / / ¿Exi st e l a pr opi edad en el cont r ol or i gen?  i f not I sPubl i shedPr op( Or i gen, Pr opi edades[ I ] ) t hen  Cont i nue;

  / / ¿Exi st e l a pr opi edad en el cont r ol dest i no?  i f not I sPubl i shedPr op( Dest i no, Pr opi edades[ I ] ) t hen  Cont i nue;

  / / ¿Son del mi smo t i po l as dos pr opi edades?i f Pr opType( Or i gen, Pr opi edades[ I ] ) <>

  PropType( Dest i no, Pr opi edades[ I ] ) t hen  Cont i nue;

  / / Copi amos l a pr opi edad según si es var i abl e o método  case Pr opType(Or i gen, Pr opi edades[ i ] ) of   t kCl ass:  Set Obj ect Pr op( Dest i no, Pr opi edades[ i ] ,  Get Obj ect Pr op( Or i gen, Propi edades[i ] ) ) ;

  t kMet hod:  Set Met hodPr op( Dest i no, Propi edades[ I ] ,  GetMet hodProp( Or i gen, Propi edades[ I ] ) ) ;  el se  Set Pr opVal ue( Dest i no, Pr opi edades[ i ] ,  Get Pr opVal ue( Or i gen, Pr opi edades[ i ] ) ) ;  end;  end;  except  Resul t : = Fal se;  end;end;

Para copiar las características principales de una etiqueta habría que llamar ala función de la siguiente manera:

  Cl onar Pr opi edades( Label 1, Label 2, [ ' Font ' , ' Col or ' , ' Al i gnment ' ,  ' Wi dth' , ' Hei ght ' , ' Layout ' ] ) ;

También se pueden copiar eventos tales como OnClick, OnMouseDown, etc.permitiendo así abarcar muchos controles con un solo evento.

Pruebas realizadas en Delphi 7.

Aplicar antialiasing a una imagenEl algoritmo antialiasing se suele aplicar a una imagen para evitar los bordesdentados y los degradados bruscos de color. Lo que hace es suavizar toda laimagen.

En este caso el siguiente procedimiento toma un objeto TImage como primer

parámetro y como segundo el porcentaje de antialiasing deseado, siendonormal no aplicar más del 10 o 20%:

Page 99: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 99/306

pr ocedur e Ant i al i asi ng( I magen: TI mage; i Por cent aj e: I nt eger ) ;type  TRGBTri pl eAr r ay = arr ay[ 0. . 32767] of TRGBTr i pl e;  PRGBTr i pl eAr r ay = T̂RGBTr i pl eAr r ay;var

  SL, SL2: PRGBTri pl eAr r ay;  l , m, p: I nt eger ;  R, G, B: TCol or ;  R1, R2, G1, G2, B1, B2: Byt e;begi n  wi t h I magen. Canvas do  begi n  Br ush. St yl e : = bsCl ear;  Pi xel s[1, 1] : = Pi xel s[ 1, 1] ;

  f or l : = 0 t o I magen. Hei ght - 1 do  begi n  SL : = I magen. Pi ct ur e. Bi t map. ScanLi ne[ l ] ;

  f or p : = 1 t o I magen. Wi dt h - 1 do  begi n  R1 : = SL[ p] . r gbt Red;  G1 : = SL[ p] . r gbtGr een;  B1 : = SL[ p] . r gbt Bl ue;

  i f ( p < 1) t hen  m : = I magen. Wi dt h  el se  m : = p - 1;

  R2 : = SL[m] . r gbt Red;  G2 : = SL[m] . r gbt Gr een;  B2 : = SL[ m] . r gbtBl ue; 

i f ( R1 <> R2 ) or ( G1 <> G2 ) or ( B1 <> B2 ) t hen  begi n  R : = Round( R1 + ( R2 - R1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  G : = Round( G1 + ( G2 - G1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  B : = Round( B1 + ( B2 - B1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  SL[ m] . r gbt Red : = R;  SL[ m] . r gbt Gr een : = G;  SL[ m] . r gbtBl ue : = B;

  end;

  i f ( p > I magen. Wi dt h - 2 ) t hen

  m : = 0  el se  m : = p + 1;

  R2 : = SL[m] . r gbt Red;  G2 : = SL[m] . r gbt Gr een;  B2 : = SL[ m] . r gbtBl ue;

  i f ( R1 <> R2 ) or ( G1 <> G2 ) or ( B1 <> B2 ) t hen  begi n  R : = Round( R1 + ( R2 - R1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  G : = Round( G1 + ( G2 - G1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  B : = Round( B1 + ( B2 - B1 ) * 50 / ( i Por cent aj e + 50 ) ) ;

  SL[ m] . r gbt Red : = R;  SL[ m] . r gbt Gr een : = G;

Page 100: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 100/306

  SL[ m] . r gbt Bl ue : = B;  end;

  i f ( l < 1 ) t hen  m : = I magen. Hei ght - 1  el se

  m : = l - 1;  SL2 : = I magen. Pi ct ur e. Bi t map. ScanLi ne[ m] ;  R2 : = SL2[ p] . r gbt Red;  G2 : = SL2[ p] . r gbt Gr een;  B2 : = SL2[ p] . r gbt Bl ue;

  i f ( R1 <> R2 ) or ( G1 <> G2 ) or ( B1 <> B2 ) t hen  begi n  R : = Round( R1 + ( R2 - R1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  G : = Round( G1 + ( G2 - G1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  B : = Round( B1 + ( B2 - B1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  SL2[ p] . r gbt Red : = R;  SL2[ p] . r gbt Gr een : = G;  SL2[ p] . r gbt Bl ue : = B;  end;

  i f ( l > I magen. Hei ght - 2 ) t hen  m : = 0  el se  m : = l + 1;

  SL2 : = I magen. Pi ct ur e. Bi t map. ScanLi ne[ m] ;  R2 : = SL2[ p] . r gbt Red;  G2 : = SL2[ p] . r gbt Gr een;  B2 : = SL2[ p] . r gbt Bl ue;

  i f ( R1 <> R2 ) or ( G1 <> G2 ) or ( B1 <> B2 ) t hen  begi n  R : = Round( R1 + ( R2 - R1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  G : = Round( G1 + ( G2 - G1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  B : = Round( B1 + ( B2 - B1 ) * 50 / ( i Por cent aj e + 50 ) ) ;  SL2[ p] . r gbt Red : = R;  SL2[ p] . r gbt Gr een : = G;  SL2[ p] . r gbt Bl ue : = B;  end;  end;  end;  end;end;

Suponiendo que tengamos en un formulario un objeto TImage llamado Image1llamaríamos a este procedimiento de la siguiente manera:

  Ant i al i asi ng( I mage1, 10 ) ;

Dibujar varias columnas en un ComboBoxVamos a ver un ejemplo de dibujar tres columnas al desplegar un ComboBox.El truco está en guardar el valor de las tres columnas en el mismo item pero

separado por un punto y coma.

Page 101: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 101/306

Después implementamos nuestra propia función de dibujado de columnas paraque muestre las tres columnas. Para ello metemos en el evento OnDrawItemdel ComboBox:

pr ocedur e TFor mul ar i o. ComboBoxDr awI t em( Cont r ol : TWi nCont r ol ; I ndex:I nt eger ;  Rect : TRect ; St at e:

 TOwnerDr awSt at e ) ;var  sVal or , sTodo: st r i ng;  i , i Pos: I nt eger ;  r c: TRect ;  AnchoCol umna: ar r ay[0. . 3] of I nt eger ;begi n  ComboBox. Canvas. Br ush. St yl e : = bsSol i d;  ComboBox. Canvas. Fi l l Rect ( Rect ) ;

  / / Las col umnas deben i r separadas por un ;  sTodo : = ComboBox. I t ems[ I ndex] ;

  / / Est abl ecemos el ancho de l as col umnas  AnchoCol umna[ 0] : = 0;  AnchoCol umna[ 1] : = 100; / / Ancho de l a col umna 1  AnchoCol umna[ 2] : = 200; / / Ancho de l a col umna 2  AnchoCol umna[ 3] : = 300; / / Ancho de l a col umna 3

  / / Leemos el t exto de l a pr i mera col umna  i Pos : = Pos( ' ; ' , sTodo ) ;  sVal or : = Copy( sTodo, 1, i Pos - 1 ) ;

  f or i : = 0 t o 3 do  begi n  / / Di buj amos l a pr i mera col umna  r c. Lef t : = Rect . Lef t + AnchoCol umna[ i ] + 2;  r c. Ri ght : = Rect . Lef t + AnchoCol umna[ i +1] - 2;  r c. Top : = Rect . Top;  r c. Bot t om : = Rect . Bot t om;

  / / Escri bi mos el t ext o  Combobox. Canvas. TextRect ( r c, r c. Lef t , rc. Top, sVal or ) ;

  / / Di buj amos l as l í neas que separan l as col umnas  i f i < 3 t hen

  begi n  Combobox. Canvas. MoveTo( r c. Ri ght , r c. Top ) ;  Combobox. Canvas. Li neTo( r c. Ri ght , r c. Bot t om ) ;  end;

  / / Leemos el t ext o de l a segunda col umna  sTodo : = Copy( sTodo, i Pos + 1, Lengt h( sTodo ) - i Pos ) ;  i Pos : = Pos( ' ; ' , sTodo ) ;  sVal or : = Copy( sTodo, 1, i Pos - 1 ) ;  end;end;

Modificando el bucle y el array de enteros AnchoColumna podemos crear elnúmero de columnas que queramos. Ahora sólo hay que meter los items en elComboBox separados por punto y coma:

Page 102: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 102/306

  wi t h Combobox. I t ems do  begi n  Add( ' J OSE; SANCHEZ; GARCI A; ' ) ;  Add( ' MARI A; PEREZ; GOMEZ; ' ) ;  Add( ' ANDRES; MARTI NEZ; RUI Z; ' ) ;

  end;

Por último hay que decirle al ComboBox que la rutina de pintar los items correpor nuestra cuenta:

pr ocedur e TFor mul ar i o. For mCr eat e(Sender: TObj ect ) ;begi n  / / Le deci mos al ComboBox que l o vamos a pi ntar nosot r os  Combobox. Styl e : = csOwnerDr awFi xed;end;

Pruebas realizadas en Delphi 7.

Conversiones entre unidades de medidaDelphi incorpora una librería interesante encargada de realizar conversionesentre unidades de tiempo, volumen, distancia, etc. Para ello hay que añadirlas unidades ConvUtils y StdConvs:

uses  Wi ndows, Messages, . . . , ConvUt i l s, St dConvs;

La función encargada de realizar conversiones es la siguiente:Convert( ValorAConvertir: Double; DesdeUnidad, HastaUnidad:TConvType): Double;

Veamos algunos ejemplos mostrando el resultado en un campo Memo.Para convertir de millas a kilómetros:

var dMi l l as, dKi l omet r os: Doubl e;begi n  dMi l l as : = 15;  dKi l ometr os : = Conver t ( dMi l l as, duMi l es, duKi l omet ers ) ;

  Memo. Li nes. Add( Format( ' %8. 4f Mi l l as = %8. 4f Ki l omet r os' , [ dMi l l as,dKi l omet r os] ) ) ;

Esta otra convierte de pulgadas de área a centímetros de area:

var dPul gadas, dCent i met r os: Doubl e;begi n  dPul gadas : = 21;  dCent i met r os : = Conver t ( dPul gadas, auSquar eI nches,auSquareCent i meter s ) ;  Memo. Li nes. Add( Format ( ' %8. 4f Pul gadas de área = %8. 4f Cent í met r osde ár ea' , [ dPul gadas, dCent i met r os] ) ) ;

Y si queremos convertir libras en kilogramos:

Page 103: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 103/306

var dLi br as, dKi l os: Doubl e;begi n  dLi br as : = 60;  dKi l os : = Conver t ( dLi br as, muPounds, muKi l ogr ams ) ;  Memo. Li nes. Add( Format( ' %8. 4f Li br as = %8. 4f Ki l os' , [ dLi br as,

dKi l os ] ) ) ;

También podemos convertir unidades de temperatura:

var dFahr enhei t , dCel si us: Doubl e;begi n  dFahr enhei t : = 84;  dCel si us : = Conver t ( dFahr enhei t , t uFahr enhei t , t uCel si us ) ;  Memo. Li nes. Add( For mat ( ' %8. 4f º Fahr enhei t = %8. 4f º Cel si us' ,[ dFahr enhei t , dCel si us] ) ) ;

Así como conversión entre unidades de volumen:

var dMet r osCubi cos, dLi t r os: Doubl e;begi n  dMet r osCubi cos : = 43;  dLi t r os : = Conver t ( dMet r osCubi cos, vuCubi cMet ers, vuLi t er s ) ;  Memo. Li nes. Add( For mat ( ' %8. 4f Met r os cúbi cos = %8. 4f º Li t r os' ,[ dMet r osCubi cos, dLi t r os] ) ) ;

Ahora vamos a ver todos los tipos de conversión según las unidades demedida.Para convertir entre unidades de área tenemos:

  auSquar eMi l l i met ers

  auSquar eCent i met er s  auSquar eDeci met er s  auSquar eMet er s  auSquar eDecamet er s  auSquar eHect omet er s  auSquareKi l ometer s  auSquar eI nches  auSquar eFeet  auSquar eYards  auSquareMi l es  auAcr es  auCent ar es  auAr es

  auHect ar es  auSquar eRods

Convertir entre unidades de distancia:

  duMi cr omi cr ons  duAngst r oms  duMi l l i mi crons  duMi cr ons  duMi l l i met er s  duCent i met er s  duDeci met er s  duMet er s  duDecamet er s  duHect omet er s

Page 104: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 104/306

  duKi l omet ers  duMegamet er s  duGi gamet er s  duI nches  duFeet  duYards

  duMi l es  duNaut i cal Mi l es  duAst r onomi cal Uni t s  duLi ght Years  duPar secs  duCubi t s  duFat homs  duFur l ongs  duHands  duPaces  duRods  duChai ns  duLi nks  duPi cas  duPoi nt s

Convertir entre unidades de masa:

  muNanogr ams  muMi cr ogr ams  muMi l l i gr ams  muCent i gr ams  muDeci gr ams  muGrams  muDecagr ams  muHect ogr ams

  muKi l ograms  muMet r i cTons  muDrams  muGr ai ns  muLongTons  muTons  muOunces  muPounds  muSt ones

Convertir entre unidades de temperatura:

  t uCel si us

  t uKel vi n  t uFahr enhei t  t uRanki ne  t uReamur

Convertir entre unidades de tiempo:

  t uMi l l i Seconds  t uSeconds  t uMi nut es  t uHour s  t uDays

  t uWeeks  t uFor t ni ght s  t uMont hs

Page 105: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 105/306

  t uYears  t uDecades  t uCent ur i es  t uMi l l enni a  t uDat eTi me  tuJ ul i anDat e

  t uModi f i edJ ul i anDat e

Convertir entre unidades de volumen:

  vuCubi cMi l l i met ers  vuCubi cCent i meter s  vuCubi cDeci met er s  vuCubi cMet er s  vuCubi cDecamet er s  vuCubi cHect omet er s  vuCubi cKi l ometers  vuCubi cI nches  vuCubi cFeet

  vuCubi cYards  vuCubi cMi l es  vuMi l l i Li t ers  vuCent i Li t er s  vuDeci Li t er s  vuLi t er s  vuDecaLi t ers  vuHect oLi t er s  vuKi l oLi t ers  vuAcr eFeet  vuAcr eI nches  vuCor ds  vuCor dFeet

  vuDeci st eres  vuSt eres  vuDecast eres  vuFl ui dGal l ons  vuFl ui dQuar t s  vuFl ui dPi nt s  vuFl ui dCups  vuFl ui dGi l l s  vuFl ui dOunces  vuFl ui dTabl espoons  vuFl ui dTeaspoons  vuDr yGal l ons  vuDr yQuar t s

  vuDr yPi nt s  vuDr yPecks  vuDr yBucket s

  vuDr yBushel s  vuUKGal l ons  vuUKPot t l es  vuUKQuar t s  vuUKPi nt s  vuUKGi l l s  vuUKOunces  vuUKPecks  vuUKBucket s  vuUKBushel s

Pruebas realizadas en Delphi 7.

Page 106: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 106/306

Tipos de punteroPointer:

- Es un tipo general de puntero hacia cualquier objeto o variable en memoria.- Al no ser de ningún tipo suele ser bastante peligroso si provocadesbordamientos de memoria.

PAnsiChar:

- Es un tipo de puntero hacia un valor AnsiChar.- También puede ser utilizado para apuntar a caracteres dentro de unacadena AnsiString.- Al igual que otros punteros permite la aritmética, es decir, los

procedimientos Inc y Dec pueden utilizarse para mover el puntero enmemoria.

PAnsiString:

- Apunta hacia una cadena AnsiString.- Debido a que AnsiString ya es un puntero hacia si misma, el punteroPAnsiString no suele utilizarse mucho.

PChar:

- Es un tipo de puntero hacia un valor Char.- Puede ser utilizado para apuntar a caracteres dentro de una cadena string.- Permite aritmética de punteros mediante los procedimientos Inc y Dec.- Suele utilizarse mucho para procesar cadenas de caracteres terminadas encero, tal como las utilizadas en el lenguaje C/C++.- Los caracteres Char son idénticos a los de las variables AnsiChar, siendo de8 bits de tamaño.

PCurrency:

- Apunta hacia un valor Currency.- Permite aritmética de punteros mediante los procedimientos Inc y Dec.

PDateTime:

- Apunta hacia un valor TDateTime.- Permite aritmética de punteros mediante los procedimientos Inc y Dec.

PExtended:

- Apunta hacia un valor Extended.

- Permite aritmética de punteros mediante los procedimientos Inc y Dec.

Page 107: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 107/306

PInt64:

- Apunta hacia un valor Int64.- Permite aritmética de punteros mediante los procedimientos Inc y Dec.

PShortString:

- Apunta hacia una cadena ShortString.- Debido a que las variables ShortString difieren de las variables string,para apuntar a una variable ShortString es necesario utilizar la función Addr.

PString:

- Apunta hacia una cadena String.- Al ser la cadena String un puntero en si misma no suele utilizarse muchoeste puntero.

PVariant:

- Apunta hacia un valor Variant.- Al ser Variant un tipo genérico y variable hay que extremar la precaución enel manejo de este puntero.

PWideChar:

- Apunta hacia un valor WideChar.

- Puede ser utilizado para apuntar a caracteres dentro de una cadenaWideString.- Permite aritmética de punteros mediante los procedimientos Inc y Dec.

PWideString:

- Apunta hacia una cadena WideString.- Al ser ya cadena WideString un puntero en si misma no suele utilizarsemucho.

Pruebas realizadas en Delphi 7.

Dando formato a los números realesLa unidad SysUtils dispone del tipo TFloatFormat siguiente:

t ype TFl oat For mat = ( f f General , f f Exponent , f f Fi xed, f f Number ,f f Cur r ency) ;

Este tipo es utilizado por las funciones CurrToStrF, FloatToStrF yFloatToText para dar formato a los números reales que pasen a formatotexto. Como hemos visto anteriormente, los posibles valores de TFloatFormatson:

Page 108: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 108/306

ffGeneral

Define el formato general de un número real acercando el valor resultantetanto como sea posible. Quita los ceros que se arrastran y la coma cuando sea

necesario. No muestra ningún separador de miles y utiliza el formatoexponencial cuando la mantisa es demasiado grande para el valor especificadosegún el formato. El formato de la coma es determinado por la variableDecimalSeparator.

Veamos un ejemplo mostrando el resultado en un campo Memo:

var r Cant i dad: Ext ended; / / Número r eal para hacer pr uebasbegi n  r Cant i dad : = 1234. 56;

  Memo. Li nes. Add( ' General 4, 0 = ' + Fl oat ToSt r F( r Cant i dad,

f f Gener al , 4, 0 ) ) ;  Memo. Li nes. Add( ' General 6, 0 = ' + Fl oat ToSt r F( r Cant i dad,f f Gener al , 6, 0 ) ) ;  Memo. Li nes. Add( ' General 6, 2 = ' + Fl oat ToSt r F( r Cant i dad,f f Gener al , 6, 2 ) ) ;  Memo. Li nes. Add( ' General 3, 2 = ' + Fl oat ToSt r F( r Cant i dad,f f Gener al , 3, 2 ) ) ;

El resultado que nos muestra es el siguiente:

  Gener al 4, 0 = 1235  General 6, 0 = 1234, 56  General 6, 2 = 1234, 56  General 3, 2 = 1, 23E03

Como vemos la función FloatToStrF toma los siguientes parámetros:

function FloatToStrF(Value: Extended; Format: TFloatFormat; Precision,Digits: Integer): string; overload;

Value es el número real que vamos a pasar a texto.Format es el tipo de formato en coma flotante que vamos a utilizar (en estecaso ffGeneral).

Precision es el número máximo de dígitos enteros que soporta el formato.

Digits es el número máximo de decimales que soporta el formato.

En el caso anterior cuando forzamos a mostrar menos dígitos de precisión delo que el número real tiene lo que hace la función es recortar o biendecimales o si la cantidad entera es superior al formato lo pasa al formatoexponencial.

Veamos el resto de formatos:

ffExponent

Muestra el número real en formato científico cuyo exponente viene

Page 109: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 109/306

representado con la letra E con base 10. Por ejemplo E+15 significa 1015. Elcarácter de la coma es representado por la variable DecimalSeparator.

Aquí vemos como queda el mismo número en distintos formatosexponenciales:

  Memo. Li nes. Add( ' Exponenci al 4, 0 = ' + Fl oat ToSt r F( r Cant i dad,f f Exponent , 4, 0 ) ) ;  Memo. Li nes. Add( ' Exponenci al 6, 0 = ' + Fl oat ToSt r F( r Cant i dad,f f Exponent , 6, 0 ) ) ;  Memo. Li nes. Add( ' Exponenci al 6, 2 = ' + Fl oat ToSt r F( r Cant i dad,f f Exponent , 6, 2 ) ) ;  Memo. Li nes. Add( ' Exponenci al 3, 2 = ' + Fl oat ToSt r F( r Cant i dad,f f Exponent , 3, 2 ) ) ;

cuyo resultado es:

  Exponenci al 4, 0 = 1, 235E+3

  Exponenci al 6, 0 = 1, 23456E+3  Exponenci al 6, 2 = 1, 23456E+03  Exponenci al 3, 2 = 1, 23E+03

El formato ffFixed

Este formato no utiliza ningún separador de unidades de millar. Al igual quelos formatos anteriores si la precisión del número real es superior al formatoentonces muestra el resultado en notación científica. Quedaría de la siguientemanera:

  Memo. Li nes. Add( ' Fi j o 4, 0 = ' + Fl oat ToSt r F( r Cant i dad, f f Fi xed, 4,0 ) ) ;  Memo. Li nes. Add( ' Fi j o 6, 0 = ' + Fl oat ToSt r F( r Cant i dad, f f Fi xed, 6,0 ) ) ;  Memo. Li nes. Add( ' Fi j o 6, 2 = ' + Fl oat ToSt r F( r Cant i dad, f f Fi xed, 6,2 ) ) ;  Memo. Li nes. Add( ' Fi j o 3, 2 = ' + Fl oat ToSt r F( r Cant i dad, f f Fi xed, 3,2 ) ) ;

dando los valores:

  Fi j o 4, 0 = 1235  Fi j o 6, 0 = 1235

  Fi j o 6, 2 = 1234, 56  Fi j o 3, 2 = 1, 23E03

El formato ffNumber

Es igual al formato ffFixed salvo que también incluye el separador deunidades de millar, el cual viene representado por la variableThousandSeparator. En nuestro ejemplo:

  Memo. Li nes. Add( ' Número 4, 0 = ' + Fl oat ToSt r F( r Cant i dad, f f Number ,4, 0 ) ) ;  Memo. Li nes. Add( ' Número 6, 0 = ' + Fl oat ToSt r F( r Cant i dad, f f Number ,

6, 0 ) ) ;  Memo. Li nes. Add( ' Número 6, 2 = ' + Fl oat ToSt r F( r Cant i dad, f f Number ,

Page 110: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 110/306

6, 2 ) ) ;  Memo. Li nes. Add( ' Número 3, 2 = ' + Fl oat ToSt r F( r Cant i dad, f f Number ,3, 2 ) ) ;

mostraría:

  Númer o 4, 0 = 1. 235  Númer o 6, 0 = 1. 235  Númer o 6, 2 = 1. 234, 56  Númer o 3, 2 = 1, 23E03

El formato ffCurrency

Es similar al formato ffNumber pero con un símbolo de secuencia agregado,según se haya definido en la variable CurrencyString. Este formato tambiénesta influenciado por las variables CurrencyFloat y NegCurrFloat. Sigamos elejemplo:

  Memo. Li nes. Add( ' Moneda 4, 0 = ' + Fl oat ToSt r F( r Cant i dad,f f Cur r ency, 4, 0 ) ) ;  Memo. Li nes. Add( ' Moneda 6, 0 = ' + Fl oat ToSt r F( r Cant i dad,f f Cur r ency, 6, 0 ) ) ;  Memo. Li nes. Add( ' Moneda 6, 2 = ' + Fl oat ToSt r F( r Cant i dad,f f Cur r ency, 6, 2 ) ) ;  Memo. Li nes. Add( ' Moneda 3, 2 = ' + Fl oat ToSt r F( r Cant i dad,f f Cur r ency, 3, 2 ) ) ;

Da como resultado:

  Moneda 4, 0 = 1. 235 €

  Moneda 6, 0 = 1. 235 €  Moneda 6, 2 = 1. 234, 56 €  Moneda 3, 2 = 1, 23E03

según las variables mencionadas anteriormente que recogen el formatomoneda por defecto configurado en Windows.

Pruebas realizadas en Delphi 7.

Conversiones entre tipos numéricosHasta ahora hemos visto como pasar cualquier tipo de variable a string o alrevés. Ahora vamos a ver funciones para convertir valores de un tipo numéricoa otro.

PASAR NÚMEROS REALES A NÚMEROS ENTEROS

function Trunc( X: Extended ): Int64;

Esta función convierte un tipo Extended al tipo entero Int64 truncando losdecimales (sin redondeos). Por ejemplo:

Page 111: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 111/306

 Tr unc( 1234. 5678 ) devuel ve 1234

function Round( X: Extended ): Int64;

Esta función convierte un tipoExtended

 al tipo enteroInt64

 peroredondeando la parte entera según la parte decimal. Por ejemplo:

Round( 1234. 5678 ) devuel ve 1235Round( 1234. 4678 ) devuel ve 1234

function Ceil( const X: Extended ): Integer;

Convierte un valor Extended en Integer redondeando al número entero queeste mas próximo hacia arriba. Por ejemplo:

Cei l ( 1234. 5678 ) devuel ve 1235

Cei l ( 1234. 4678 ) devuel ve 1235Cei l ( 1235. 5678 ) devuel ve 1236Cei l ( 1235. 4678 ) devuel ve 1236

function Floor( const X: Extended ): Integer;

Convierte un valor Extended en Integer redondeando al número entero queeste más próximo hacia abajo. Por ejemplo:

Fl oor ( 1234. 5678 ) devuel ve 1234Fl oor ( 1234. 4678 ) devuel ve 1234Fl oor ( 1235. 5678 ) devuel ve 1235

Fl oor ( 1235. 4678 ) devuel ve 1235

También tenemos una función un poco rara para extraer el exponente y lamantisa de un número real Extended:

procedure FloatToDecimal( var DecVal: TFloatRec; const Value; ValueType:TFloatValue; Precision, Decimals: Integer );

Convierte un número Extented o Currency a formato decimal guardándolo enla siguiente estructura de datos:

t ype TFl oat Rec = r ecor d  Exponent : Smal l i nt ;  Negat i ve: Bool ean;  Di gi t s: ar r ay[ 0. . 20] of Char ;end;

Por ejemplo:

var  e: Ext ended;  Regi st r oFl oat : TFl oat Rec;begi n  e : = 1234. 5678;

  Fl oat ToDeci mal ( Regi st r oFl oat , e, f vExtended, 10, 4 ) ;end;

Page 112: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 112/306

Al ejecutarlo da los siguientes valores:

Regi st r oFl oat . Di gi t s = 12345678Regi st r oFl oat . Negati ve = Fal seRegi st r oFl oat . Exponent = 4

TRUNCANDO NÚMEROS REALES

function Int( X: Extended ): Extended;

Aunque esta función devuelve sólo la parte entera de un valor Extended, elvalor devuelto sigue siendo Extended. El resultado es similar a la funciónTrunc (sin redondeos). Por ejemplo:

I nt ( 1234. 5678 ) devuel ve 1234I nt ( 1234. 4678 ) devuel ve 1234

function Frac( X: Extended ): Extended;

Devuelve la parte fraccionaria de un valor Extended. Por ejemplo:

Fr ac( 1234. 4678 ) devuel ve 0. 5678

CONVIRTIENDO VALORES EXTENDED Y CURRENCY

Como vimos anteriormente, el tipo Currency no es un auténtico formato enpunto flotante sino un entero de punto fijo con 4 decimales como máximo, es

decir, los valores Currency se almacenan como un entero multiplicado por10000. Veamos de que funciones disponemos para convertir de un tipo a otro:

function FloatToCurr( const Value: Extended ): Currency;

Esta función convierte de tipo Extended a Currency. Por ejemplo:

Fl oat ToCurr ( 1234. 5678 ) devuel ve 1234. 5678 ( de t i po Cur r ency)Fl oat ToCurr ( 123. 456789 ) devuel ve 123. 4568 ( de t i po Cur r ency yperdemos 2 deci mal es)

function TryFloatToCurr( const Value: Extended; out AResult: Currency ):Boolean;

Esta función es similar a FloatToCurr pero sólo comprueba si se puedeconvertir a Currency. El procedimiento sería:

var  c: Cur r ency;begi n  i f Tr yFl oat ToCur r ( 123. 456789, c ) t hen  ShowMessage( Curr ToSt r ( c ) ) ;end;

CONVIRTIENDO VALORES ENTEROS A VALORES REALES

Page 113: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 113/306

Se puede convertir un valor entero a real convirtiendo la variabledirectamente:

var  i : I nteger ;  r : Real ;begi n  i : = 1234;  r : = i ; / / real i za el cast i ngautomát i cament e  ShowMessage( Fl oat ToSt r ( r ) ) ; / / muest r a 1234end;

Esto no ocurre en todos los casos pero es cuestión de probar (aunque esrecomendable utilizar los tipos Variant para esa labor).

Pruebas realizadas en Delphi 7.

Creando un cliente de chat IRC con Indy (I)Aunque MSN es el rey de la comunicación instantánea aún siguen muy vivos losservidores de chat IRC tales como irc-hispano.

IRC define un protocolo de comunicaciones entre clientes y servidorespermitiendo incluso el envío de archivos por conexión directa. De todos esconocido el potente programa para Windows llamado MIRC creado hace años yque aún sigue siendo el cliente más utilizado para el IRC debido a susmúltiples extensiones.

En nuestro caso vamos a ver como utilizar el componente de la clase TIdIRC elcual esta situado en la pestaña de componentes Indy.

CONECTANDO CON EL SERVIDOR 

Antes de comenzar a chatear con el servidor hay que establecer una conexióncon el mismo utilizando un apodo (nick) y una contraseña en el caso de quesea necesaria (la mayoría de los servidores IRC son públicos).

Supongamos que tenemos en el formulario principal de nuestra aplicación uncomponente IdIRC que vamos a llamar IRC. Para conectar con el servidor hayque hacer lo siguiente:

I RC. Ni ck : = ' j uani t o33487' ;I RC. Al t Ni ck : = ' j uani t o33488' ;I RC. Username : = ' j uani t o33487' ;I RC. Real Name : = ' j uan' ;I RC. Passwor d : = ' ' ;I RC. Host : = ' i rc . i r c- hi spano. or g' ;

t r y  I RC. Connect ;except

Page 114: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 114/306

  Appl i cat i on. MessageBox( ' No se ha podi do conect ar con el servi dor. ' ,  ' Err or de conexi ón' , MB_I CONSTOP ) ;end;

Estos son los parámetros para conectar:

Ni ck - > apodo por el que nos conocer án l os demás usuar i osAl t Ni ck - > si el ni ck que hemos ut i l i zado est á ocupado por otr ousuar i o coger á est e ot r o ni ckUser name - > Nombr e del usuar i o para el ser vi dor ( da l o mi smo si noest amos r egi st r ados)Real name - > Nombre r eal del usuar i oPassword - > Cl ave de acceso para usuar i os regi st r adosHost - > Di r ecci ón I P del ser vi dor

Cualquier persona puede conectarse a un servidor de chat dando sólo elnombre del usuario sin contraseña. Pero si queremos que nadie utilice nuestronick nos podemos registrar gratuitamente (en la mayoría de los casos) en el

servidor con un usuario y password obteniendo además algunas característicasadicionales como entrar a salas de chat restringidas o tener nuestro propioservidor de mensajes y correo online 24 horas al día.

Una vez conectados al servidor toca entrar en una sala de chat. Pero, ¿de quesalas de chat dispone el servidor? Para ello utilizamos el comando:

I RC. Raw( ' LI ST' ) ;

El método Raw permite mandar cualquier tipo de comando al servidor. Sesuele utilizar cuando el componente IRC no contiene métodos para realizar

tareas específicas. Los servidores IRC trabajan enviando y recibiendomensajes de texto continuamente. Para los que les interese saber comofunciona por dentro el protocolo IRC disponen de este documento RFC:

ht t p: / / www. r f c- es. or g/ r f c/ r f c1459- es. t xt

Pese a los comandos del IRC estándar algunos servidores disponen decomandos propios para tareas específicas. Para esos casos utilizaremos elmétodo Raw.

Hay que tener mucho cuidado cuando se llama al comando LIST ya que hay

servidores como los irc-hispano que disponen de miles de canales lo cualpuede hacer que el tiempo en sacar el listado llegue a ser desesperante. Porello sería deseable que tanto la función de conectar a un servidor IRC como lade listar los canales estuviera dentro de un hilo de ejecución, ya que hastaque no conecta da la sensación de que nuestro programa esta colgado.

Después de ejecutar dicho comando tenemos que programar el evento OnListpara recoger la información del listado de canales. En este caso para volcarlos canales en un componente ListBox llamado Canales hacemos lo siguiente:

pr ocedur e TFor mul ari o. I RCLi st ( Sender: TObj ect ; AChans: TSt r i ngLi st ;

  APosi t i on: I nt eger ; ALast : Bool ean ) ;begi n

Page 115: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 115/306

  Canal es. I t ems : = AChans;end;

Con esto ya tenemos la lista de canales que dispone el servidor así como elnúmero de usuarios que hay conectados por canal y su descripción. Porejemplo:

#ami st ades 20 Busca nuevos ami g@s#amor 50 Conoce a t u medi a nar anj a#sexo 80 No t e cor t es un pel o#musi ca 34 Compart e l a pasi ón por t us ar t i st as pr ef er i dos. . . .

En un servidor de IRC los canales comienzan con el carácter # seguido de sunombre sin espacios.

En el próximo artículo veremos como entrar a los canales de chat y hablar con

otros usuarios.Pruebas realizadas en Delphi 7.

Creando un cliente de chat IRC con Indy (II)Una vez establecida la conexión con el servidor y sabiendo de que salas dechat dispone, vamos a entrar a un canal a chatear.

ENTRANDO A UN CANAL

Para entrar a un canal se utiliza el método Join:

I RC. J oi n( ' #ami st ades' ) ;

Esto viene a ser lo mismo que hacer:

I RC. Raw( ' j oi n #ami st ades' ) ;

Podemos entrar a tantos canales como deseemos pero para hacer un buencliente de chat hay que crear un formulario por canal, ya que el protocolo IRCnos permite chatear en múltiples canales simultaneamente.

Una vez se ha enviado el comando JOIN el servidor nos devuelve la lista deusuarios que hay en ese canal. Para recoger dicha lista hay que utilizar el

Page 116: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 116/306

evento OnNames del componente IdIRC, donde volcaremos el contenido de lamisma en un ListBox que vamos a llamar Usuarios:

pr ocedur e TFor mul ar i o. I RCNames( Sender : TObj ect ; AUser s: TI dI RCUser s;AChannel : TI dI RCChannel ) ;var i : I nt eger ;

begi n  i f Assi gned( AUsers ) t hen  begi n  Usuar i os. Cl ear ;

  f or i : = 0 t o AUsers . Count - 1 do  Usuar i os. I t ems. Add( AUser s. I t ems[ i ] . Ni ck ) ;  end;end;

Es conveniente que el ListBox este ordenado alfabéticamente activando lapropiedad Sorted para no volvernos locos buscando los usuarios por su

nombre.

LEYENDO LOS MENSAJES DEL SERVIDOR 

Una vez conectados a un canal, el servidor nos mandará dos tipos demensajes: lo que hablan todos los usuarios en el canal y lo que un usuario enconcreto nos esta diciendo. ¿Cómo distinguimos cada cual? Pues para ello elevento OnMessaje nos dice el usuario y canal que nos manda el mensaje:

pr ocedur e TFormul ar i o. I RCMessage( Sender : TObj ect ; AUser : TI dI RCUser ;  AChannel : TI dI RCChannel ; Cont ent :st r i ng ) ;begi n  i f Assi gned( AUser ) t hen  begi n  i f Assi gned( AChannel ) t hen  Canal . Li nes. Add( AUser . Ni ck + ' > ' + Cont ent )  el se  Pr i vado. Li nes. Add( AUser. Ni ck + ' > ' + Cont ent ) ;  end;end;

Como se puede apreciar primero comprobamos si existe el usuario (por siacaso es un mensaje del sistema) y luego si tiene canal metemos el mensaje

por el mismo (poniendo el nick delante como MIRC) y si no es asi lo mandamosa una conversación privada. En este caso he enviado el texto de laconversación a un componente RichEdit.

Sería interesante que nuestro cliente de chat dispusiera de un formulario porcada conversación privada. Por ello, sería recomendable que nuestroprograma de chat utilizara ventanas MDI para permitir el control absolutocada una de las salas en las que estamos así como cada una de lasconversaciones privadas. En otro artículo escribiré como crear aplicacionesMDI.

Otra característica que lo haría más profesional sería dibujar el nick y elmensaje del usuario con colores distintos como vimos anteriormente en el

Page 117: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 117/306

artículo dedicado al componente RichEdit. Y si además damos al usuario laposibilidad de seleccionar los colores de fondo, texto, nicks y tipo de fuente,el programa se aproximaría al famoso cliente MIRC.

ENVIANDO MENSAJES AL SERVIDOR 

Se pueden enviar dos tipos de mensajes: a un canal donde estamos (para elpúblico en general ) o a un usuario en concreto. El método say delcomponente IdIRC cumple dicho cometido.

Si quiero enviar un mensaje al canal amistades:

I RC. Say( ' #ami st ades' , ' hol a a t odos' ) ;

Y si es para un usuario con el que estamos chateando:

I RC. Say( ' #mar i a' , ' hol a guapa, que t al ?' ) ;

Al fin y al cabo para el servidor todo son canales, tanto canales públicos comousuarios particulares. Tenemos que ser nosotros los que debemos procesar dedonde viene el mensaje y a donde deseamos enviarlo. De ahí la necesidad decrear un formulario por canal y otro por conversación privada.

ABANDONANDO UN CANAL Y UN SERVIDOR 

Para abandonar un canal se utiliza el método Part:

I RC. Par t ( ' #ami st ades' ) ;

Si se nos olvida hacer esto y seguimos abriendo canales al final gastaremosmucho ancho de banda, ya que el servidor nos manda todos los mensajespublicos de cada uno de los canales abiertos.

Si queremos desconectar del servidor se utiliza el comando:

I RC. Di sconnect ;

Esto cancelará la conexión con el servidor cerrando todos los mensajespúblicos y privados del mismo. En nuestro programa deberemos cerrar todaslas ventanas de conversación abiertas.

Todavía quedan algunos puntos importantes para terminar de crear un clientede chat, tales como saber si un usuario entra o abandona el canal dondeestamos.

Pruebas realizadas en Delphi 7.

Creando un cliente de chat IRC con Indy

(III)

Page 118: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 118/306

Después de haber visto la parte básica del componente IdIRC pasemos a veralgunas cosas que hay que controlar en un cliente de chat.

QUIEN SALE Y QUIEN ENTRA A UN CANAL

Mientras permanezcamos en un canal entrarán y saldrán usuariosconstantemente, lo cual nos obliga a refrescar la lista de usuarios y notificarlos cambios en la misma ventana del canal:

Ha ent r ado el usuar i o l ol a33Ha sal i do el usuar i o car l os21

Para controlar los usuarios que entran a un canal utilizaremos el eventoOnJoin:

pr ocedur e TFor mul ar i o. I RCJ oi n( Sender: TObj ect; AUser: TI dI RCUser ;AChannel : TI dI RCChannel ) ;begi n  i f Assi gned( AChannel ) and Assi gned( AUser ) t hen  i f AChannel . Name = Canal Act ual . Text t hen  begi n  Canal . Li nes. Add( ' Ha ent r ado el usuar i o ' + AUser . Ni ck ) ;

  i f Usuar i os. I t ems. I ndexOf ( AUser. Ni ck ) = - 1 t hen  Usuar i os. I t ems. Add( AUser. Ni ck ) ;  end;end;

Después de informar en el canal actual que ha aparecido un nuevo usuariotambién lo hemos dado de alta en la lista de usuarios, controlando que no serepitan, ya que a veces suele coincidir que se ejecuta el comando NAMES delservidor y a la vez entra un usuario nuevo.

Y cuando un usuario abandona el canal entonces utilizamos el evento OnPart:

pr ocedur e TFor mul ar i o. I RCPar t ( Sender: TObj ect; AUser: TI dI RCUser ;AChannel : TI dI RCChannel ) ;var i Usuar i o: I nt eger ;begi n  i f Assi gned( AChannel ) and Assi gned( AUser ) t hen  i f AChannel . Name = Canal Act ual . Text t hen  begi n  Canal . Li nes. Add( ' Ha sal i do el usuar i o ' + AUser . Ni ck ) ;  i Usuar i o : = Usuar i os. I t ems. I ndexOf ( AUser. Ni ck ) ;

  i f i Usuar i o > - 1 t hen  Usuar i os. I t ems. Del et e( i Usuar i o ) ;  end;end;

La única dificultad que hay que controlar es que si tenemos varias ventanas(una por canal) hay que llevar las notificaciones a la ventana correspondiente.

Los canales de chat tienen dos clases de usuarios: operadores y usuarios

Page 119: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 119/306

normales. Los operadores son usuarios que tienen permisos especiales:pueden hacer operadores a usuarios normales o echar a un usuario de un canalsi esta incumpliendo las normas del mismo.

Cuando un operador echa fuera a un usuario entonces nuestro componente

IdIRC provoca el evento OnKick:pr ocedur e TFor mul ar i o. I RCKi ck( Sender: TObj ect; AUser, AVi ct i m:

 TI dI RCUser ; AChannel : TI dI RCChannel ) ;var i Usuar i o: I nt eger ;begi n  i f Assi gned( AChannel ) and Assi gned( AUser ) t hen  i f AChannel . Name = Canal Act ual . Text t hen  begi n  Canal . Li nes. Add( ' El usuari o ' + Auser. Ni ck + ' ha expul sado ausuar i o ' + AVi ct i m. Ni ck ) ;  i Usuar i o : = Usuar i os. I t ems. I ndexOf ( AUser. Ni ck ) ;

  i f i Usuar i o > - 1 t hen  Usuar i os. I t ems. Del et e( i Usuar i o ) ;  end;end;

Este evento nos informa del nombre del operador, el de la victima y del canalde donde ha sido expulsado.

Y por último tenemos el problema de que un usuario puede cambiarse elapodo (nick) en cualquier momento. Para ello utilizaremos el eventoOnNickChange para notificarlo:

pr ocedur e TFormul ar i o. I RCNi ckChange( Sender: TObj ect ; AUser : TI dI RCUser ; ANewNi ck: St r i ng ) ;var i Usuar i o: I nt eger ;begi n  Canal . Li nes. Add( ' El usuari o ' + AUser. Ni ck + ' ahor a se l l ama ' +ANewNi ck ) ;  i Usuar i o : = Usuari os. I t ems. I ndexOf ( AUser . Ni ck ) ;

  i f i Usuar i o > - 1 t hen  Usuar i os. I t ems[ i Usuar i o] : = ANewNi ck;end;

Aparte de notificarlo le hemos cambiado el nombre en nuestra lista deusuarios.

INTERCEPTANDO LOS MENSAJES DEL SISTEMA

A pesar de todos los eventos de los que dispone el componente de la claseTIdIRC también podemos interceptar a pelo los mensajes del sistema a travésdel evento OnSystem, el cual nos dice el usuario, el código de comando y elcontenido del mensaje del sistema. Por ejemplo estos son algunos códigos decomando del servidor:

353 - > Comi enzo del comando NAMES366 - > Fi n del comando NAMES

Page 120: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 120/306

376 - > Mensaj e del dí aetc.

Para más información hay que ver el documento RFC perteneciente alprotocolo IRC que mencione en el artículo anterior. Por ejemplo, paraaveriguar los datos sobre un usuario en concreto hay que mandarle al servidorel comando:

WHOIS maria

Y el servidor devolverá lo siguiente:

319 - WHOI S - mar i a i s on #ami st ades312 - WHOI S - mar i a i s usi ng j upi t er2. i r c- hi spano. or g Ser vi dor I RC deLLei da Net works318 - WHOI S - mar i a : End of / WHOI S l i st

Nos esta diciendo que maria está en el canal #amistades y que está conectadautilizando el servidor jupiter2. Si estuviera conectada a más canales tambiénlo diría (así sabemos también sus otras aficiones).

¿Cómo nos comemos todo eso? Pues supongamos que si yo hago doble clicsobre un usario quiero que me devuelva información sobre el mismo en uncampo Memo cuyo nombre es DatosUsuario. Lo primero sería procesar elevento OnDblClick en la lista de usuarios:

pr ocedur e TFor mul ar i o. Usuari osDbl Cl i ck( Sender: TObj ect ) ;begi n  i f Usuari os. I t emI ndex > - 1 t hen  I RC. Raw( ' WHOI S ' + Usuar i os. I t ems[ Usuar i os. I t emI ndex] ) ;end;

Y ahora en el evento OnSystem del componente IdIRC esperamos a que nosllegue la información:

pr ocedur e TFor mul ar i o. I RCSyst em( Sender: TObj ect ; AUser : TI dI RCUser ;ACmdCode: I nteger ; ACommand, ACont ent : st r i ng ) ;begi n  case ACmdCode of   312, 318, 319: DatosUsuar i o. Li nes. Add( ACont ent ) ;  end;

end;

Así, estudiando un poco el protocolo IRC y sabiendo algunos comandospodemos hacer más cosas de las que permite el componente IdIRC.

ENVIO Y RECEPCION DE ARCHIVOS POR ACCESO DIRECTO

El protocolo IRC permite establecer el envío y recepción de archivos conconexión punto a punto entre la IP del remitente y la IP del receptor. Esto yano se suele utilizar hoy en día por las siguiente razones:

- Debido a que casi todas las redes locales están detrás de un router se hacenecesario abrir y redireccionar puertos en el mismo, cosa que no todo el

Page 121: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 121/306

mundo sabe hacer.

- En cuanto intentemos establecer conexión por puertos que no sean losestandar los cortafuegos saltarán como liebres pidiendo permiso paraconectar. Los novatos siempre dirán NO por si acaso.

- Hay robots automáticos programados en MIRC mediante scripts que no parande realizar ataques continuamente a conexiones DCC.

Por ello no voy a comentar aquí como utilizarlo (para ello tenemosrapidshare, megaupload y demás hierbas que superan con creces este tipo deconexiones y además son más seguros).

Con esto finalizamos lo más importante del componente IdIRC de la paleta decomponentes Indy.

Pruebas realizadas en Delphi 7.

Creando un procesador de textos conRichEdit (I)Para crear un procesador de textos vamos a utilizar el componente RichEditque se encuentra en la pestaña Win32. Este componente tiene laparticularidad de poder definir distintos estilos de texto al contrario de uncomponente Memo cuya fuente es estática para todo el documento.

El componente de la clase TRichEdit hereda de TCustomMemo añadiendocaracterísticas tan interesantes como la de modificar el estilo de la fuente,colorear palabras o frases, etc.

Veamos como se podría crear un mini procesador de textos utilizando estecomponente. Como procesador de textos lo primero que debemos definir sonlas funciones para la creación, carga y grabación de documentos en disco.

La programación la voy a realizar sobre este formulario:

Page 122: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 122/306

Las opciones del menú son:

Ar chi vo - > Nuevo, Abr i r , Guardar , Guar dar como y Sal i r .Edi ci ón - > Cor t ar , Copi ar y Pegar .For mat o - > Fuent eAyuda - > Acer ca de. . .

GUARDANDO EL TEXTO EN UN ARCHIVO

Lo primero que vamos a contemplar es la grabación en un archivo de texto.Para ello vamos a insertar en el formulario un componente de claseTSaveDialog que se encuentra en la pestaña Dialogs y lo vamos a llamarGuadarTexto.

Entonces al pulsar la opción Archivo -> Guardar como del ménu ejecutamoslo siguiente:

pr ocedur e TFor mul ar i o. GuardarComoCl i ck( Sender : TObj ect ) ;begi n  i f Guar darTexto. Execut e t hen  begi n  Ri chEdi t . Li nes. SaveToFi l e( Guar dar Text o. Fi l eName ) ;  sAr chi vo : = Guar darText o. Fi l eName;  end;end;

La variable sArchivo se va a encargar de guardar la ruta y el nombre archivoque se guardó por primera vez, para que cuando seleccionemos la opciónguardar no haya que volver a decirle de nuevo el nombre. Esta variable lavamos a declarar en la sección privada de nuestro formulario:

  pr i vat e  { Pr i vat e decl ar at i ons }  sAr chi vo: St r i ng;

Page 123: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 123/306

Ahora tenemos que hacer que si el usuario pulsa la opción Guardar se guardeel archivo sin preguntarnos el nombre si ya lo tiene:

pr ocedur e TFor mul ar i o. Guar darCl i ck( Sender : TObj ect ) ;begi n  / / ¿No t i ene nombre?  i f sAr chi vo = ' ' t hen  GuardarComoCl i ck( Sel f )  el se  Ri chEdi t . Li nes. SaveToFi l e( sAr chi vo ) ;end;

Si no tuviera nombre es que es un archivo nuevo, con lo cual lo desviamos a laopción Guardar como.

CARGADO EL TEXTO DESDE UN ARCHIVO

Para cargar el texto tenemos que añadir al formulario el componenteTOpenDialog y lo llamamos CargarTexto. Y al pulsar en el menú la opciónArchivo -> Abrir se ejecutaría:

pr ocedur e TFor mul ari o. Abr i r Cl i ck( Sender : TObj ect ) ;begi n  i f Car gar Texto. Execut e t hen  begi n  Ri chEdi t . Li nes. LoadFromFi l e( Cargar Texto. Fi l eName ) ;  sAr chi vo : = CargarText o. Fi l eName;  end;end;

También guardamos en la variable sArchivo el nombre del archivo cargadopara su posterior utilización en la opción Guardar.

CREANDO UN NUEVO TEXTO

En nuestro programa vamos a hacer que si el usuario selecciona la opción delmenú Archivo -> Nuevo se elimine el texto del componente RichEdit. Lo quesi hay que controlar es que si había un texto anterior le pregunte al usuario sidesea guardarlo.

pr ocedur e TFormul ar i o. NuevoCl i ck( Sender : TObj ect ) ;begi n  / / ¿Hay al go i nt r oduci do?  i f Ri chEdi t . Text <> ' ' t hen  i f Appl i cat i on. MessageBox( ' ¿Desea guar dar el t ext o act ual ?' ,' At enci ón' ,  MB_I CONQUESTI ON OR MB_YESNO ) = I D_YESt hen  Guar dar Cl i ck( Sel f ) ;

  Ri chEdi t . Cl ear ;end;

CONTROLANDO LA EDICION DEL TEXTO

Page 124: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 124/306

Otra de las cosas básicas que debe llevar todo editor de texto son lasfunciones de cortar, copiar y pegar. Para ello vamos a implemantar primero laopción del menú Edición -> Cortar:

pr ocedur e TFor mul ar i o. Cor t arCl i ck( Sender: TObj ect ) ;begi n  Ri chEdi t . Cut ToCl i pboard;end;

Después hacemos la opción de Edición -> Copiar:

pr ocedur e TFor mul ar i o. Copi arCl i ck( Sender: TObj ect ) ;begi n  Ri chEdi t . CopyToCl i pboar d;end;

Y por último la opción de Edición -> Pegar:

pr ocedur e TFor mul ar i o. PegarCl i ck( Sender : TObj ect ) ;begi n  Ri chEdi t . Past eFr omCl i pboar d;end;

CAMBIANDO EL ESTILO DEL TEXTO

Una vez tenemos implementada la parte básica del editor de texto vamos adarle la posibilidad al usuario de que pueda cambiar la fuente, el estilo, elcolor, etc.

Para que el usuario pueda elegir la fuente tenemos que introducir en elformulario el componente de la clase TFontDialog situado en la pestañaDialogs. Le vamos a dar el nombre de ElegirFuente.

Ahora en la opción del menú Formato -> Fuente ejecutamos:

pr ocedur e TFor mul ar i o. Fuent eCl i ck( Sender: TObj ect ) ;begi n  i f El egi r Fuent e. Execut e t hen  wi t h Ri chEdi t , El egi r Fuent e do  begi n  Sel At t r i but es. Name : = Font . Name;

  Sel At t r i but es. Si ze : = Font . Si ze;  Sel At t r i but es. Col or : = Font . Col or ;  Sel At t r i but es. Pi t ch : = Font . Pi t ch;  Sel At t r i but es. St yl e : = Font . St yl e;  Sel At t r i but es. Hei ght : = Font . Hei ght ;  end;end;

¿Por qué no hemos hecho lo siguiente?

Ri chEdi t . Font : = El egi r Fuent e. Font ;

Si hago esto me cambia la fuente de todo el texto, incluso la que he escritoanteriormente y a mi lo que me interesa es modificar la fuente de lo que sevaya a escribir a partir de ahora. Para ello se utilizan las propiedades de

Page 125: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 125/306

SelAttributes las cuales se encargan de establecer el estilo del textoseleccionado, y en el caso de que no haya texto seleccionado se aplica adonde esté el cursor.

En el próximo artículo seguiremos ampliando nuestro pequeño procesador de

textos.Pruebas realizadas en Delphi 7

Creando un procesador de textos conRichEdit (II)Sigamos añadiendo características a nuestro pequeño editor de textos:

NEGRITA, CURSIVA Y SUBRAYADO

Vamos a definir la función de los típicos botones de negrita, cursiva ysubrayado que llevan los procesadores de texto. Comencemos con el botón denegrita:

pr ocedur e TFor mul ar i o. BNegr i t aCl i ck( Sender : TObj ect ) ;begi n  wi t h Ri chEdi t . Sel At t r i but es do  i f not ( fsBol d i n St yl e ) then  St yl e : = St yl e + [ f sBol d]  el se  St yl e : = St yl e - [ f sBol d] ;

  Ri chEdi t . Set Focus;end;

Hay que tener en cuenta que si el usuario pulsa una vez negrita y el texto no

estaba en negrita entonces se aplica dicho estilo, pero si ya lo estabaentonces hay que quitarlo.

Page 126: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 126/306

Lo mismo sería para el botón de cursiva:

pr ocedur e TFor mul ar i o. BCur si vaCl i ck( Sender : TObj ect ) ;begi n  wi t h Ri chEdi t . Sel At t r i but es do

  i f not ( f s I t al i c i n Sty l e ) then  St yl e : = St yl e + [ f s I t al i c ]  el se  Styl e : = Styl e - [ f s I t al i c] ;

  Ri chEdi t . Set Focus;end;

Y para el botón de subrayado:

pr ocedur e TFormul ar i o. BSubr ayadoCl i ck( Sender : TObj ect ) ;begi n  wi t h Ri chEdi t . Sel At t r i but es do  i f not ( f sUnder l i ne i n St yl e ) t hen  St yl e : = St yl e + [ f sUnder l i ne]  el se  St yl e : = St yl e - [ f sUnder l i ne] ;

  Ri chEdi t . Set Focus;end;

MOSTRANDO LA POSICION ACTUAL DEL CURSOR 

Una de las cosas que se agradece en todo procesador de textos es quemuestre en que fila y columna estoy situado. Para ello vamos a insertar en laparte inferior del formulario una barra de estado. El componente se llamaStatusBar y se encuentra en la pestaña Win32. Le vamos a poner el nombrede Estado y ponemos a True su propiedad SimplePanel.

Lo que haremos a continuación es un procedimiento que muestre la posiciónactual del cursor dentro del RichEdit en la barra de estado:

pr ocedur e TFor mul ar i o. Most r arPosi ci on;begi n  Est ado. Si mpl eText : = Format ( ' Fi l a: %d Col umna %d' ,  [ Ri chEdi t . Car et Pos. y+1, Ri chEdi t . Car et Pos. x+1] ) ;

end;

Este procedimiento hay que llamarlo cuando creamos el formulario:

pr ocedur e TFor mul ar i o. For mCr eat e( Sender : TObj ect ) ;begi n  Most r ar Posi ci on;end;

y en el evento OnSelectionChange del RichEdit:

pr ocedur e TFor mul ar i o. Ri chEdi t Sel ect i onChange( Sender: TObj ect ) ;begi n  Most r ar Posi ci on;end;

Page 127: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 127/306

Al ejecutar el programa veremos que el resultado queda más profesional (yapodían los de Microsoft ponerle esto al cutre Bloc de Notas).

DANDO FORMATO A LOS PARRAFOS DEL DOCUMENTO

Otra característica que vamos a implementar es la de alinear el párrafo actuala la izquierda, al centro y a la derecha. Para ello vamos a poner arriba tresbotones llamados BIzquierda, BDerecha y BCentro, cuyos eventos serían:

pr ocedur e TFor mul ar i o. BI zqui er daCl i ck( Sender : TObj ect ) ;begi n  Ri chEdi t . Par agr aph. Al i gnment : = t aLef t J ust i f y;end;

pr ocedur e TFor mul ar i o. BCent r oCl i ck( Sender : TObj ect ) ;begi n

  Ri chEdi t . Par agr aph. Al i gnment : = t aCent er;end;

pr ocedur e TFormul ar i o. BDerechaCl i ck( Sender: TObj ect ) ;begi n  Ri chEdi t . Par agr aph. Al i gnment : = t aRi ght J ust i f y;end;

Otra característica que se le puede añadir a un párrafo es la de añadir unpunto por la izquierda como hace Microsoft Word. Para ello vamos a añadir elbotón de nombre BPunto cuyo procedimiento es:

pr ocedur e TFor mul ar i o. BPunt oCl i ck( Sender: TObj ect ) ;begi n  wi t h Ri chEdi t . Par agr aph do  i f Numberi ng = nsNone t hen  Number i ng : = nsBul l et  el se  Number i ng : = nsNone;

  Ri chEdi t . Set Focus;end;

Si se pulsa una vez este botón añade un punto al párrafo y si se pulsa denuevo lo quita.

Al párrafo actual se le pueden modificar también las propiedades:

Fi r st I ndent - > Es el espaci o en pi xel s por l a i zqui er da que se l e da al a pr i mer a l í neaLef t I ndent - > Es el espaci o en pi xel s por l a i zqui erda que se l e da at odas l as l í neasRi ght I ndent - > Es el espaci o en pi xel s por l a derecha que se l e da at odas l as l í neas

LAS OTRAS PROPIEDADES DEL COMPONENTE RICHEDIT

Hay otras propiedades que nos permiten personalizar nuestro editor de textocomo son:

Page 128: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 128/306

PlainTex: Si se activa esta propiedad al guardar el archivo de texto a disco noalmacena las propiedades de color, fuente, etc. Se comporta como el Bloc deNotas.

SelLength: Es el número de caracteres seleccionados por el usuario.

SelStart: Es la posición en el texto donde comienza la selección.

SelText: Es el texto seleccionado.

Con esto finalizamos las características más importantes del componenteRichEdit.

Pruebas realizadas en Delphi 7.

Dibujando con la clase TCanvas (I)La unidad Graphics dispone de la clase TCanvas dedicada al dibujo de objetossobre la superficie de un control visual. Los controles estándar de Windowstales como Edit o Listbox no requieren canvas, ya que son dibujados por elsistema operativo.

Un objeto Canvas proporciona propiedades, métodos y eventos para realizar

tareas como pueden ser:- Escribir texto especificando fuente, color y estilo.

- Copiar imágenes de una superficie a otra.

- Dibujar líneas, rectángulos y circulos con distintos patrones de color y estilo.

- Leer y modificar cada pixel de la imagen dibujada.

COMO DIBUJAR UN RECTANGULO

Antes de dibujar una figura se pueden seleccionar los colores del margen y de

Page 129: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 129/306

fondo. Supongamos que quiero hacer un rectángulo con un marco de 3 pixelsde ancho y de color rojo. El fondo va a ser de color azul. Para ello nos vamosal evento OnPaint del formulario y escribimos lo siguiente:

pr ocedur e TFormul ar i o. For mPai nt ( Sender: TObj ect ) ;begi n  wi t h Canvas do  begi n  Pen. Col or : = cl Red;  Pen. Wi dth : = 3;  Br ush. Col or : = cl Bl ue;  Rectangl e( 10, 10, 110, 110 ) ;  end;end;

El resultado es el siguiente:

Hemos utilizado la propiedad Pen para seleccionar el color del marco y suancho. Con la propiedad Brush establecemos el color de fondo del rectángulo.Y mediante el procedimiento Rectangle hemos dibujado un rectángulo en laposición x = 10 e y = 10 teniendo un ancho y alto de 100x100. Elprocedimiento Rectangle tiene los siguientes parámetros:

pr ocedur e Rect angl e( X1, Y1, X2, Y2: I nt eger ) ;pr ocedur e Rect angl e( const Rect : TRect ) ;

X1, Y1 -> Son las coordenadas de la esquina superior izquierda del rectánguloX2, Y2 -> Son las coordenadas de la esquina inferior derecha del rectángulo

También se podría haber utilizado la estructura de datos TRect (rectángulo)para dibujar el rectángulo del siguiente modo:

var  R: TRect ;begi n  wi t h Canvas do  begi n  Pen. Col or : = cl Red;  Pen. Wi dth : = 3;  Br ush. Col or : = cl Bl ue;  R. Lef t : = 10;  R. Top : = 10;  R. Ri ght : = 110;  R. Bot t om : = 110;  Rect angl e( R ) ;

  end;end;

Page 130: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 130/306

La estructura TRect esta definida dentro de la unidad Type del siguientemodo:

 TRect = packed r ecor d  case I nt eger of   0: ( Lef t , Top, Ri ght , Bot t om: I nt eger ) ;  1: ( TopLef t , Bot t omRi ght : TPoi nt ) ;  end;

donde los valores Left y Top determinan la posición superior izquierda delrectángulo y los valores Right y Bottom la esquina inferior derecha.

También existe otro procedimiento para hacer rectángulos sólo con fondo (sinborde) utilizando el procedimiento:

pr ocedur e Fi l l Rect ( const Rect : TRect ) ;

Si lo hubiésemos utilizado este procedimiento sólo tendríamos un rectángulocon fondo azul. Lo único que tiene en cuenta es la propiedad Brush ignorandoel valor de Pen.

Para el caso anterior no es necesario utilizar la estructura TRect, pero si sevan a dibujar muchos rectángulos utilizando las mismas rutinas si esinteresante utilizarla, evitándonos el tener que declarar las variables x1, y1,x2, y2 para las coordenadas. La estructura TRect es de uso general y no tieneporque estar asociada sólo al dibujo de rectángulos como veremos másadelante.

Las propiedades Pen y Brush son permantentes, es decir, que mientras no semodifiquen todo lo que se dibuje posteriormente tendrá los colores y estiloespecificado por ambas. Si las figuras que se van a dibujar tienen mismo colorde borde y fondo no es necesario especificar de nuevo el valor de Pen yBrush.

DIBUJANDO UN RECTANGULO CON ESQUINAS REDONDEADAS

Mediante el método RoundRect se pueden crear rectángulos con esquinassuavizadas. Veamos un ejemplo:

wi t h Canvas dobegi n  Pen. Col or : = cl Gr ay;  Pen. Wi dt h : = 3;  Br ush. Col or : = cl Whi t e;  RoundRect ( 300, 150, 380, 200, 30, 30 ) ;end;

Cuyo resultado sería:

Page 131: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 131/306

El procedimiento RoundRect tiene los siguientes parámetros:

pr ocedur e RoundRect ( X1, Y1, X2, Y2, X3, Y3: I nt eger ) ;

donde:

X1, Y1 -> Son las coordenadas de la esquina superior izquierdaX2, Y2 -> Son las coordenadas de la esquina inferior derechaX3, Y3 -> Es grado de redondeo de las esquinas (cuanto más grande másredondeado)

COMO DIBUJAR UN CIRCULO

Para dibujar un círculo se utiliza el procedimiento Ellipse. Vamos a dibujar uncírculo con un borde de color azul oscuro y un fondo de color amarillo:

wi t h Canvas dobegi n  Pen. Col or : = cl Navy;  Pen. Wi dt h : = 5;  Br ush. Col or : = cl Yel l ow;  Br ush. Styl e : = bsDi agCr oss;  El l i pse( 160, 10, 260, 110 ) ;end;

Quedaría así:

A la propiedad Brush le he especificado que me dibuje el fondo amarilloutilizando un patrón de líneas cruzadas diagonales. Al igual que el

procedimiento Rectangle, el procedimiento Ellipse utiliza los mismosparámetros:

pr ocedur e El l i pse( X1, Y1, X2, Y2: I nt eger ) ;pr ocedur e El l i pse( const Rect : TRect ) ;

En este otro ejemplo voy a dibujar un círculo chafado horizontalmente (unaelipse) con un borde de 1 pixel de color blanco y un fondo gris:

wi t h Canvas dobegi n  Pen. Col or : = cl Whi t e;

  Pen. Wi dt h : = 1;  Br ush. Col or : = cl Gr ay;

Page 132: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 132/306

  Br ush. St yl e : = bsSol i d;  El l i pse( 300, 10, 330, 110 ) ;end;

Este sería el resultado:

He tenido que volver a dejar la propiedad Style del Brush con el valor bsSolidpara que vuelva a dibujarme el fondo sólido porque sino me lo hubiera hechocon líneas cruzadas como lo dejé anteriormente.

COMO DIBUJAR LINEAS

La clase TCanvas dispone de un puntero invisible donde dibujará las líneas sino se especifica posición. Para modificar la posición de dicho puntero existeel método MoveTo:

pr ocedur e MoveTo( X, Y: I nt eger ) ;

Si queremos averiguar donde se ha quedado el puntero disponemos de lapropiedad PenPos que es de tipo TPoint:

t ype TPoi nt = packed recor d  X: Longi nt ;  Y: Longi nt ;end;

Al igual que la estructura de datos TRect está definida en la unidad Types.Veamos como realizar un triángulo de color negro y sin fondo:

wi t h Canvas dobegi n  Pen. Col or : = cl Bl ack;  Pen. Wi dt h : = 3;  MoveTo( 50, 150 ) ;

  Li neTo( 100, 220 ) ;  Li neTo( 10, 220 ) ;  Li neTo( 50, 150 ) ;end;

El triángulo quedaría así:

Hemos situado el puntero en un punto y a partir de ahí hemosido tranzando líneas hasta cerrar el triángulo. En este caso la propiedad Brush

Page 133: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 133/306

no tiene ningún valor para el procedimiento LineTo.

COMO DIBUJAR POLIGONOS

El triángulo que hemos hecho anteriormente también podría haberse realizado

mediante un polígono. Además los polígonos permiten especificar el color defondo mediante la propiedad Brush. Por ejemplo vamos a crear un triángulocon un borde amarillo y fondo de color verde:

var  Punt os: ar r ay of TPoi nt ;begi n  wi t h Canvas do

begi n  Pen. Col or : = cl Yel l ow;  Pen. Wi dth : = 3;  Br ush. Col or : = cl Gr een;  SetLengt h( Punt os, 3 ) ;  Punt os[ 0] . x : = 150;  Punt os[ 0] . y : = 150;  Punt os[ 1] . x : = 250;  Punt os[ 1] . y : = 200;  Punt os[ 2] . x : = 150;  Punt os[ 2] . y : = 200;  Pol ygon( Punt os ) ;  end;end;

Quedaría así:

He creado un array dinámico del tipo TPoint con una longitud de 3. Despuéshe ido especificando cada punto del polígono (en este caso un triángulo) y porúltimo le paso dicho array al procedimiento Polygon.

En el próximo artículo seguiremos exprimiendo las características del Canvas.

Pruebas realizadas en Delphi 7.

Dibujando con la clase TCanvas (II)Una vez que ya sabemos como dibujar las figuras básicas con el objeto Canvaspasemos ahora a ver algunas más complicadas.

Page 134: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 134/306

DIBUJAR UN POLIGONO SOLO CON LINEAS

Para dibujar un polígono transparente utilizando líneas tenemos elprocedimiento:

pr ocedur e Pol yl i ne( Poi nt s: ar r ay of TPoi nt ) ;

Funciona exactamente igual que el procedimiento Polygon salvo que no cierrael polígono automáticamente, es decir, el último punto y el primero tiene queser el mismo (para crear el polígono, aunque puede realizarse cualquier otrafigura). Este procedimiento es equivalente a crear un trazado de líneasutilizando el procedimiento LineTo. Veamos un ejemplo:

wi t h Canvas dobegi n  Pen. Col or : = cl Bl ack;  Set Lengt h( Punt os, 4 ) ;  Punt os[ 0] . x : = 200;  Punt os[ 0] . y : = 250;

  Punt os[ 1] . x : = 250;  Punt os[ 1] . y : = 300;  Punt os[ 2] . x : = 150;  Punt os[ 2] . y : = 300;  Punt os[ 3] . x : = 200;  Punt os[ 3] . y : = 250;  Pol yLi ne( Punt os ) ;end;

Este sería el resultado:

Page 135: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 135/306

DIBUJAR POLIGONOS BEZIER 

Los polígonos de Bezier se dibujan trazando curvas entre todos los puntos delpolígono, es decir, son polígonos con las esquinas muy redondeadas. Elprocedimiento para dibujarlos es el siguiente:

wi t h Canvas dobegi n  Pen. Col or : = cl Bl ack;  Set Lengt h( Punt os, 4 ) ;  Punt os[ 0] . x : = 80;  Punt os[ 0] . y : = 250;  Punt os[ 1] . x : = 150;

  Punt os[ 1] . y : = 300;  Punt os[ 2] . x : = 10;  Punt os[ 2] . y : = 300;  Punt os[ 3] . x : = 80;  Punt os[ 3] . y : = 250;  Pol yBezi er ( Punt os ) ;end;

Este sería el resultado:

Aunque he dado las mismas coordenadas que un triángulo, tiene las esquinasinferiores redondeadas.

ESCRIBIENDO TEXTO

Para escribir texto encima de una imagen con la clase TCanvas tenemos elmétodo:

pr ocedur e Text Out ( X, Y: I nt eger ; const Text : st r i ng ) ;

Este método toma como parámetros las coordenadas donde va a comenzar aescribirse el texto y el texto a escribir. Por ejemplo:

wi t h Canvas dobegi n  TextOut ( 10, 10, ' Escr i bi endo t ext o medi ant e el Canvas delf ormul ar i o' ) ;end;

Al no especificar fuente ni color quedaría del siguiente modo:

Page 136: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 136/306

Cuando se escribe texto en un formulario la fuente, el color del texto y elcolor del fondo vienen predeterminados por las propiedades Brush y Font delCanvas. Supongamos que quiero escribir texto con fuente Tahoma, negrita,10 y de color azul con fondo transparente:

wi t h Canvas dobegi n  Br ush. St yl e : = bsCl ear;  Font . Col or : = cl Bl ue;  Font . Name : = ' Tahoma' ;  Font . Si ze : = 10;  TextOut ( 200, 50, ' Ot r o t ext o de pr ueba' ) ;end;

Aquí tenemos nuestro texto personalizado:

Pero nos puede surgir un pequeño problema. ¿Como podemos averiguar lo queva a ocupar en pixels el texto que vamos a escribir? Pues para ello tenemos lasiguiente función:

f uncti on Text Wi dt h( const Text : st r i ng ) : I nt eger ;

Nos devuelve el ancho en pixels del texto que le pasamos como parámetrosegún como esté la propiedad Font antes de llamar a esta función. Y siqueremos saber su altura entonces tenemos esta otra función:

f uncti on Text Hei ght ( const Text : st r i ng ) : I nt eger ;

Lo que afecta a esta función principalmente es la propiedad Font.Size delCanvas. También tenemos esta otra función para obtener simultáneamente elancho y alto del texto a escribir:

f uncti on Text Ext ent ( const Text : st r i ng ) : TSi ze;

Donde TSize es una estructura definida en la unidad Types del siguientemodo:

type  t agSI ZE = packed r ecord  cx: Longi nt ;  cy: Longi nt ;  end;

 TSi ze = t agSI ZE;

COMO RELLENAR LAS FIGURAS DE UN COLOR 

Todos los programas de dibujo incorporan la función de rellenar con pinturauna imagen hasta que encuentre bordes. Es como volcar un bote de pinturasobre las superficie hasta que choque con algo. El Canvas del formulario

dispone del procedimiento:

Page 137: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 137/306

pr ocedure Fl oodFi l l ( X, Y: I nt eger; Col or: TCol or ; Fi l l St yl e: TFi l l St yl e ) ;

Los parámetros X, Y especifican donde se va a comenzar a pintar, elparámetro Color establece el color del borde donde va a chocar la pintura yFillStyle define el modo de pintar. Veamos un ejemplo de cómo pintar untriángulo de rojo con bordes negros:

wi t h Canvas dobegi n  Br ush. Col or : = cl Red;  Fl oodFi l l ( 200, 270, c l Bl ack, f sBor der ) ;end;

El último parámetro (FillStyle) se utiliza para decirle a la función si queremosque pinte hasta el borde de un color en concreto (clBlack y fsBorder) o si

deseamos que dibuje toda la superficie de un color hasta que encuentre uncolor distinto. Por ejemplo:

wi t h Canvas dobegi n  Br ush. Col or : = cl Red;  Fl oodFi l l ( 200, 270, cl Si l ver , f sSurf ace ) ;end;

En este caso le he dicho que me pinte de rojo toda la superficie cuyo fondo esde color clSilver. Si encuentra un color que no sea clSilver entonces se para.Esa es la diferencia entre los valores fsBorder (un sólo borde) y fsSurface

(cualquier borde pero la misma superficie). En ambos ejemplos he rellenadode color rojo el polígono creado al principio de este artículo:

En el próximo artículo terminaremos de ver las propiedades de la claseTCanvas.

Pruebas realizadas en Delphi 7.

Dibujando con la clase TCanvas (III)Vamos a terminar de ver lo más importante de las operaciones que se puedenrealizar con el objeto Canvas.

DIBUJAR UN RECTANGULO SIN FONDO

El procedimiento FrameRect permite crear rectángulos sin fondo teniendo encuenta el valor de la propiedad Brush:

Page 138: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 138/306

pr ocedur e FrameRect ( const Rect : TRect ) ;

Veamos un ejemplo:

wi t h Canvas do

begi n  R. Lef t : = 300;  R. Top : = 250;  R. Ri ght : = 350;  R. Bot t om : = 300;  Br ush. Col or : = cl Gr een;  Fr ameRect ( R ) ;end;

Este sería el dibujo resultante:

COMO COPIAR IMAGENES DE UN BITMAP A OTRO

La manera más utilizada de copiar imágenes es mediante el método Draw:

pr ocedur e Dr aw( X, Y: I nt eger; Gr aphi c: TGr aphi c ) ;

Vamos a ver un ejemplo de como copiar la imagen de un objeto de la claseTImage a nuestro formulario utilizando el procedimiento Draw:

varR: TRect ;begi n  wi t h Canvas do  begi n  R. Lef t : = 300;  R. Top : = 250;  R. Ri ght : = 350;  R. Bot t om : = 300;  Br ush. Col or : = cl Gr een;  Fr ameRect ( R ) ;  Dr aw( 100, 100, I magen. Pi ct ur e. Gr aphi c ) ;

  end;end;

Hemos supuesto que el objeto de la clase TImage se llama Imagen. Este seríael resultado:

Page 139: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 139/306

La imagen copiada consta de cuatro iconos y un fondo de color blanco. Otracosa que podemos hacer es modificar el tamaño de la imagen a copiarutilizando el procedimiento:

pr ocedur e St r etchDr aw( const Rect : TRect ; Gr aphi c: TGr aphi c ) ;

El parámetro Rect determina las nuevas coordenadas así como en ancho y altode la imagen a copiar. En el siguiente ejemplo voy a copiar la misma imagenpero voy a reducirla un tamaño de 100x100:

var  R: TRect ;begi n  wi t h Canvas do  begi n  R. Lef t : = 100;  R. Top : = 100;  R. Ri ght : = 200;  R. Bot t om : = 200;  St r et chDr aw( R, I magen. Pi ct ur e. Gr aphi c ) ;  end;end;

Quedaría de la siguiente manera:

También se pueden copiar imágenes utilizando el procedimiento:

pr ocedur e CopyRect ( const Dest : TRect ; Canvas: TCanvas; const Sour ce: TRect ) ;

cuyos parámetros son:

Page 140: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 140/306

Dest: Coordenadas del rectángulo destino.Canvas: Referencia al Canvas del origen a copiar.Source: Coordenadas del rectángulo origen.

Para el ejemplo anterior voy a copiar la imagen en su tamaño original al

formulario:var  Or i gen, Dest i no: TRect ;begi n  wi t h Canvas do  begi n  Or i gen. Lef t : = 0;  Or i gen. Top : = 0;  Or i gen. Ri ght : = I magen. Wi dt h;  Or i gen. Bot t om : = I magen. Hei ght ;  Dest i no. Lef t : = 100;  Dest i no. Top : = 100;

  Dest i no. Ri ght : = 100 + I magen. Wi dth;  Dest i no. Bot t om : = 100 + I magen. Hei ght ;  CopyRect ( Dest i no, I magen. Canvas, Or i gen ) ;  end;end;

Entonces, ¿que diferencia hay entre Draw o StretchDraw y CopyRect? Ladiferencia es que CopyRect sólo puede utilizarse cuando el objeto TImage hacargado un bitmap (*.BMP) porque si es un JPG provoca una excepción. Encambio las funciones Draw o StretchDraw siempre funcionanindependientemente del tipo de imagen que sea.

LOS MODOS DE COPIA DE UNA IMAGEN

En principio cuando se realiza la copia de una imagen a otra, la copia de lospixels es exacta. Pero si queremos modificar el modo de copiar entonces laclase TCanvas tiene de la propiedad CopyMode que establece que tipo deoperación que se va a realizar. Estos son sus posibles valores:

cmBlackness: pinta la imagen destino de color negro, independientementedel origen.

cmDstInvert: Invierte los colores de la imagen según la imagen destino.cmMergeCopy: mezcla la imagen origen y destino utilizando el operadorbinario AND.

cmMergePaint: mezcla la imagen origen y destino utilizando el operadorbinario OR.

cmNotSrcCopy: copia la imagen origen invertida a la imagen destino.

cmNotSrcErase: mezcla las imagenes origen y destino y después las invierte

con el operador binario OR.

Page 141: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 141/306

cmPatCopy: copia la imagen según el valor de la propiedad Brush.Style delCanvas.

cmPatInvert: copia la imagen invertida según el valor de la propiedadBrush.Style del Canvas.

cmPatPaint: combina las imágenes origen y destino utilizando la operaciónbinaria OR para luego invertirlas.

cmSrcAnd: combina la imagen origen con la imagen destino utilizando eloperador binario AND.

cmSrcCopy: realiza una copia exacta de la imagen origen a la imagen destino(por defecto).

cmSrcErase: invierte la imagen destino y copia el origen utilizando el

operador binario AND.

cmSrcInvert: invierte las imágenes origen y destino utilizando el operadorbinario XOR.

cmSrcPaint: combina las imágenes destino y origen utilizando el operadorbinario AND.

cmWhiteness: pinta toda la imagen destino de color blanco ignorando laimagen origen.

Por ejemplo voy a crear un efecto fantasma con la imagen origen:

wi t h Canvas dobegi n  CopyMode : = cmMer gePai nt ;  Dr aw( 100, 100, I magen. Pi ct ur e. Gr aphi c ) ;end;

El efecto sería el siguiente:

COMO ACCEDER A LOS PIXELS DE LA IMAGEN

Si no son suficientes las operaciones que podemos realizar en una imagen

Page 142: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 142/306

también podemos acceder directamente a los pixels de una imagen a travesde su propiedad Pixels:

pr oper t y Pi xel s[ X, Y: I nt eger ] : TCol or ;

Esta propiedad nos sirve igualmente para leer y para escribir pixels en laimagen. El componente TColor es un tipo entero de 32 bits definido en launidad Graphics del siguiente modo:

type  TCol or = - $7FFFFFFF- 1. . $7FFFFFFF;

Dentro del mismo número entero están definidos los colores básicos RGB loscuales son:

R  = Red (Rojo)G = Green (Verde)B = Blue (Azul)

Combinando estos tres colores se puede crear cualquier otro color. Estoscolores estan dentro del valor TColor del siguiento modo:

$00GGBBRR 

Donde GG es el byte en hexadecimal que representa el color verde, BB es elcolor azul y RR  es el color rojo. Aquí tenemos unos ejemplos de los númerosen hexadecimal:

Roj o : = $000000FF;Azul : = $0000FF00;Ver de : = $00FF0000;Bl anco : = $00FFFFFF;Negr o : = $00000000;

Para no complicarnos mucho la vida Delphi ya dispone de colorespredeterminados tales como clRed (Rojo), clBlue (Azul), etc. Sabiendo estoimaginaos que deseo hacer un programa que convierta todos los coloresblancos de la imagen en amarillo:

var  i , j : I nt eger ;begi n  wi t h Canvas do  f or j : = 0 t o Cl i ent Hei ght - 1 do  f or i : = 0 t o Cl i ent Wi dt h - 1 do  i f Pi xel s[ i , j ] = cl Whi te then  Pi xel s [ i , j ] : = cl Yel l ow;end;

Mediante un doble bucle recorro toda la imagen y compruebo si el pixel dondeestoy es blanco para sustituirlo por el amarillo. Este sería el resultado:

Page 143: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 143/306

Con este método se podemos realizar nuestros propios filtros o crear cualquiertipo de efecto.

Con esto finalizamos el apartado dedicado al objeto Canvas.

Pruebas realizadas en Delphi 7.

El componente TTreeView (I)Cuando queremos representar una información en forma de árbol incluyendonodos padres e hijos el componente ideal para ello es el TreeView, el cualfunciona exactamente igual que la parte izquierda del Explorador deWindows.

Veamos las peculiaridades que aporta este componente. Todas las funcioneslas voy a aplicar sobre esta pantalla:

AÑADIENDO ELEMENTOS AL ARBOL

Page 144: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 144/306

Cuando se añaden elementos a un árbol pueden pertenecer al elemento raizdel mismo (sin padre) o pertenecer a un elemento ya creado. Los elementosdel árbol se llaman nodos (Node) pudiendose crear tantos niveles de nodoscomo se deseen.

Vamos a crear un procedimiento asociado al botón Nuevo que va a añadir unnuevo nodo al árbol. El elemento insertado será un nodo raiz, a menos que elusuario seleccione un nodo antes de ello, que hará que sea su hijo:

pr ocedur e TFTr eeVi ew. BNuevoCl i ck( Sender : TObj ect ) ;var  sNombr e: st r i ng;begi n  sNombr e : = I nput Box( ' Cr ear un nodo' , ' Nombr e: ' , ' ' ) ;

  i f sNombr e <> ' ' t hen  begi n  / / ¿Hay un nodo sel ecci onado?  i f Tr eeVi ew. Sel ect ed <> ni l t hen  begi n  TreeVi ew. I t ems. AddChi l d( TreeVi ew. Sel ect ed, sNombr e ) ;  TreeVi ew. Sel ect ed. Expanded : = True;  end  el se  TreeVi ew. I t ems. Add( ni l , sNombre ) ;  end;end;

Lo que hace este procedimiento es preguntarnos el nombre del nodo y si elusuario ha seleccionado uno anteriormente lo mete como hijo (AddChild)expandiendo además el nodo padre (Expanded) como si el usuario hubiesepulsado el botón + del elemento padre.

MODIFICANDO LOS ELEMENTOS DEL ARBOL

Una vez creado un árbol de elementos se puede modificar el texto de cadauno de ellos mediante la propiedad Text. Vamos a hacer que si el usuariopulsa el botón Modificar y hay un nodo seleccionado el programa nos pregunteel nuevo nombre del nodo y lo cambie:

pr ocedur e TFTr eeVi ew. BModi f i car Cl i ck( Sender : TObj ect ) ;var  sNombr e: st r i ng;begi n  i f TreeVi ew. Sel ected <> ni l t hen  begi n  sNombre : = I nput Box( ' Cr ear un nodo' , ' Nombr e: ' ,

 Tr eeVi ew. Sel ect ed. Text ) ;

  i f sNombr e <> ' ' t hen  TreeVi ew. Sel ect ed. Text : = sNombr e;  end;

end;

Page 145: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 145/306

Cuando se van a hacer operaciones con los nodos hay que asegurarse siempreque el nodo al que vayamos a acceder no sea nil, para evitar AccessViolations.

ELIMINANDO LOS ELEMENTOS DEL ARBOL

Para eliminar un nodo del árbol se utiliza el método Delete:

pr ocedur e TFTr eeVi ew. BEl i mi nar Cl i ck( Sender : TObj ect ) ;begi n  i f TreeVi ew. Sel ected <> ni l t hen  Tr eeVi ew. Sel ect ed. Del ete;end;

Y si queremos eliminar todos los nodos se utiliza el método Clear de lapropiedad Items:

pr ocedur e TFTreeVi ew. BBorr arTodosCl i ck( Sender : TObj ect ) ;begi n  Tr eeVi ew. I t ems. Cl ear ;end;

ORDENANDO LOS NODOS ALFABETICAMENTE

El modo de ordenar los elementos de un componente TreeView es igual al deordenar los elementos de un componente ListView. Hay dos métodos:CustomSort y AlphaSort. El método CustomSort no voy a explicarlo ya que lomencioné anteriormente en el artículo dedicado al componente ListView y esun poco más primitivo que utilizar AlphaSort.

Para ordenar todos los elementos de un árbol TreeView se utiliza el método:

pr ocedur e TFTr eeVi ew. BOr denarCl i ck( Sender: TObj ect ) ;begi n  Tr eeVi ew. Al phaSor t ( Tr ue ) ;end;

El parámetro booleano especifica si queremos que ordene tanto los elementospadres como los hijos (True) o sólo los elementos padres. Si no se especifica

nada se asume que ordene todos los elementos.

Ahora hay que programar el evento OnCompare del TreeView para queordene alfabéticamente los elementos:

procedure TFTr eeVi ew. TreeVi ewCompar e( Sender : TObj ect ; Node1,  Node2: TTr eeNode; Data: I nteger; var Compare: I nt eger ) ;

begi n  Compare : = CompareText ( Node1. Text , Node2. Text ) ;end;

Si queremos que la ordenación sea descendente entonces sólo hay que

cambiar el signo:

Page 146: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 146/306

Compar e : = - Compar eText ( Node1. Text , Node2. Text ) ;

GUARDANDO LA INFORMACION DEL ARBOL EN UN ARCHIVO DE TEXTO

La claseTTreeView

 dispone del métodoSaveToFile

 para volcar todos losnodos en un archivo de texto para su posterior utilización. Voy a hacer unprocedimiento que pregunte al usuario que nombre deseamos dar al archivo ylo guardará en el mismo directorio del programa con la extensión .TXT:

pr ocedur e TFTr eeVi ew. BGuardarCl i ck( Sender: TObj ect ) ;var sNombre: st r i ng;begi n  sNombr e : = I nput Box( ' Cr ear un nodo' , ' Nombr e: ' , ' ' ) ;

  i f sNombr e <> ' ' t hen  Tr eeVi ew. SaveToFi l e( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) +sNombr e + ' . t xt ' ) ;

end;

La información la guarda por líneas donde cada elemento hijo va separado portabuladores:

document os  vent ascont actos  Pabl o  Ana  Mari acl aves

  Terra  Hot mai l  GMai l

CARGANDO LA INFORMACION DEL ARBOL GUARDADA ANTERIORMENTE

Para cargar el archivo de texto vamos a crear en tiempo real el componenteTOpenDialog para que le pregunte al usuario el archivo de texto a cargar:

pr ocedur e TFTreeVi ew. BCargarCl i ck( Sender : TObj ect ) ;var  Abr i r : TOpenDi al og;

begi n  Abr i r : = TOpenDi al og. Cr eat e( Sel f ) ;  Abr i r . I ni t i al Di r : = Ext r actFi l ePat h( Appl i cat i on. ExeName ) ;  Abr i r . Fi l ter : = ' TreeVi ew| * . t xt ' ;

  i f Abr i r . Execut e t hen  Tr eeVi ew. LoadFr omFi l e( Abr i r . Fi l eName ) ;

  Abr i r . Free;end;

Hemos modificado el filtro de la carga para que sólo se vean archivos TXT.

En el próximo artículo seguiremos viendo más propiedades interesantes delcomponente TreeView.

Page 147: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 147/306

Pruebas realizadas en Delphi 7.

El componente TTreeView (II)Después de ver el manejo básico de un árbol TreeView pasemos a ver otras

características menos conocidas pero también de suma importancia.

OBTENIENDO EL NIVEL DE CADA NODO

Cuando se van insertando elementos dentro de un árbol tenemos la dificultadde saber en que nivel de profundidad se encuentra cada uno de los nodos.Cada uno de los Items del componente TTreeView es de la clase TTreeNode.

Esta clase dispone de la propiedad Level la cual nos da el nivel deprofundidad dentro del árbol. Veamos como obtener el nivel de cada uno delos nodos del árbol. Este procedimiento recorre todos los nodos del árbol y lesañade a su nombre el nivel:

pr ocedur e TFTr eeVi ew. BNi vel Cl i ck( Sender : TObj ect ) ;var i : I nt eger ;begi n  / / Aver i guamos el ni vel de cada nodo  f or i : = 0 t o Tr eeVi ew. I t ems. Count - 1 do  Tr eeVi ew. I t ems[ i ] . Text : = Tr eeVi ew. I t ems[ i ] . Text + ' _' +  I nt ToSt r ( ( Tr eeVi ew. I t ems[ i ] as TTr eeNode ) . Level ) ;end.

Los elementos situados en la raiz tienen un nivel 0, los hijos un nivel 1, losnietos un 2...

LEYENDO LOS NODOS SELECCIONADOS

El nodo actualmente seleccionado se obtiene mediante:

 Tr eeVi ew. Sel ect ed

donde valdrá nil si no hay ninguno seleccionado. Pero si activamos la

propiedad MultiSelect la forma de leer aquellos nodos seleccionados sería lasiguiente:

pr ocedur e TFTreeVi ew. BSel ecci onadosCl i ck( Sender: TObj ect ) ;var  i : I nteger ;  Sel ecci onados: TSt r i ngLi st ;begi n  Sel ecci onados : = TSt r i ngLi st . Cr eat e;

  f or i : = 0 t o Tr eeVi ew. I t ems. Count - 1 do  i f Tr eeVi ew. I t ems[ i ] . Sel ected t hen  Sel ecci onados. Add( Tr eeVi ew. I t ems[ i ] . Text ) ;

  ShowMessage( Sel ecci onados. Text ) ;

Page 148: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 148/306

  Sel ecci onados. Fr ee;end;

Al igual que hicimos con el componente ListView volcamos el nombre de losnodos seleccionados en un StringList y lo sacamos por pantalla.

ASOCIANDO UNA IMAGEN A CADA NODO

Si añadimos a nuestro formulario el componente TImageList se puedenasocionar iconos distintos a cada uno de los elementos del árbol TTreeView.Para ello asociamos este componente a la propiedad Images del TTreeView.Una vez hecho esto todos los nodos tendrán a su izquierda la primera imagende la lista de imágenes TImageList.

Ya será cuestión de cada cual asociar la imagen correspondiente cada nodo.Vamos a ver un ejemplo que recorre los elementos del árbol y va a poner la

segunda imagen de la lista de imágenes a aquellos nodos hijos (de nivel 1):pr ocedur e TFTr eeVi ew. Cambi arNodosHi j os;var  i : I nteger ;begi n  f or i : = 0 t o Tr eeVi ew. I t ems. Count - 1 do  i f ( Tr eeVi ew. I t ems[ i ] as TTr eeNode ) . Level = 1 t hen  ( TreeVi ew. I t ems[ i ] as TTr eeNode ) . I mageI ndex : = 1;end;

CAMBIANDO LA FORMA EN QUE SE MUESTRAN LOS NODOS

El componente TreeView dispone la propiedad Ident la cual determina laidentación de entre los nodos padres y sus hijos que por defecto es de 19pixels. Se puede cambiar en cualquier momento, pero afectará a todos losnodos del árbol.

Otra cosa que se puede hacer al cargar un árbol desde un archivo de texto esexpandir todos sus nodos, ya que cuando se carga el árbol esta compactado.Para solucionarlo se hace:

 Tr eeVi ew. Ful l Expand;

Equivale a pulsar el botón + de cada uno de los nodos padres.

CAMBIANDO LA FORMA DE DIBUJAR LOS NODOS

Al igual que vimos con el componente ListView también se puede modificaren tiempo real la forma de dibujar cada nodo. Para ello lo que hacemos esreprogramar el evento OnCustomDrawItem. Veamos un ejemplo de comohacer que los nodos hijos aparenzan de color de fuente azul y negrita:

procedur e TFTr eeVi ew. Tr eeVi ewCust omDr awI t em( Sender : TCust omTr eeVi ew;  Node: TTr eeNode; St at e: TCust omDr awSt at e; var Def aul t Dr aw: Bool ean) ;

Page 149: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 149/306

begi n  i f Node. Level = 1 t hen  begi n  Sender. Canvas. Font . Col or : = cl Bl ue;  Sender. Canvas. Font . St yl e : = [ f sBol d] ;  end

  el se  begi n  Sender. Canvas. Font . Col or : = cl Bl ack;  Sender. Canvas. Font . St yl e : = [ ] ;  end;

  i f cdsFocused i n St at e t hen  Sender. Canvas. Font . Col or : = cl Whi t e;end;

En las dos últimas líneas del procedimiento también nos hemos asegurado deque si un elemento esta seleccionado la fuente salga de color blanca. Hemosutilizado para ello el parámetro State del evento. Los posibles valores de estavariable son:

cdsSel ect ed - > La col umna o f i l a ha si do sel ecci onadacdsGr ayed - > La col umna o f i l a est a gri saceacdsDi sabl ed - > La col umna o f i l a esta deshabi l i t adacdsChecked - > La f i l a aparece con el CheckBox act i vadocdsFocused - > La col umna o f i l a est a enf ocadacdsDef aul t - > Por def ect ocdsHot - > Se ha act i vado el HotTrack y est a enf ocadocdsMarked - > La f i l a est a marcadacdsI ndet ermi nate - > La f i l a no est a sel ecci onada ni desel ecci onada

Con esto se resumen las propiedades más importantes del componenteTreeView.

Pruebas realizadas en Delphi 7.

Explorar unidades y directoriosSi importante es controlar el manejo de archivos no menos importante es elsaber moverse por las unidades de disco y los directorios.

Veamos que tenemos Delphi para estos menesteres:

function CreateDir( const Dir: string ): Boolean;

Esta función crea un nuevo directorio en la ruta indicada por Dir. DevuelveTrue o False dependiendo si ha podido crearlo o no. El único inconvenienteque tiene esta función es que deben existir los directorios padres. Porejemplo:

Cr eat eDi r ( ' C: \ pr ueba' ) devuel ve Tr ueCr eat eDi r ( ' C: \ pr ueba\ document os' ) devuel ve Tr ue

Cr eat eDi r ( ' C: \ ot r apr ueba\ document os' ) devuel ve Fal se ( y no l o cr ea)

Page 150: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 150/306

function ForceDirectories( Dir: string ): Boolean;

Esta función es similar a CreateDir salvo que también crea toda la ruta dedirectorios padres.

For ceDi r ect or i es( ' C: \ pr ueba' ) devuel ve Tr ueFor ceDi r ect or i es( ' C: \ pr ueba\ document os' ) devuel ve TrueFor ceDi r ect or i es( ' C: \ otr apr ueba\ document os' ) devuel ve True

procedure ChDir( const S: string ); overload;

Este procedimiento cambia el directorio actual al indicado por el parámetroS. Por ejemplo:

ChDi r ( ' C: \ Wi ndows\ Font s' ) ;

function GetCurrentDir: string;

Nos devuelve el nombre del directorio actual donde estamos posicionados. Porejemplo:

Get Cur r ent Di r devuel ve C: \ Wi ndows\ Font s

function SetCurrentDir( const Dir: string ): Boolean;

Establece el directorio actual devolviendo True si lo ha conseguido. Por

ejemplo:Set Cur r ent Di r ( ' C: \ Wi ndows\ J ava' ) ;

procedure GetDir( D: Byte; var S: string );

Devuelve el directorio actual de una unidad y lo mete en la variable S. Elparámetro D es el número de la unidad siendo:

D Uni dad- - - - - - - - - - - - - - - - - - - - -0 Uni dad por def ecto

1 A:2 B:3 C:. . .

Por ejemplo para leer el directorio actual de la unidad C:

var  sDi r ect or i o: St r i ng;begi n  Get Di r ( 3, sDi r ector i o ) ;  ShowMessage( ' El di r ect ori o act ual de l a uni dad C: es ' +

sDi rect or i o ) ;end;

Page 151: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 151/306

function RemoveDir( const Dir: string ): Boolean;

Elimina un directorio en el caso de que este vacío, devolviendo False si no hapodido hacerlo.

RemoveDi r ( ' C: \ pr ueba\ document os' ) devuel ve TrueRemoveDi r ( ' C: \ pr ueba' ) devuel ve TrueRemoveDi r ( ' C: \ ot r apr ueba' ) devuel ve Fal se porque no est avací o

function DirectoryExists( const Directory: string ): Boolean;

Comprueba si existe el directorio indicado por el parámetro Directory. Porejemplo:

Di r ect or yExi st s( ' C: \ Wi ndows\ Syst em32\ ' ) devuel ve True

Di r ect or yExi st s( ' C: \ Wi ndows\ Mi sDocument os\ ' ) devuel ve Fal se

function DiskFree( Drive: Byte ): Int64;

Devuelve el número de bytes libres de una unidad de dico indicada por laletra Drive:

Dr i ve Uni dad- - - - - - - - - - - - - - - - -0 Uni dad por def ect o1 A:2 B:

3 C:. . .

Por ejemplo vamos a ver el número de bytes libres de la unidad C:

Di skFr ee( 3 ) devuel ve 5579714560

function DiskSize( Drive: Byte ): Int64;

Nos dice el tamaño total en bytes de una unidad de disco. Por ejemplo:

Di skSi ze( 3 ) devuel ve 20974428160

BUSCANDO ARCHIVOS DENTRO DE UN DIRECTORIO

Para buscar archivos dentro de un directorio disponemos de las funciones:

function FindFirst( const Path: string; Attr: Integer; var F: TSearchRec ):Integer;

Busca el primer archivo, directorio o unidad que se encuentre dentro de una

Page 152: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 152/306

ruta en concreto. Devuelve un cero si ha encontrado algo. El parámetroTSearchRec es una estructura de datos donde se almacena lo encontrado:

type  TSearchRec = r ecor d  Ti me: I nt eger ;

  Si ze: I nt eger ;  At t r : I nt eger;  Name: TFi l eName;  Excl udeAt t r : I nt eger ;  Fi ndHandl e: THandl e;  Fi ndDat a: TWi n32Fi ndDat a;  end;

function FindNext( var F: TSearchRec ): Integer;

Busca el siguiente archivo, directorio o unidad especificado anteriormente por

la función FindFirst. Devuelve un cero si ha encontrado algo.

procedure FindClose( var F: TSearchRec );

Este procedimiento cierra la búsqueda comenzada por FindFirst y FindNext.

Veamos un ejemplo donde se utilizan estas funciones. Vamos a hacer unprocedimiento que lista sólo los archivos de un directorio que le pasemos yvuelca su contenido en un StringList:

pr ocedur e TFPr i nci pal . Li st ar ( sDi r ectori o: st r i ng; var Resul t ado:

 TSt r i ngLi st ) ;var  Busqueda: TSearchRec;  i Resul t ado: I nt eger ;begi n  / / Nos aseguramos que t ermi ne en cont r abar r a  sDi r ect or i o : = I ncl udeTr ai l i ngBacksl ash( sDi r ectori o ) ;

  i Resul t ado : = Fi ndFi r st ( sDi r ector i o + ' *. *' , f aAnyFi l e, Busqueda) ;

  whi l e i Resul t ado = 0 do  begi n

  / / ¿Ha encont r ado un archi vo y no es un di r ect or i o?  i f ( Busqueda. At t r and f aAr chi ve = f aAr chi ve ) and  ( Busqueda. At t r and f aDi r ect ory <> f aDi r ect or y ) t hen  Resul t ado. Add( Busqueda. Name ) ;

  i Resul t ado : = Fi ndNext ( Busqueda ) ;  end;

  Fi ndCl ose( Busqueda ) ;end;

Si listamos el raiz de la unidad C:

var  Di rect or i o: TSt r i ngLi st ;

Page 153: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 153/306

begi n  Di r ectori o : = TSt r i ngLi st . Cr eat e;  Li s tar ( ' C: ' , Di rector i o ) ;  ShowMessage( Di r ector i o. Text ) ;  Di r ectori o. Free;end;

El resultado sería:

AUTOEXEC. BATBoot f ont . bi nCONFI G. SYSI NSTALL. LOGI O. SYSMSDOS. SYSNTDETECT. COM

Con estas tres funciones se pueden hacer cosas tan importantes como eliminar

directorios, realizar búsquedas de archivos, calcular lo que ocupa undirectorio en bytes, etc.

Pruebas realizadas en Delphi 7.

Mostrando datos en el componenteStringGridAnteriormente vimos como mostrar información en un componente ListViewllegando incluso a cambiar el color de filas y columnas a nuestro antojo. Elúnico inconveniente estaba en que no se podían cambiar los títulos de las

columnas, ya que venían predeterminadas por los colores de Windows.

Pues bien, el componente de la clase TStringGrid es algo más cutre que elListView, pero permite cambiar al 100% el formato de todas las celdas.Veamos primero como meter información en el mismo. Al igual que ocurríacon el ListView, todos las celdas de un componente StringGrid son de tipostring, siendo nosotros los que le tenemos que dar formato a mano.

AÑADIENDO DATOS A LA REJILLA

Vamos a crear una rejilla de datos con las siguiente columnas:

NOMBRE, APELLI DO1, APELLI DO2, NI F, I MPORTE PTE.

Cuando insertamos un componente StringGrid en el formulario nos va a ponerpor defecto la primera columna con celdas fijas (fixed). Vamos a fijar lassiguientes propiedades:

Pr opi edad Val or Descr i pci ón- - - - - - - - - - - - - - - - - - - - - - - - -Col Count 5 5 col umnas

RowCount 4 4 f i l asFi xedCol s 0 0 col umnas f i j as

Page 154: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 154/306

Fi xedRows 1 1 f i l a f i j aDef aul t RowHei ght 20 al t ur a de l as f i l as a 20 pi xel s

Ahora creamos un procedimiento para completar de datos la rejilla:

pr ocedur e TFor mul ar i o. Rel l enar Tabl a;

begi n  wi t h St r i ngGr i d do  begi n  / / Tí t ul o de l as col umnas  Cel l s[ 0, 0] : = ' NOMBRE' ;  Cel l s[ 1, 0] : = ' APELLI DO1' ;  Cel l s[ 2, 0] : = ' APELLI DO2' ;  Cel l s [3, 0] : = ' NI F' ;  Cel l s[ 4, 0] : = ' I MPORTE PTE. ' ;

  / / Dat os  Cel l s[ 0, 1] : = ' PABLO' ;  Cel l s[ 1, 1] : = ' GARCI A' ;

  Cel l s[ 2, 1] : = ' MARTI NEZ' ;  Cel l s[ 3, 1] : = ' 67348321D' ;  Cel l s[ 4, 1] : = ' 1500, 36' ;

  / / Dat os  Cel l s[ 0, 2] : = ' MARI A' ;  Cel l s[ 1, 2] : = ' SANCHEZ' ;  Cel l s[ 2, 2] : = ' PALAZON' ;  Cel l s[ 3, 2] : = ' 44878234A' ;  Cel l s[ 4, 2] : = ' 635, 21' ;

  / / Dat os  Cel l s[ 0, 3] : = ' CARMEN' ;

  Cel l s[ 1, 3] : = ' PEREZ' ;  Cel l s[ 2, 3] : = ' GUI LLEN' ;  Cel l s[ 3, 3] : = ' 76892693L' ;  Cel l s[ 4, 3] : = ' 211, 66' ;  end;end;

Al ejecutar el programa puede apreciarse lo mal que quedan los datos enpantalla, sobre todo la columna del importe pendiente:

DANDO FORMATO A LAS CELDAS DE UN COMPONENTE STRINGGRIND

Lo que vamos a hacer a continuación es lo siguiente:

- La primera fila fija va a ser de color de fondo azul oscuro con fuente blancay además el texto va a ir centrado.

- La columna del importe pendiente va a tener la fuente de color verde y va air alineada a la derecha.

Page 155: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 155/306

- El resto de columnas tendrán el color de fondo blanco y el texto en negro.

Todo esto hay que hacerlo en el evento OnDrawCell del componenteStringGrid:

pr ocedur e TFor mul ar i o. St r i ngGr i dDr awCel l ( Sender : TObj ect ; ACol ,  ARow: I nt eger; Rect : TRect ; St at e: TGr i dDr awSt ate ) ;var  sTexto: St r i ng; / / Text o que va a i mpr i mi r en l a cel daact ual  Al i neaci on: TAl i gnment ; / / Al i neaci ón que l e vamos a dar al t ext o  i AnchoTexto: I nt eger ; / / Ancho del t ext o a i mpr i mi r en pi xel sbegi n  wi t h St r i ngGr i d. Canvas do  begi n  / / Lo pr i mero es coger l a f uent e por def ecto que l e hemos asi gnadoal component e

  Font . Name : = St r i ngGr i d. Font . Name;  Font . Si ze : = St r i ngGr i d. Font . Si ze;

  i f ARow = 0 t hen  Al i neaci on : = t aCent er  el se  / / Si es l a col umna del i mpor t e pendi ent e al i neamos el t ext o al a der echa  i f ACol = 4 t hen  Al i neaci on : = t aRi ght J ust i f y  el se  Al i neaci on : = t aLef t J ust i f y;

  / / ¿Es una cel da f i j a de sól o l ectur a?  i f gdFi xed i n St at e t hen  begi n  Br ush. Col or : = cl Navy; / / l e ponemos azul de f ondo  Font . Col or : = cl Whi t e; / / f uent e bl anca  Font . St yl e : = [ f sBol d] ; / / y negr i t a  end  el se  begi n  / / ¿Est a enf ocada l a cel da?  i f gdFocused i n St at e t hen  begi n  Br ush. Col or : = cl Red; / / f ondo r oj o  Font . Col or : = cl Whi t e; / / f uent e bl anca  Font . St yl e : = [ f sBol d] ; / / y negr i t a  end  el se  begi n  / / Par a el rest o de cel das el f ondo l o ponemos bl anco  Br ush. Col or : = cl Wi ndow;

  / / ¿Es l a col umna del i mport e pendi ent e?  i f ACol = 4 t hen  begi n  Font . Col or : = cl Gr een; / / l a pi nt amos de azul  Font . St yl e : = [ f sBol d] ; / / y negr i t a  Al i neaci on : = t aRi ght J ust i f y;  end  el se

Page 156: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 156/306

  begi n  Font . Col or : = cl Bl ack;  Font . St yl e : = [ ] ;  end;  end;  end;

  sText o : = St r i ngGr i d. Cel l s[ ACol , ARow] ;  Fi l l Rect( Rect ) ;  i AnchoText o : = Text Wi dt h( sText o ) ;

  case Al i neaci on of   t aLef t J ust i f y: Text Out ( Rect . Lef t + 5, Rect . Top + 2, sTexto ) ;  t aCent er : Text Out ( Rect. Lef t + ( ( Rect . Ri ght - Rect. Lef t ) -i AnchoText o ) di v 2, Rect . Top + 2, sText o ) ;  t aRi ght J ust i f y: Text Out ( Rect . Ri ght - i AnchoText o - 2, Rect . Top+ 2, sText o ) ;  end;  end;end;

Así quedaría al ejecutarlo:

Sólo hay un pequeño inconveniente y es que la rejilla primero se pinta demanera normal y luego nosotros volvemos a pintarla encima con el eventoOnDrawCell con lo cual hace el proceso dos veces. Si queremos que sólo sehaga una vez hay que poner a False la propiedad DefaultDrawing. Quedaríade la siguiente manera:

Por lo demás creo que este componente que puede sernos muy útil para

mostrar datos por pantalla en formato de sólo lectura. En formato deescritura es algo flojo porque habría que controlar que tipos de datos puedeescribir el usuario según en que columnas esté.

Pruebas realizadas en Delphi 7.

Funciones y procedimientos para fecha yhora (I)Si hay algo de lo que no podemos escapar los programadores de gestión es eltener que lidiar con campos de fecha y hora tales como cálculo de días entrefechas, averiguar en que fecha caen los días festivos, cronometrar el tiempoque tardan nuestras rutinas, etc.

Page 157: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 157/306

Para la mayoría de funciones tenemos que añadir la unidad DateUtils:

uses

  Wi ndows, Messages, . . . , Dat eUt i l s;

Veamos entonces que funciones nos pueden ser útiles para ello:

function DayOfTheMonth( const AValue: TDateTime ): Word;

Esta función extrae el número de día de una fecha en concreto sin tener queutilizar la función DecodeDate. Por ejemplo:

var Fecha: TDat e;begi n

  Fecha : = St r ToDat e( ' 23/ 08/ 2007' ) ;  ShowMessage( I nt ToSt r ( DayOf TheMont h( Fecha ) ) ; / / muest r a 23end;

function DayOfTheWeek( const AValue: TDateTime ): Word;

Nos díce en que día de la semana (1-7) cae una fecha en concreto (el primerdía es el Lunes, al contrario de otras funciones de fecha y hora donde elprimer día es el Domingo). Por ejemplo:

Fecha : = St r ToDat e( ' 5/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf TheWeek( Fecha ) ) ) ; / / devuel ve 7

( Domi ngo)Fecha : = St r ToDate( ' 10/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf TheWeek( Fecha ) ) ) ; / / devuel ve 5( Vi er nes)Fecha : = St r ToDate( ' 23/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf TheWeek( Fecha ) ) ) ; / / devuel ve 4( J ueves)

function DayOfWeek( Date: TDateTime ): Integer;

Esta función es igual a la función DayOfTheWeek con la diferencia de que elprimer día de la semena es el Domingo. Por ejemplo:

Fecha : = St r ToDat e( ' 5/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf Week( Fecha ) ) ) ; / / devuel ve 1 ( Domi ngo)Fecha : = St r ToDate( ' 10/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf Week( Fecha ) ) ) ; / / devuel ve 6 (Vi ernes)Fecha : = St r ToDate( ' 23/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf Week( Fecha ) ) ) ; / / devuel ve 5 ( J ueves)

function DayOfTheYear( const AValue: TDateTime ): Word;

Al pasarle una fecha nos devuelve el número de día que corresponde a lo largodel año. Por ejemplo:

Fecha : = St r ToDat e( ' 5/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf TheYear ( Fecha ) ) ) ; / / devuel ve 217

Page 158: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 158/306

Fecha : = St r ToDate( ' 10/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf TheYear ( Fecha ) ) ) ; / / devuel ve 222Fecha : = St r ToDate( ' 23/ 08/ 2007' ) ;ShowMessage( I nt ToSt r ( DayOf TheYear ( Fecha ) ) ) ; / / devuel ve 235

function DaysBetween( const ANow, AThen: TDateTime ): Integer;

Devuelve el número de días que hay entre las fechas ANow y AThen. Porejemplo:

var  Fecha1, Fecha2: TDat e;begi n  Fecha1 : = St r ToDate( ' 10/ 08/ 2007' ) ;  Fecha2 : = St r ToDate( ' 23/ 08/ 2007' ) ;  ShowMessage( I nt ToSt r ( DaysBetween( Fecha1, Fecha2 ) ) ) ; / /devuel ve 13end;

function DaysInMonth( const AValue: TDateTime ): Word;

Nos dice cuantos días tiene el mes que le pasamos como fecha. Por ejemplo:

DaysI nMont h( St r ToDate( ' 01/ 01/ 2007' ) ) devuel ve 31DaysI nMont h( St r ToDate( ' 01/ 02/ 2007' ) ) devuel ve 28DaysI nMont h( St r ToDate( ' 01/ 04/ 2007' ) ) devuel ve 30

function DaysInYear( const AValue: TDateTime ): Word;

Nos dice el número de días que tiene un año según la fecha que le pasamos.Por ejemplo:

DaysI nYear ( St r ToDate( ' 01/ 01/ 2007' ) ) devuel ve 365

function DaySpan( const ANow, AThen: TDateTime): Double;

Devuelve el número de días de diferencia entre dos fechas y horas incluyendola parte fraccional. Por ejemplo:

var  FechaHor a1, FechaHora2: TDat eTi me;

begi n  FechaHora1 : = St r ToDat eTi me( ' 10/ 08/ 2007 13: 00' ) ;  FechaHora2 : = St r ToDat eTi me( ' 23/ 08/ 2007 19: 00' ) ;  ShowMessage( Fl oatToSt r ( DaySpan( FechaHora1, FechaHora2 ) ) ) ; / /devuel ve 13, 25end;

procedure DecodeDate( Date: TDateTime; var Year, Month, Day: Word );

Este procedimiento convierte un valor de fecha TDate en valores enteros parael año, mes y día. Por ejemplo:

var  wAnyo, wMes, wDi a: Wor d;begi n

Page 159: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 159/306

  DecodeDat e( St r ToDate( ' 23/ 08/ 2007' ) , wAnyo, wMes, wDi a ) ;  Memo. Li nes. Add( ' Dí a: ' + I nt ToSt r ( wDi a ) ) ;  Memo. Li nes. Add( ' Mes: ' + I nt ToSt r ( wMes ) ) ;  Memo. Li nes. Add( ' Año: ' + I nt ToSt r ( wAnyo ) ) ;end;

Al ejecutarlo mostraría en el campo memo:Dí a: 23Mes: 8Año: 2007

procedure DecodeTime( Time: TDateTime; var Hour, Min, Sec, MSec: Word);

Este procedimiento convierte un valor de hora TTime en valores enteros parala hora, minutos, segundos y milisegundos. Por ejemplo:

var  wHora, wMi nut os, wSegundos, wMi l i segundos: Word;begi n  DecodeTi me( St r ToTi me( ' 14: 25: 37' ) , wHora, wMi nut os, wSegundos,wMi l i segundos ) ;  Memo. Li nes. Add( I nt ToSt r ( wHora ) + ' horas' ) ;  Memo. Li nes. Add( I nt ToSt r ( wMi nut os ) + ' mi nut os' ) ;

  Memo. Li nes. Add( I nt ToSt r ( wSegundos ) + ' segundos' ) ;  Memo. Li nes. Add( I nt ToSt r ( wMi l i segundos ) + ' mi l i segundos' ) ;end;

Al ejecutar este código mostraría:

14 hor as25 mi nutos37 segundos0 mi l i segundos

En el siguiente artículo seguiremos con más funciones para fecha y hora.

Pruebas realizadas en Delphi 7.

Funciones y procedimientos para fecha yhora (II)Vamos a seguir viendo funciones para fecha y hora:

procedure DecodeDateTime( const AValue: TDateTime; out AYear, AMonth,ADay, AHour, AMinute, ASecond, AMilliSecond: Word );

Este procedimiento decodifica un valor TDateTime en valores enteros para elaño, mes, día, hora, minutos, segundos y milisegundos. Por ejemplo:

Page 160: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 160/306

var  wAnyo, wMes, wDi a: Wor d;  wHora, wMi nut os, wSegundos, wMi l i segundos: Word;begi n  DecodeDat eTi me( St r ToDat eTi me( ' 24/ 08/ 2007 12: 34: 53' ) , wAnyo, wMes,

wDi a, wHor a, wMi nut os, wSegundos, wMi l i segundos ) ;  Memo. Li nes. Add( ' Dí a: ' + I nt ToSt r ( wDi a ) ) ;  Memo. Li nes. Add( ' Mes: ' + I nt ToSt r ( wMes ) ) ;  Memo. Li nes. Add( ' Año: ' + I nt ToSt r ( wAnyo ) ) ;  Memo. Li nes. Add( I nt ToSt r ( wHora ) + ' horas' ) ;  Memo. Li nes. Add( I nt ToSt r ( wMi nut os ) + ' mi nut os' ) ;  Memo. Li nes. Add( I nt ToSt r ( wSegundos ) + ' segundos' ) ;  Memo. Li nes. Add( I nt ToSt r ( wMi l i segundos ) + ' mi l i segundos' ) ;end;

Al ejecutarlo mostraría:

Dí a: 24

Mes: 8Año: 200712 hor as34 mi nutos53 segundos0 mi l i segundos

function EncodeDate( Year, Month, Day: Word ): TDateTime;

Convierte los valores enteros de año, mes y día a un valor TDateTime oTDate. Por ejemplo:

EncodeDat e( 2007, 8, 24 ) devuel ve 24/ 08/ 2007

function EncodeTime( Hour, Min, Sec, MSec: Word ): TDateTime;

Convierte los valores enteros de hora, minutos, segundos y milisegundos en unvalor TDateTime o TTime. Por ejemplo:

EncodeTi me( 12, 34, 47, 0 ) devuel ve 12: 34: 47

function EncodeDateTime( const AYear, AMonth, ADay, AHour, AMinute,ASecond, AMilliSecond: Word ):TDateTime;

Convierte los valores enteros de año, mes, día, hora, minutos, segundos ymilisegundos en un valor TDateTime. Por ejemplo:

EncodeDat eTi me( 2007, 8, 24, 12, 34, 47, 0 ) devuel ve 24/ 08/ 200712: 34: 47

function EndOfADay( const AYear, ADayOfYear: Word ): TDateTime;overload;function EndOfADay( const AYear, AMonth, ADay: Word ): TDateTime;

overload;

Page 161: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 161/306

Devuelve un valor TDateTime que representa la última fecha y hora de un díaen concreto. Por ejemplo:

EndOf ADay( 2007, 8, 13 ) devuel ve 13/ 08/ 2007 23: 59: 59EndOf ADay( 2007, 1, 1 ) devuel ve 01/ 01/ 2007 23: 59: 59EndOf ADay( 2007, 2, 1 ) devuel ve 01/ 02/ 2007 23: 59: 59

EndOf ADay( 2007, 8, 23 ) devuel ve 08/ 23/ 2007 23: 59: 59EndOf ADay( 2007, 1 ) devuel ve 01/ 01/ 2007 23: 59: 59EndOf ADay( 2007, 32 ) devuel ve 01/ 02/ 2007 23: 59: 59

NOTA IMPORTANTE: La función con tres parámetros es erronea en la versiónde Delphi 7 Build 4.453. Da los siguientes valores:

EndOf ADay( 2007, 8, 13 ) devuel ve 12/ 09/ 2007 23: 59: 59 ( mal )EndOf ADay( 2007, 1, 1 ) devuel ve 31/ 01/ 2007 23: 59: 59 ( mal )EndOf ADay( 2007, 2, 1 ) devuel ve 28/ 02/ 2007 23: 59: 59 ( mal )EndOf ADay( 2007, 8, 23 ) devuel ve 12/ 09/ 2007 23: 59: 59 ( mal )EndOf ADay( 2007, 1 ) devuel ve 01/ 01/ 2007 23: 59: 59 ( bi en)

EndOf ADay( 2007, 32 ) devuel ve 01/ 02/ 2007 23: 59: 59 ( bi en)

Con lo cual es recomendable actualizarse a una versión superior (yo me heactualizado a la versión Delphi 7.0 Build 8.1 y sigue sin hacerme ni puto caso).

function EndOfAMonth( const AYear, AMonth: Word ): TDateTime;

Devuelve un valor TDateTime que representa la última fecha y hora de unmes. Por ejemplo:

EndOf AMont h( 2007, 1 ) devuel ve 31/ 01/ 2007 23: 59: 59EndOf AMont h( 2007, 2 ) devuel ve 28/ 02/ 2007 23: 59: 59EndOf AMont h( 2007, 8 ) devuel ve 31/ 08/ 2007 23: 59: 59

function EndOfAWeek( const AYear, AWeekOfYear: Word; constADayOfWeek: Word = 7 ): TDateTime;

Devuelve un valor TDateTime que representa la última fecha y hora de unasemana. El parámetro AWeekOfYear representa el número de semana a lolargo del año. Por ejemplo:

EndOf AWeek( 2007, 1 ) devuel ve 07/ 01/ 2007 23: 59: 59EndOf AWeek( 2007, 2 ) devuel ve 14/ 01/ 2007 23: 59: 59

EndOf AWeek( 2007, 3 ) devuel ve 21/ 01/ 2007 23: 59: 59

function EndOfAYear( const AYear ): TDateTime;

Devuelve un valor TDateTime que representa la última fecha y hora del añoque le pasemos. Por ejemplo:

EndOf AYear ( 2007 ) devuel ve 31/ 12/ 2007 23: 59: 59EndOf AYear ( 2008 ) devuel ve 31/ 12/ 2008 23: 59: 59EndOf AYear ( 2009 ) devuel ve 31/ 12/ 2009 23: 59: 59

function IncDay( const AValue: TDateTime; const ANumberOfDays: Integer= 1 ): TDateTime;

Page 162: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 162/306

Devuelve el valor fecha y hora AValue incrementando el número de díasespecificado en ANumberOfDays (por defecto 1). Por ejemplo:

I ncDay( St r ToDat e( ' 24/ 08/ 2007' ) ) devuel ve 25/ 08/ 2007I ncDay( St r ToDat e( ' 24/ 08/ 2007' ) , 10 ) devuel ve 03/ 09/ 2007

function IncMonth( const Date: TDateTime; NumberOfMonths: Integer = 1): TDateTime;

Devuelve el valor fecha y hora AValue incrementando el número de mesesespecificado en NumberOfMonths (por defecto 1). Por ejemplo:

I ncMont h( St r ToDat e( ' 24/ 08/ 2007' ) ) devuel ve 24/ 09/ 2007I ncMont h( St r ToDat e( ' 24/ 08/ 2007' ) , 3 ) devuel ve 24/ 11/ 2007

procedure IncAMonth( var Year, Month, Day: Word; NumberOfMonths:Integer = 1 );

Este procedimiento es similar a la función IncMonth pero en vez de pasarle lafecha en formato TDateTime se le pasa en formato año, mes y día. Porejemplo:

var  wAnyo, wMes, wDi a: Wor d;begi n  wDi a : = 24;  wMes : = 8;  wAnyo : = 2007;  I ncAMonth( wAnyo, wMes, wDi a ) ;

  Memo. Li nes. Add( ' Dí a: ' + I nt ToSt r ( wDi a ) ) ;  Memo. Li nes. Add( ' Mes: ' + I nt ToSt r ( wMes ) ) ;  Memo. Li nes. Add( ' Año: ' + I nt ToSt r ( wAnyo ) ) ;end;

Al ejecutarlo obtenemos:

Dí a: 24Mes: 9Año: 2007Dí a: 1Mes: 4Año: 2007

function IncWeek( const AValue: TDateTime; const ANumberOfWeeks:Integer = 1 ): TDateTime;

Devuelve el valor fecha y hora AValue incrementando el número de semanasespecificado en ANumberOfWeeks (por defecto 1). Por ejemplo:

I ncWeek( St r ToDate( ' 24/ 08/ 2007' ) ) devuel ve 31/ 08/ 2007I ncWeek( St r ToDate( ' 24/ 08/ 2007' ) , 2 ) devuel ve 07/ 09/ 2007

function IncYear( const AValue: TDateTime; const ANumberOfYears:

Integer = 1 ): TDateTime;

Page 163: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 163/306

Devuelve el valor fecha y hora AValue incrementando el número de añosespecificado en ANumberOfYears (por defecto 1). Por ejemplo:

I ncYear( St r ToDat e( ' 24/ 08/ 2007' ) ) devuel ve 24/ 08/ 2008I ncYear( St r ToDat e( ' 24/ 08/ 2007' ) , 2 ) devuel ve 24/ 08/ 2009

En el próximo artículo veremos más funciones interesantes.

Pruebas realizadas en Delphi 7.

Funciones y procedimientos para fecha yhora (III)Sigamos con las funciones para el tratamiento de TTime, TDate y TDateTime:

function IsLeapYear( Year: Word ): Boolean;

Esta función nos dice si un año es bisiesto. Por ejemplo:

I sLeapYear ( 2004 ) devuel ve Tr ueI sLeapYear ( 2006 ) devuel ve Fal seI sLeapYear ( 2007 ) devuel ve Fal seI sLeapYear ( 2008 ) devuel ve Tr ueI sLeapYear ( 2009 ) devuel ve Fal se

function MonthOfTheYear( const AValue: TDateTime ): Word;

Devuelve el número de mes de un año a partir de un valor TDateTime (evitael tener que utilizar DecodeTime). Por ejemplo:

Mont hOf TheYear ( St r ToDate( ' 20/ 01/ 2007' ) ) devuel ve 1Mont hOf TheYear ( St r ToDate( ' 27/ 08/ 2007' ) ) devuel ve 8Mont hOf TheYear ( St r ToDate( ' 31/ 12/ 2007' ) ) devuel ve 12

function Now: TDateTime;

Esta función dos devuelve la fecha y hora actual del sistema (reloj deWindows). Por ejemplo:

Now devuel ve 27/ 08/ 2007 10: 05: 01

function RecodeDate( const AValue: TDateTime; const AYear, AMonth,ADay: Word ): TDateTime;

Esta función modifica la fecha de un valor TDateTime sin afectar a la hora.Por ejemplo:

var  dt : TDat eTi me;

Page 164: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 164/306

begi n  dt : = St r ToDateTi me( ' 27/ 08/ 2007 15: 21: 43' ) ;  ShowMessage( ' Fecha y hora ant es de modi f i car : ' +DateTi meToSt r ( dt ) ) ;  dt : = RecodeDate( dt , 2005, 7, 26 ) ;  ShowMessage( ' Fecha y hor a después de modi f i cadar : ' +

DateTi meToSt r ( dt ) ) ;end;

Al ejecutarlo muestra:

Fecha y hor a ant es de modi f i car : 27/ 08/ 2007 15: 21: 43Fecha y hor a después de modi f i car : 26/ 07/ 2005 15: 21: 43

function RecodeTime( const AValue: TDateTime; const AHour, AMinute,ASecond, AMilliSecond: Word ): TDateTime;

Modifica la hora de un valor TDateTime sin afectar a la fecha. Por ejemplo:

var  dt : TDat eTi me;begi n  dt : = St r ToDateTi me( ' 27/ 08/ 2007 15: 21: 43' ) ;  ShowMessage( ' Fecha y hora ant es de modi f i car : ' + DateTi meToSt r (d t ) ) ;  dt : = RecodeTi me( dt , 16, 33, 14, 0 ) ;  ShowMessage( ' Fecha y hora después de modi f i car : ' + DateTi meToSt r (d t ) ) ;end;

El resultado sería:

Fecha y hor a ant es de modi f i car : 27/ 08/ 2007 15: 21: 43Fecha y hor a después de modi f i car : 27/ 08/ 2007 16: 33: 14

procedure ReplaceDate( var DateTime: TDateTime; const NewDate:TDateTime );

Este procedimiento modifica la fecha de una variable TDateTime sin afectar ala hora. Por ejemplo:

var  dt : TDat eTi me;begi n  dt : = St r ToDateTi me( ' 27/ 08/ 2007 18: 15: 31' ) ;  ShowMessage( ' Fecha y hora ant es de modi f i car : ' + DateTi meToSt r (d t ) ) ;  Repl aceDat e( dt , St r ToDat e( ' 01/ 09/ 2007' ) ) ;  ShowMessage( ' Fecha y hora después de modi f i car : ' + DateTi meToSt r (d t ) ) ;end;

El resultado sería:

Page 165: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 165/306

Fecha y hor a ant es de modi f i car : 27/ 08/ 2007 18: 15: 31Fecha y hor a después de modi f i car : 01/ 09/ 2007 18: 15: 31

procedure ReplaceTime( var DateTime: TDateTime; const NewTime:TDateTime );

Modifica la hora de una variable TDateTime sin afectar a la fecha. Porejemplo:

var  dt : TDat eTi me;begi n  dt : = St r ToDateTi me( ' 27/ 08/ 2007 19: 33: 22' ) ;  ShowMessage( ' Fecha y hora ant es de modi f i car : ' + DateTi meToSt r (d t ) ) ;  Repl aceTi me( dt , St r ToTi me( ' 14: 21: 05' ) ) ;

  ShowMessage( ' Fecha y hora después de modi f i car : ' + DateTi meToSt r (d t ) ) ;end;

El resultado sería:

Fecha y hor a ant es de modi f i car : 27/ 08/ 2007 19: 33: 22Fecha y hor a después de modi f i car : 27/ 08/ 2007 14: 21: 05

function Time: TDateTime;function GetTime: TDateTime;

Estas dos funciones devuelven la hora actual en formato TDateTime. Porejemplo:

ShowMessage( ' Ti me devuel ve ' + Ti meToSt r ( Ti me ) ) ;ShowMessage( ' Get Ti me devuel ve ' + Ti meToSt r ( Get Ti me ) ) ;ShowMessage( ' Ti me devuel ve ' + Dat eTi meToSt r ( Ti me ) ) ;ShowMessage( ' Get Ti me devuel ve ' + Dat eTi meToStr ( Get Ti me ) ) ;

Al ejecutarlo muestra:

 Ti me devuel ve 10: 36: 10Get Ti me devuel ve 10: 36: 10

 Ti me devuel ve 30/ 12/ 1899 10: 36: 10Get Ti me devuel ve 30/ 12/ 1899 10: 36: 10

Si os fijais bien cuando mostramos la hora en formato TDateTime vemos quela fecha esta nula (30/12/1899). Mucho cuidado con eso si no quereis que elusuario se quede con cara de flipado, sobre todo al guardar o cargar el valorde un campo de la base de datos.

function Tomorrow: TDateTime;

Nos devuelve la fecha de mañana. Por ejemplo:

Page 166: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 166/306

ShowMessage( ' Dat eToStr ( Tomor r ow ) devuel ve ' + Dat eToSt r ( Tomor r ow )) ;ShowMessage( ' Dat eToStr ( Tomor r ow ) devuel ve ' + Dat eTi meToSt r (

 Tomor r ow ) ) ;ShowMessage( ' Dat eToStr ( Tomor r ow ) devuel ve ' + Ti meToSt r ( Tomor r ow )

) ;

Nos mostraría:

Dat eToSt r ( Tomor r ow ) devuel ve 28/ 08/ 2007Dat eToSt r ( Tomor r ow ) devuel ve 28/ 08/ 2007Dat eToSt r ( Tomorr ow ) devuel ve 0: 00: 00

Como vemos en el ejemplo sólo nos da el día de mañana, pero no la hora(0:00:00).

function Yesterday: TDateTime;

Nos devuelve el día de ayer en formato TDateTime. Por ejemplo:

DateToSt r ( Yest erday ) devuel ve 26/ 08/ 2007

Las siguientes funciones ya las hemos visto anteriormente:

function DateToStr( Date: TDateTime ): string; overload;function TimeToStr( Time: TDateTime ): string; overload;function DateTimeToStr( DateTime: TDateTime ): string; overload;function StrToDate( const S: string ): TDateTime; overload;

function StrToTime( const S: string ): TDateTime; overload;function StrToDateTime( const S: string ): TDateTime; overload;

Pruebas realizadas en Delphi 7.

Implementando interfaces en Delphi (I)Delphi es un lenguaje que utiliza la herencia simple al contrario de C++ quepermite herencia múltiple. Esto significa que cualquier clase sólo puedeheredar de una clase padre. Por lo tanto, si queremos que una clase herede

métodos de más de una clase entonces hay que utilizar interfaces (interface).

Una interfaz es como una clase que contiene sólo métodos abstractos(métodos sin implentación) definiendo limpiamente su funcionalidad. Porconvención, los nombres de las interfaces comienzan con la letra mayúscula I.

Veamos un ejemplo de definición de interfaz para un artículo:

type  I Ar t i cul o = i nt er f ace  pr ocedur e I ncr ement ar Exi st enci as( r Cant i dad: Real ) ;  pr ocedur e Decr ement arExi st enci as( r Cant i dad: Real ) ;

Page 167: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 167/306

  f unct i on Facturar: I nt eger ;  end;

Las interfaces nunca pueden ser instanciadas. Es decir, no se puede hacer:

var

  Ar t i cul o: I Ar t i cul o;begi n  Ar t i cul o : = I Ar t i cul o. Cr eat e;end;

Al compilar nos daría el error:

Object or class typed required (se requiere una clase u objeto)

Para utilizar una interfaz necesitamos implementarla a través de una clase. Seharía de la siguiente manera:

type  TAr t i cul o = cl ass( TI nt er f acedObj ect, I Ar t i cul o )  pr ocedur e I ncr ement ar Exi st enci as( r Cant i dad: Real ) ;  pr ocedur e Decr ement arExi st enci as( r Cant i dad: Real ) ;  f unct i on Facturar: I nt eger ;

¿Que ventajas aporta esto respecto a una clase abstracta? Pues en este caso ladefinición es más limpia, nada más. Ahora veremos que ventaja de utilizarinterfaces.

UTILIZANDO EL POLIMORFIRMO PARA LA CREACION DE CLASES

Las clases polimórficas son aquellas clases que teniendo una definición comúnse pueden utilizar para distintos tipos de objeto. Supongamos que tenemos lasiguiente interfaz:

I Vehi cul o = i nt er f ace  pr ocedur e Matr i cul ar ;end;

Utilizando la misma interfaz vamos a definir una clase para cada tipo devehículo:

type  TCoche = cl ass( TI nt erf acedObj ect , I Vehi cul o )  pr ocedur e Mat r i cul ar;  end;

  TCami on = cl ass( TI nt er f acedObj ect , I Vehi cul o )  pr ocedur e Mat r i cul ar;  end;

Ahora instanciamos ambas clases utilizando la misma interfaz:

var

  Vehi cul o: I Vehi cul o;begi n  Vehi cul o : = TCoche. Cr eat e;

Page 168: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 168/306

  Vehi cul o. Mat r i cul ar ;  Vehi cul o : = TCami on. Cr eat e;  Vehi cul o. Mat r i cul ar ;end;

En este caso todas las clases que implementan la interfaz IVehiculo tienen un

método común pero podría darse el caso de que tuvieran métodos distintos, locual significa que hay que utilizar la herencia múltiple.

UTILIZANDO HERENCIA MULTIPLE MEDIANTE INTERFACES

Supongamos que tenemos estas dos interfaces:

type  I Vehi cul o = i nt er f ace  pr ocedur e Mat r i cul ar;  end;

  I Remol que = i nt er f ace  procedur e Cargar ;

  end;

El vehículo tiene el método Matricular y el remolque tiene el método Cargar.A mi me interesa que un coche utilice el método Matricular, pero sólo elcamión tiene que poder Cargar. Entonces la implementación de las clases deharía así:

  TCoche = cl ass( TI nt erf acedObj ect , I Vehi cul o )  pr ocedur e Mat r i cul ar;  end;

  TCami on = cl ass( TI nt erf acedObj ect , I Vehi cul o, I Remol que )  pr ocedur e Mat r i cul ar;  pr ocedure Cargar ;  end;

Como puede apreciarse la clase TCoche sólo implementa la interfaz IVehiculopero la clase TCamion hereda implementa simultáneamente las interfacesIVehiculo y IRemolque pudiendo utilizar sus métodos indistintamente. Esto eslo que se conoce como herencia múltiple.

En el próximo artículo seguiremos viendo más características sobre lasinterfaces.

Pruebas realizadas en Delphi 7.

Implementando interfaces en Delphi (II)Cuando creamos clases para nuestros programas de gestión uno se preguntaque ventajas pueden aportar las interfaces dentro de nuestro programa. Puesuna de las ventajas es que encapsula aún más nuestra clase dejando sóloaccesible los métodos y no las variables internas (ya sean private, protected

o public). Por ejemplo, supongamos que deseo implementar una interfaz yuna clase para el control de mis clientes:

Page 169: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 169/306

type  I Cl i ent e = i nt er f ace  f unct i on Get _I D: I nt eger ;  f unct i on Get_Nombr e: st r i ng;  f unct i on Get _NI F: st r i ng;

  f unct i on Get _Sal do: Real ;  pr ocedur e Set _I D( I D: I nt eger ) ;  pr ocedure Set _Nombr e( sNombre: st r i ng ) ;  pr ocedur e Set _NI F( sNI F: st r i ng ) ;  pr ocedur e Set _Sal do( r Sal do: Real ) ;  end;

  TCl i ent e = cl ass( TI nt er f acedObj ect , I Cl i ent e )  pr i vat e  I D: I nt eger ;  sNombr e, sNI F: st r i ng;  r Sal do: Real ;  publ i c  f unct i on Get _I D: I nt eger ;  f unct i on Get_Nombr e: st r i ng;  f unct i on Get _NI F: st r i ng;  f unct i on Get _Sal do: Real ;  pr ocedur e Set _I D( I D: I nt eger ) ;  pr ocedure Set _Nombr e( sNombre: st r i ng ) ;  pr ocedur e Set _NI F( sNI F: st r i ng ) ;  pr ocedur e Set _Sal do( r Sal do: Real ) ;  end;

Esta clase se puede instanciar de dos formas. Si se hace en una variable declase:

var  Cl i ent e: TCl i ent e;begi n  Cl i ent e : = TCl i ent e. Cr eat e;  Cl i ent e. Set _I D( 1 ) ;  Cl i ent e. Set _Nombre( ' CARLOS MARTI NEZ RUI Z' ) ;  Cl i ent e. Set _NI F( ' 67876453F' ) ;  Cl i ent e. Set _Sal do( 145. 87 ) ;end;

entonces se comporta como una clase normal, permitiendo acceder incluso alas variables privadas cuando en el editor de Delphi ponemos:

Cl i ent e.

y esperamos a que el asistente nos muestre las propiedades y métodos de laclase. Y no sólo eso, sino que además nos muestra todas propiedades ymétodos que hemos heredado de TObject haciendo más engorrosa labúsqueda de variables y métodos.

Pero si implementamos la clase a partir de la interfaz:

var  Cl i ent e: I Cl i ent e;

begi n  Cl i ent e : = TCl i ent e. Cr eat e;

Page 170: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 170/306

  Cl i ent e. Set _I D( 1 ) ;  Cl i ent e. Set _Nombre( ' CARLOS MARTI NEZ RUI Z' ) ;  Cl i ent e. Set _NI F( ' 67876453F' ) ;  Cl i ent e. Set _Sal do( 145. 87 ) ;end;

al teclear en el editor de Delphi:Cl i ent e.

no sólo hemos eliminado las variables privadas de la lista, sino que ademássólo muestra nuestros métodos y los de las interfaz (QueryInterface, _AddRefy _Release). Esto aporta mucha mayor claridad al programador a la hora deinstanciar sus clases, sobre todo cuando tenemos que distribuir nuestralibrería a otros programadores (ellos sólo tienen que fijarse como se utiliza lainterfaz, ni siquiera tienen porque saber como está implementada la clase).

La única desventaja es que hay que crear tantas funciones y procedimientosSet y Get como propiedades tenga nuestra clase. Pero es buenoacostumbrarse a hacerlo así como ocurre con los Beans de Java.

USANDO INTERFACES COMO PARAMETROS EN PROCEDIMIENTOS

Utilizando el polimorfismo mediante interfaces ahora podemos crearprocedimientos genéricos que manejen objetos de distinta clase de unamanera simple. Usando las interfaces IArticulo e IVehiculo definidas enartículo anterior podemos escribir los siguientes procedimientos:

pr ocedur e Factur ar Ar t i cul os( Ar t i cul os: ar r ay of I Ar t i cul o ) ;var  i : I nteger ;begi n  f or i : = Low( Ar t i cul os ) t o Hi gh( Ar t i cul os ) do  Ar t i cul os[ i ] . Facturar ;end;

pr ocedur e Mat r i cul ar Vehi cul os( Vehi cul os: ar r ay of I Vehi cul o ) ;var  i : I nteger ;begi n  f or i : = Low( Vehi cul os ) t o Hi gh( Vehi cul os ) do

  Vehi cul os[ i ] . Mat r i cul ar ;end;

El procedimiento FacturarArticulos no tiene porque saber como se facturainternamente cada artículo. Lo mismo ocurre con el procedimientoMatricularVehiculos, ya sea de la clase TCoche o TCamion ya se encargaráinternamente el objeto de llamar a su método correspondiente,abstrayéndonos a nosotros de como funciona internamente cada clase.

LA INTERFAZ IINTERFACE

Al igual que todos los objetos heredan directa o indirectamente de TObject,todas las interfaces heredan de la interfaz IInterface. Esta interfaz incorpora

Page 171: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 171/306

el método QueryInterface el cual es muy útil para descubrir y usar otrasinterfaces que implementan el mismo objeto.

Para contar el número de referencias introduce también los métodos _AddRefy _Release. El compilador de Delphi automáticamente proporciona llamadas a

estos métodos cuando las interfaces son utilizadas. Para no tener queimplementar nosotros a mano estos métodos (ya que una interfaz nos obliga aimplementar todos sus métodos), para ello heredamos de la claseTInterfaceObject que proporciona una implementación base para interfaces.Heredar de TInterfaceObject no es obligatorio pero muy útil.

La clase TInterfacedObject está declarada en la unidad System de lasiguiente manera:

type  TI nt er f acedObj ect = cl ass ( TObj ect , I I nt er f ace )  prot ect ed  FRef Count : I nt eger ;  f unct i on Quer yI nt er f ace( const I I D: TGUI D; out Obj ) : HResul t ;s tdcal l ;  f unct i on _AddRef : I nt eger ; st dcal l ;  f uncti on _Rel ease: I nt eger ; stdcal l ;  publ i c  pr ocedur e Af t erConst r uct i on; over r i de;  pr ocedur e Bef oreDest r uct i on; over r i de;  cl ass f unct i on NewI nst ance: TObj ect ; over r i de;  pr oper t y Ref Count : I nt eger; r ead FRef Count ;  end;

Por ello en los ejemplos que he mostrado heredo de esa clase aparte de lainterfaz:

type  TCl i ent e = cl ass( TI nt er f acedObj ect , I Cl i ent e )  . . .

Como puede apreciarse la clase TInterfacedObject hereda de la claseTObject y de la interfaz IUnknown. La interfaz IUnknown es utilizada paracrear objetos COM.

En el próximo artículo terminaremos de ver las características másimportantes de las interfaces.

Pruebas realizadas en Delphi 7.

Implementando interfaces en Delphi (III)Una de las propiedades más interesantes que incorporan las interfaces es lade añadir un identificador único que la diferencie del resto.

IDENTIFICACION DE INTERFACES

Una interfaz puede tener un identificador unico a nivel global llamado GUID.Este identificador tiene la siguiente forma:

Page 172: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 172/306

['{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}']

donde cada X es un dígito hexadecimal. Cada GUID es un valor binario de 16bytes que hace que cada interfaz sea única. Los tipos TGUID y PGUID están

declarados dentro de la unidad System del siguiente modo:

type  PGUI D = T̂GUI D;  TGUI D = r ecor d  D1: I nt eger;  D2: Wor d;  D3: Wor d;  D4: ar r ay[ 0. . 7] of Byt e;

Este sería un ejemplo de declaración de interfaz con GUID:

type  I Fact ur a = i nt er f ace  [ ' {78EAC667- ED60- 443B- B84B- 5D7AF161B28B}' ]  pr ocedur e Cal cul ar ;  end;

¿De donde sacamos ese código? Pues cuando en el editor de Delphi se pulsa lacombinación de teclas CTRL + MAYÚSCULAS + G entonces nos genera unaGUID aleatoria.

CREANDO PROPIEDADES EN LA INTERFAZPese que la declarión de interfaces es similar a la de las clases hay que tenervarias cosas presentes:

- No permiten declarar variables internas.

- No pueden ser instanciadas.

- No se permite especificar la visibilidad de los métodos (private, protected opublic).

- Las propiedades no pueden apuntar a variables sino sólo a métodos.

Entonces, ¿como podemos definir propiedades? Hay que definirlas utilizandométodos en vez de variables cuando se definen los parámetros read y writede la propiedad. Sería de este modo:

type  I Al bar an = i nt er f ace  f unct i on Get_Numero: I nt eger ;  f unct i on Get _I mpor t e: Real ;  pr ocedure Set _Numero( i Numero: I nt eger ) ;

  pr ocedur e Set _I mpor t e( r I mpor t e: Real ) ;  pr ocedur e I mpr i mi r ;

Page 173: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 173/306

  proper t y Numer o: I nteger r ead Get _Numero wr i t e Set _Numero;  pr oper t y I mpor t e: Real r ead Get_I mport e wr i t e Set _I mport e;  end;

y su implementación a través de la clase:

  TAl bar an = cl ass( TI nt er f acedObj ect , I Al bar an )  pr i vat e  i Numero: I nt eger ;  r I mport e: Real ;

  publ i c  f unct i on Get_Numero: I nt eger ;  f unct i on Get _I mpor t e: Real ;  pr ocedure Set _Numero( i Numero: I nt eger ) ;  pr ocedur e Set _I mpor t e( r I mpor t e: Real ) ;  pr ocedur e I mpr i mi r ;

  proper t y Numer o: I nteger r ead Get _Numero wr i t e Set _Numero;  pr oper t y I mpor t e: Real r ead Get_I mport e wr i t e Set _I mport e;  end;

i mpl ement at i on

{ TAl bar an }

f unct i on TAl baran. Get _I mport e: Real ;begi n  Resul t : = r I mport e;

end;

f unct i on TAl baran. Get_Numero: I nt eger ;begi n  Resul t : = i Numer o;end;

pr ocedur e TAl baran. I mpr i mi r ;begi n  ShowMessage( ' I mpr i mi endo. . . ' ) ;end;

pr ocedur e TAl bar an. Set _I mport e( r I mpor t e: Real ) ;begi n  Sel f . r I mpor t e : = r I mpor t e;

end;pr ocedur e TAl bar an. Set _Numero( i Numero: I nt eger ) ;begi n  Sel f . i Numero : = i Numero;end;

Esto nos permite utilizar nuestra interfaz sin llamar a los procedimientos Set yGet de cada campo:

var  Al bar an: I Al bar an;begi n  Al baran : = TAl baran. Cr eat e;  Al baran. Numer o : = 1;

Page 174: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 174/306

  Al baran. I mport e : = 68. 21;

  ShowMessage( ' Numero=' + I ntToSt r ( Al bar an. Numero ) ) ;  ShowMessage( ' I mport e=' + Fl oat ToSt r ( Al baran. I mport e ) ) ;end;

Con esta comodidad se pueden instanciar clases a partir de interfacesmanteniendo la claridad de código tanto dentro como fuera de laimplementación.

Con esto finalizamos la parte básica de implementación de interfaces enDelphi.

Pruebas realizadas en Delphi 7.

La potencia de los ClientDataSet (I)

El mundo de la programación de bases de datos cambia tan rápidamente quecasi no nos da tiempo a asimilar los nuevos protocolos. Comenzamos conDBase, Clipper, FoxPro, .., hasta hoy en día que tenemos Microsoft SQLServer, Interbase, Firebird, etc.

Luego tenemos el maremagnum de lenguajes de programación donde cadacual se come el acceso a datos a su manera: Java con sus infinitos framesworktales como hibernate, struts, etc., Ruby con el archiconocido Ruby On Railsque utiliza el paradigma MVC (Modelo, Vista, Controlador), Microsoft a su rollocon ADO y su plataforma Microsoft.NET. Todo eso sin contar con los potenteslenguajes script que no tienen la atención que merecen como son PHP, Ruby,

Python, TCL/TK, Groovy, etc. Hasta la mismísima CodeGear nos hasorprendido con su IDE 3rdRails para programación en Ruby On Rails.

Pero si hay algo que hace que Delphi destaque sobre el resto de entornos deprogramación es su acceso a múltiples motores de bases de datos utilizandouna misma lógica de negocio que abstrae al programador de las rutinas a bajonivel. En la conocida tecnología de acceso a datos llamada MIDAS.

Las primeras versiones de Delphi contaban con el veterano controlador debases de datos BDE (Borland Database Engine) que permitía acceder a lasclásicas bases de datos DBASE, PARADOX, etc. En la versión 5 de Delphi seincluyeron los componentes IBExpres (IBX) que permitían tener un accesodirecto a bases de datos Interbase y Firebird. Fue a partir de Delphi 6 cuandoBorland apostó por la tecnología DBExpress, un sistema rápido mediantedrivers que utilizando un mismo protocolo permitía conectividad con múltiplesmotores de bases de datos tales como Interbase, Firebird, Oracle, MySQL,Informix, Microsoft SQL Server, etc. Incluso en su última versión (DBX4)permite acceder a su nuevo motor de bases de datos multiplataforma llamadoBlackFish programado integramente en .NET y Java (anteriormente se llamabaJDataStore y estaba programado en Java).

Luego tenemos también otros componentes muy buenos de acceso a datostales como Zeos, Interbase Objects (IBO), FIBPlus, etc. Y por supuesto el

Page 175: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 175/306

conocido protocolo de acceso a datos de Microsoft llamado ADO, siendo suúltima versión ADO.NET la que tiene bastantes posibilidades de convertirse enun estandar para todos los entornos Windows.

Pero si hay un componente de acceso a bases de datos en Delphi que destaque

sobre todos los demás ese es el ClientDataSet. Combina la facilidad de accesoa datos a través de la clase TDataSet y la potencia de controlarautomáticamente las transacciones al motor de bases de datos, las SQL deconsulta, actualización y eliminación así como la conexión y desconexión delas tablas con el servidor haciendo que el programador no tenga quepreocuparse de las particularidades del motor de bases de datos.

El componente de la clase TClientDataSet no conecta directamente sobre unabase de datos en concreto, si no que utiliza el componente DataSetProviderque actua de intermediario haciendo de puente entre los componentes debases de datos (IBX,IBO,etc) y nuestra tabla ClientDataSet. El componente

ClientDataSet es algo así como una tabla de memoria (como la que tienen loscomponentes RX) que se trae y lleva datos a las tablas de la base de datosencargándose automáticamente de las transacciones.

LA ESTRUCTURA CORRECTA DE UN PROGRAMA

Para crear una buena aplicación con acceso a bases de datos hay que dividirnuestro programa en tres partes principales:

Capa de acceso a datos: se encarga de conectar con un motor de bases de

datos en concreto ya sea con componentes IBX, ADO, BDE, etc.Lógica de negocio: aquí se definen como son nuestras tablas (CLIENTES,ARTICULOS,etc), los campos que contienen así como el comportamiento al darde alta registros, modificarlos, realización de cálculos internos, etc. Todoesto lo haremos con componentes de la clase TClientDataSet yTDataSetProvider.

Interfaz de usuario: se compone de los formularios, informes y menús deopciones que va a visualizar el usuario y que estarán internamente conectadoscon los ClientDataSet.

La interfaz de usuario sólo podrá acceder a la lógica de negocio y esta últimasólo a la capa de acceso a datos. Así, si en un futuro queremos cambiar lainterfaz de usuario (por ejemplo para Windows Vista) o el motor de base dedatos no afectaría al resto de las capas del programa.

Para ello vamos a utilizar los componentes contenedores de la claseTDataModule para alojar la lógica de negocio y el acceso a datos. En nuestroejemplo crearemos una base de datos de clientes definiendo las tres capas.

CREANDO LA BASE DE DATOS

En este ejemplo voy a crear una base de datos de clientes utilizando el motor

Page 176: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 176/306

de bases de datos Firebird 2.0 y con los componentes IBX. Las tablas las voy acrear con el programa IBExpert (http://www.ibexpert.com) cuya versiónpersonal es gratis y muy potente. La base de datos se va a llamarBASEDATOS.FDB, pero si haceis las pruebas con Interbase entonces seríaBASEDATOS.GDB. No voy a explicar aquí como funciona el programa IBExpert

o IBConsole ya que hay en la red información abundate sobre ambosprogramas.

Creamos la tabla de clientes:

CREATE TABLE CLI ENTES (  I D I NTEGER NOT NULL,  NOMBRE VARCHAR(100) ,  NI F VARCHAR( 15) ,  DI RECCI ON VARCHAR( 100) ,

  POBLACI ON VARCHAR( 50) ,  CP VARCHAR( 5) ,  PROVI NCI A VARCHAR( 50) ,  I MPORTEPTE DOUBLE PRECI SI ON,  PRI MARY KEY ( I D))

Como quiero que el ID sea autonumérico voy a crear un generador:

CREATE GENERATOR I DCLI ENTE

Y un disparador para que cuando demos de alta el registro rellene

automáticamente el ID y autoincremente el contador del generador:CREATE TRI GGER CONTADOR_CLI ENTES FOR CLI ENTESACTI VE BEFORE I NSERT POSI TI ON 0ASBEGI N  NEW. I D = GEN_I D( I DCLI ENTE, 1 ) ;END

NOTA IMPORTANTE: Hay algunas versiones de Delphi 7 que tienen un error enlos componentes IBExpress (IBX) que hacen que los componentesClientDataSet no funcionen correctamente. Recomiendo actualizar los

componentes IBX a la versión 7.04 que podeis encontrarla en:http://codecentral.borland.com/Item.aspx?id=18893

En el siguiente artículo comenzaremos a realizar el programa utilizando estabase de datos.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

Page 177: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 177/306

La potencia de los ClientDataSet (II)Después de haber creado la base de datos en Firebird 2.0 ya podemoscomenzar a crear un nuevo proyecto que maneje dicha información. Elobjetivo del proyecto es hacer el siguiente mantenimiento de clientes:

CREANDO LA CAPA DE ACCESO A DATOS

La capa de acceso a datos encargada de conectar con Firebird va a incorporarlos siguientes componentes:

- Un contenedor DataModule llamado AccesoDatos.

- Un compomente IBDatabase (pestaña Interbase) llamado BaseDatos.

- Un componente IBTransaction (pestaña Interbase) llamado Transaccion.

- Dos componentes IBQuery (pestaña Interbase) llamados LstClientes yClientes.

Se sopone que la base de datos BASEDATOS.FDB esta al lado de nuestroejecutable. Vamos a comenzar a configurar cada uno de estos componentes.

Hacemos doble clic sobre el componente IBDatabase y en el campo User

Page 178: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 178/306

Name le ponemos SYSDBA. En en password le ponemos masterkey:

Pulsamos OK , y después en la propiedad DefaultTransaction del componenteIBDatabase seleccionamos Transaccion, desactivamos la propiedadLoginPrompt y nos aseguramos que en SQLDialect ponga un 3.

Para el componente IBTransaction llamado Transaccion seleccionaremos ensu propiedad DefaultDatabase la base de datos BaseDatos.

Como nuestra intención es tener una rejilla que muestre el listado general declientes y un formulario para dar de alta clientes, lo que vamos a hacer escrear una tabla IBQuery para la rejilla llamada LstClientes y otra tabla parael formulario del cliente llamada Clientes. Sería absurdo utilizar un mismomantenimiento para ambos casos ya que si tenemos miles de clientes en larejilla, el tener cargados en memoria todos los campos del cliente podríarelentizar el programa.

En los componentes LstClientes y Clientes vamos a configurar también:

Database: BaseDatosTransaction: TransaccionUniDirectional: True

El motivo de activar el campo UniDirectional es para que el cursor SQLtrabaje más rápido en el servidor ya que los componentes ClientDataSetgestionan en memoria los registros leidos anteriormente.

Como en la rejilla sólo quiero mostrar los campos ID, NOMBRE y NIF entonces

en el componente LstClientes en su propiedad SQL vamos a definir:

Page 179: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 179/306

SELECT I D, NOMBRE, NI F FROM CLI ENTESORDER BY I D DESC

y para el componente Clientes:

SELECT * FROM CLI ENTESWHERE I D=: I D

En la condición WHERE de la SQL hemos añadido el parámetro :ID para que sepueda más adelante acceder directamente a un registro en concreto a partirde su campo ID.

Una vez definida nuestra capa de acceso a datos en el siguiente artículo nosencargaremos de definir nuestra lógica de negocio.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

La potencia de los ClientDataSet (III)Después de crear la base de datos y la capa de acceso a datos dentro delDataModule vamos a crear la lógica de negocio.

Antes de seguir tenemos que hacer doble clic en los componente IBQuery dela capa de acceso a datos y pulsar la combinación de teclas CTRL + A paraintroducir todos los campos en el módulo de datos. Y en cada una de ellas hayque seleccionar el campo ID y quitar la propiedad Required ya que el propiomotor de bases de datos va a meter el campo ID con un disparador.

Pero tenemos un problema, y es que no hemos seleccionado donde esta labase de datos. Para ello hacemos doble clic en el objeto BaseDatos situadomódulo de datos AccesoDatos. Seleccionamos una base de datos Remota(Remote) con IP 127.0.0.1. Y la base de datos donde la tengamos, porejemplo:

D:\Desarrollo\Delphi7\ClientDataSet\BaseDatos.fdb

De este modo, al hacer CTRL + A sobre las tablas conectará automáticamentesobre la base de datos para traerse los campos. No se os olvide luego

desconectarla.

CREANDO LA LOGICA DE NEGOCIO

La capa de lógica de negocio también la vamos a implementar dentro de unobjeto DataModule y va a constar de los siguientes componentes:

Page 180: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 180/306

- Un DataModule llamado LogicaNegocio.

- Dos componentes DataSetProvider llamados DSPLstClientes y DSPClientes.

- Dos componentes ClientDataSet llamados TLstClientes y TClientes.

Ahora vamos a configurar cada uno de estos componentes:

- Enlazamos el DataModule LogicaNegocio con el DataModule AccesoDatos enla sección uses.

- Al componente DSPLstClientes le asignamos en su propiedad DataSet elcomponente AccesoDatos.LstClientes.

- Al componente DSPClientes le asignamos en su propiedad DataSet elcomponente AccesoDatos.Clientes.

- El componente TLstClientes lo vamos a vincular con DSPLstClientes

mediante su propiedad ProviderName.

- El componente TClientes lo vamos a vincular con DSPClientes mediante supropiedad ProviderName.

- Debemos hacer doble clic en ambos ClientDataSets y pulsar la combinaciónde teclas CTRL + A para meter todos los campos.

CONTROLANDO EL NUMERO DE REGISTROS CARGADOS EN MEMORIA

Los componentes ClientDataSet tienen una propiedad llamada PacketRecordla cual determina cuandos registros se van a almacenar en memoria. Pordefecto tiene configurado -1 lo que significa que se van a cargar todos losregistros en la tabla. Como eso no me interesa en el listado general delformulario principal lo que vamos a hacer es poner esta propiedad a 100.

Por eso he ordenado la lista por el campo ID descendentemente para que sevean sólo los últimos 100 registros insertados. Una de las cosas que más megustan de los componentes ClientDataSet es que se trae los 100 últimosregistros y desconecta la tabla y la transacción quitándole trabajo al motor debases de datos. Si el usuario que maneja el programa llega hasta el registro

número 100 el propio componente conecta automáticamente con el servidor,se trae otros 100 registros y vuelve desconectar.

Page 181: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 181/306

Lo único en lo que hay que tener cuidado es no acumular demasiados registrosen memoria ya que puede relentizar el programa e incluso el sistemaoperativo si el PC no tiene mucha potencia.

El componente ClientDataSet llamado TClientes lo dejamos como está en -1ya que sólo lo vamos a utilizar para dar de alta un registro o modificarlo.

ENVIANDO LAS TRANSACCIONES AL SERVIDOR 

Cuando se utilizan los clásicos métodos Insert, Append, Post y Delete con losobjetos de la clase TClientDataSet el resultado de las operaciones conregistros no tiene lugar en la base de datos hasta que envíamos la transacciónal servidor con el método ApplyUpdates.

Por ello vamos a utilizar el evento OnAfterPost para enviar la transacción al

servidor en el caso que haya sucedido alguna modificación en el registro:

pr ocedur e TLogi caNegoci o. TCl i ent esAf t er Post ( Dat aSet : TDataSet ) ;begi n  i f TCl i ent es. ChangeCount > 0 t hen  begi n  TCl i ent es. Appl yUpdates( 0 ) ;  TCl i ent es. Ref r esh;

  i f TLstCl i ent es. Acti ve t hen  TLstCl i ent es. Ref r esh;

  end;end;

Después de enviar la transacción hemos refrescado la tabla TClientes ytambién la tabla TLstClientes para que se actualicen los cambios en la rejilla.Por último, cuando en el listado de clientes se elimine un registro tambiénhay que enviar la trasacción al servidor en su evento OnAfterDelete:

pr ocedur e TLogi caNegoci o. TLst Cl i ent esAf t er Del et e( DataSet : TDat aSet ) ;begi n  TLst Cl i ent es. Appl yUpdat es( 0 ) ;end;

Con esto ya tenemos controlada la inserción, modificación y eliminación deregistros hacia el motor de bases de datos.

ESTABLECIENDO REGLAS DE NEGOCIO EN NUESTRAS TABLAS

Las reglas de negocio definidas en el módulo de datos le quitan mucho trabajoal formulario que esté vinculado con la tabla. En un primer ejemplo vamos ahacer que cuando se demos de alta un cliente su importe pendiente sea cero.Esto se hace en el evento OnNewRecord del componente TClientes:

pr ocedur e TLogi caNegoci o. TCl i ent esNewRecor d( DataSet : TDat aSet ) ;begi n

Page 182: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 182/306

  TCl i ent esI MPORTEPTE. AsFl oat : = 0;end;

Otra de las malas costumbres que solemos cometer en los programas escontrolar los datos que introduce o no el usuario en el registro, cuya lógica lahacemos en el formulario. Esto tiene el inconveniente en que si en otroformulario hay que acceder a la misma tabla hay que volver a controlar lasacciones del usuario.

Para evitar esto, tenemos que definir también en la capa de lógica de negociolas reglas sobre las tablas y los campos, lo que se llama comunmentevalidación de campos. El componente ClientDataSet dispone de la propiedadConstraints donde pueden definirse tantas reglas como queramos. Para verlocon un ejemplo, vamos a hacer que el usuario no pueda guardar el cliente sino ha introducido su nombre.

Para definir una regla en un ClientDataSet hay que hacer lo siguiente (conTClientes):

- Pulsamos el botón [...] en la propiedad Constraints.

- Pulsamos el botón Add New.

- En la propiedad CustomConstraint definimos la condición de error medianteSQL:

NOMBRE I S NOT NULL

- En el campo ErrorMessage del mismo Constraint ponemos el mensaje deerror:

No ha i nt r oduci do el nombr e del cl i ent e

Con esta regla definida, si en cualquier parte de nuestro programa hacemosun Post de la tabla clientes y esta vacío el campo NOMBRE el programalanzará un mensaje de error sin tener que programar nada. Antes teníamosque hacer esto en el botón Aceptar del formulario para validar los campos.Con los Constraints las validadiones las hacemos en sin tener que programar.

Ahora vamos a definir otra regla la cual establece que un cliene no puedetener un importe pendiente superior a 2000 €. Creamos un nuevo Constraintcon la propiedad CustomConstrait definida con:

I MPORTEPTE <= 2000

y con la propiedad ErrorMessage que tenga:

El i mpor t e pendi ent e no puede ser super i or a 2000 €

Se pueden definir tantas reglas como deseemos. En el próximo artículo vamosa hacer los formularios de mantenimiento de clientes.

Page 183: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 183/306

La potencia de los ClientDataSet (IV)Ya tenemos todo lo necesario para realizar el programa:

- La base de datos firebird: BASEDATOS.FDB

- La capa de acceso a datos en el módulo de datos: AccesoDatos.

- La lógica de negocio en el módulo de datos: LogicaNegocio.

CREANDO EL FICHERO GENERAL DE CLIENTES

El primer formulario que vamos a crear se va a llamar FClientes y va acontener el listado general de clientes:

Va a contener los siguientes componentes:

- 4 botones de la clase TButton para dar de alta clientes, modificarlos yeliminarlos. Habrá otro llamado BCompletar que utilizaremos más adelantepara dar de alta clientes de forma masiva.

- Una rejilla de datos de la clase TDBGrid que va a contener 3 columnas parael listado del cliente: ID, NOMBRE y NIF. Su nombre va a ser ListadoClientes.

- Un componente DataSource (pestaña Data Access) llamado DSLstClientesque va a encargarse de suministrar datos a la rejilla.

- Un componente DBNavitator (pestaña Data Controls) para hacer pruebascon los registros. Lo llamaremos Navegador.

- Una barra de progreso a la izquierda del DBNavigator de la claseTProgressBar que mostrará el progreso de los clientes dados de altaautomáticamente a través del botón BCompletar.

Ahora hay que vincular los componentes como corresponde:

Page 184: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 184/306

- Lo primero es añadir en la sección uses el módulo de datos LogicaNegociopara poder vincular el listado de clientes a la rejilla.

- Vinculamos el componente DSLstClientes con el ClientDataSet llamadoTLstClientes a través de su propiedad DataSet.

- En la propiedad DataSource de la rejilla de datos ListadoClientes asignamosel componente DSLstClientes.

- Vinculamos el componente Navegador al componente DSLstClientesmediante su propiedad DataSource.

Más adelante escribiremos código para cada uno de los botones.

CREANDO EL FORMULARIO DEL CLIENTE

Vamos a crear el siguiente formulario llamado FCliente:

Va a constar de tantos componentes de la clase TLabel y TDBEdit comocampos tenga la tabla clientes. Todos los campos son modificables aexcepción del ID que lo he puesto con su propiedad Enabled a False. Tambiénlo he oscurecido podiendo de Color el valor clBtnFace.

Para vincular los campos a la tabla real de clientes introducimos uncomponente DataSource llamado DSClientes, pero antes hay que añadir en la

sección uses el módulo de datos LogicaNegocio.

Ahora se vincula el componente DSClientes con el ClientDataSet llamadoTClientes situado en el módulo de datos LogicaNegocio a través de supropiedad Dataset. Con esto ya podemos vincular cada campo TDBEdit con elDataSource DSClientes y con su campo correspondiente especificado en lapropiedad DataField.

Cuando se pulse el botón Aceptar ejecutamos el código:

pr ocedur e TFCl i ent e. BAcept arCl i ck( Sender: TObj ect ) ;begi n

Page 185: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 185/306

  Logi caNegoci o. TCl i ent es. Post ;  Modal Resul t : = mr Ok;end;

Al botón BAceptar le ponemos en su propiedad ModalResult el valor mrNone.Esto tiene su explicación. Cuando pulsemos Aceptar, si el usuario no harellenado correctamente algún dato saltará un error definido en losConstraint y no hará nada, evitamos que se cierre el formulario. Asíobligamos al usuario a introducir los datos correctamente. Sólo cuando el Postse realice correctamente le diremos al formulario que el resultado es mrOkpara que pueda cerrarse.

En el botón BCancelar con sólo asignar en su propiedad ModalResult el valormrCancel no será necesario hacer nada más.

Sólo una cosa más: si utilizais el teclado numérico en los campos de tipo Float

vereis que la tecla decimal no funciona (aquí en España). Nos obliga a utilizarla coma que está encima de la barra de espacio. Para transformar el punto encoma yo lo que suelo hacer es lo siguiente (para el campo IMPORTEPTE):

pr ocedur e TFCl i ent e. I MPORTEPTEKeyPress( Sender : TObj ect ; var Key: Char) ;begi n  i f key = ' . ' t hen  key : = ' , ' ;end;

Es decir, en el evento OnKeyPress cuando el usuario pulse el punto lo

transformo en una coma. Si tuvieramos más campos float sólo habría quereasignar el mismo eventos al resto de campos.

Con esto ya tenemos terminado el formulario del cliente.

TERMINANDO EL FICHERO GENERAL DE CLIENTES

Una vez que tenemos la ficha del cliente vamos a terminar el mantenimientode clientes asignado el código a cada botón. Comencemos con el botónNuevo:

pr ocedur e TFCl i ent es. BNuevoCl i ck( Sender : TObj ect ) ;begi n  Logi caNegoci o. TCl i ent es. Open;  Logi caNegoci o. TCl i ent es. I nser t ;  Appl i cat i on. Cr eateFor m( TFCl i ent e, FCl i ent e ) ;  FCl i ent e. ShowModal ;  Logi caNegoci o. TCl i ent es. Cl ose;end;

Como puede apreciarse abrimos la tabla TClientes sólo cuando hay que dar dealta un cliente o modificarlo. Después la cerramos ya que para ver el listadode clientes tenemos nuestra tabla TLstClientes.

Para el botón Modificar es muy parecido:

Page 186: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 186/306

Page 187: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 187/306

Antes de eliminar el registro nos aseguramos que de tenga algún dato ypreguntamos al usuario si esta seguro de eliminarlo.

Con esto finalizamos nuestro mantenimiento de clientes a falta de utilizar el

botón Completar donde crearemos un bucle que dará de alta tantos clientescomo deseemos para probar la velocidad de la base de datos. Esto lo haremosen el próximo artículo.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

La potencia de los ClientDataSet (V)Antes de proceder a crear el código para el botón Completar vamos a hacerque la conexión del programa con la base de datos sea algo más flexible.

Hasta ahora nuestro objeto IBDatabase está conectado directamente a unaruta fija donde tenemos el archivo BASEDATOS.FDB. Pero si cambiamos deruta el programa dejará de funcionar provocando un fallo de conexión.

Para evitar esto vamos a hacer que el programa conecte con la base de datosal mostrar el formulario principal FClientes. Para ello en el evento OnShowde dicho formulario ponemos lo siguiente:

pr ocedur e TFCl i ent es. For mShow( Sender : TObj ect ) ;

begi n  AccesoDat os. BaseDatos. Dat abaseName : = ' 127. 0. 0. 1: ' +Ext r act Fi l ePat h( Appl i cat i on. ExeName ) + ' BASEDATOS. FDB' ;

  t r y  AccesoDat os. BaseDat os. Open;  except  rai se;  end;

  Logi caNegoci o. TLst Cl i ent es. Act i ve : = Tr ue;end;

Esto hace que conecte con la base de datos que está al lado de nuestroejecutable. Así hacemos que nuestro programa sea portable (a falta deinstalarle el motor de bases de datos correspondiente). Si la conexión con labase de datos es correcta entonces abrimos la tabla del listado de clientes(TLstClientes).

Cuando se trata de una base de datos Interbase la conexión puede ser local oremota. Si es local no es necesario poner la IP:

AccesoDat os. BaseDat os. Dat abaseName : = ' C: \ Mi Pr ogr ama\ BaseDat os. gdb' ;

Aunque viene a ser lo mismo que hacer esto:

Page 188: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 188/306

AccesoDat os. BaseDat os. Dat abaseName : =' 127. 0. 0. 1: C: \ Mi Pr ogr ama\ BaseDatos. gdb' ;

Se trata de una conexión remota aunque estemos accediendo a nuestro mismoequipo. Si se tratara de otro equipo de la red habría que hacer lo mismo:

AccesoDat os. BaseDat os. Dat abaseName : =' 192. 168. 0. 1: C: \ Mi Progr ama\ BaseDat os. gdb' ;

Para bases de datos Firebird no existe la conexión local, siempre es remota.Así que si vamos a conectar con nuestro equipo en local habría que hacerloasí:

AccesoDat os. BaseDat os. Dat abaseName : =' 127. 0. 0. 1: C: \ Mi Pr ogr ama\ BaseDatos. f db' ;

Yo recomiendo utilizar siempre la conexión remota utilizando para ello unarchivo INI al lado de nuestro programa que contenga la IP del servidor. Porejemplo:

[ CONEXI ON]I P=127. 0. 0. 1

Así podemos hacer que nuestro programa se conecte en local o remoto sintener que volver a compilarlo. Pero que no se os olvide dejar desconectado elcomponente IBDatabase en el módulo de acceso a datos AccesoDatos porquesi no lo primero que ha a hacer es conectarse al arrancar el programaprovocando un error.

DANDO DE ALTA CLIENTES DE FORMA MASIVA

Para probar el rendimiento de un programa no hay nada mejor que darle cañametiendo miles y miles de registros a la base de datos. Para ello voy a crearun procedimiento dentro del botón Completar que le preguntará al usuariocuandos clientes desea crear. Al pulsar Aceptar dará de alta todos esosregistros mostrando el progreso en la barra de progreso que pusimosanteriormente:

pr ocedur e TFCl i ent es. BCompl etar Cl i ck( Sender : TObj ect ) ;

var  sNumero: st r i ng;  i , i I nvent ado: I nt eger ;begi n  sNumer o : = I nput Box( ' Nº de cl i ent es' , ' Rel l enando cl i ent es' , ' ' ) ;  Randomi ze; / / I ni ci al i zamos el gener ador de númer os al eat or i os

  i f sNumero <> ' ' t hen  begi n  wi t h Logi caNegoci o do  begi n  TCl i ent es. Open;  Progr eso. Max : = St r ToI nt ( sNumero ) ;

  Pr ogr eso. Vi si bl e : = Tr ue;  TLstCl i ent es. Di sabl eCont r ol s;

Page 189: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 189/306

  f or i : = 1 t o St r ToI nt ( sNumero ) do  begi n  i I nvent ado : = Random( 99999999 ) + 1000000; / / Nos i nver t amosun númer o i dent i f i cador de cl i ent e  TCl i ent es. I nser t ;

  TCl i ent esNOMBRE. AsSt r i ng : = ' CLI ENTE Nº ' + I ntToSt r (i I nvent ado ) ;  TCl i ent esNI F. AsSt r i ng : = I nt ToSt r ( i I nvent ado ) ;  TCl i ent esDI RECCI ON. AsSt r i ng : = ' CALLE Nº ' + I nt ToSt r (i I nvent ado ) ;  TCl i ent esPOBLACI ON. AsSt r i ng : = ' POBLACI ON Nº ' + I nt ToSt r (i I nvent ado ) ;  TCl i ent esPROVI NCI A. AsSt r i ng : = ' PROVI NCI A Nº ' + I nt ToSt r (i I nvent ado ) ;  i I nvent ado : = Random( 79999 ) + 10000; / / Nos i nvent amos elcódi go post al  TCl i ent esCP. AsSt r i ng : = I nt ToSt r ( i I nvent ado ) ;  TCl i ent esI MPORTEPTE. AsFl oat : = 0;  TCl i ent es. Post ;  Pr ogr eso. Posi t i on : = i ;  Appl i cat i on. Pr ocessMessages;  end;

  TCl i ent es. Cl ose;  Pr ogr eso. Vi si bl e : = Fal se;  TLst Cl i ent es. Enabl eCont r ol s;  end;  end;end;

Lo que hemos hecho es inventar el nombre de usuario, dirección, NIF, etc.

También he desconectado y he vuelto a conectar la tabla TLstClientes de larejilla utilizando los métodos DisableControls y EnableControls para ganarmás velocidad. La barra de progreso llamada Progreso mostrará la evolucióndel alta de registros.

Realmente es una burrada dar de alta masivamente registros utilizandoobjetos ClientDataSet ya que están pensados para utilizarlos para altas,modificaciones y eliminación de registros de uno a uno y sin mucha velocidad.Si quereis hacer una inserción masiva de registros hay que realizar consultasSQL con INSERT utilizando el objetos de la clase TIBSQL que es mucho másrápido que los ClientDataSet. Ya explicaré en otro momento la ventaja deutilizar dichos componentes.

Con esto finalizamos la introducción a los componentes ClientDataSet.

Pruebas realizadas en Firebird 2.0 y Delphi 7.

Page 190: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 190/306

Mostrando información en un ListView (I)Cuando nos conectamos a una base de datos estamos acostumbrados amostrar la información en un componente TDBGrid, pero hay ocasiones en lasque hay que mostrar información temporal que no va conectada a la base dedatos. Un ejemplo sería el listar ciertos campos de los albaranes antes defacturar o procesar la información de varias tablas en una sola.

DEFINIENDO COMO SE MUESTRA LA INFORMACIÓN EN UN LISTVIEW

El componente TListView permite mostrar la información de varias manerasdiferentes lo cual se especifica en su propiedad ViewStyle cuyas posibilidadesson:

vsI con - > Muest r a sus el ement os como i conos de t amaño normal

vsLi st - > Muest r a sus el ement os como una l i st a a var i as col umnasvsReport - > Muest r a sus el ement os según l as f i l as y col umnas alest i l o DBGr i d.vsSmal l I con - > Muest r a sus el ement os como i conos pequeños

En esta ocasión vamos a poner la propiedad ViewStyle a vsReport para quetenga un comportamiento similar a una rejilla de datos al estilo TDBGrid.

A continuación vamos a ver como definir las columnas a mostrar dentro de unListView. Para ello lo que hacemos es hacer doble clic sobre el ListView y seabrirá una ventana para añadir columnas. Pulsando el botón Add New vamos a

añadir las siguientes columnas:Nº col umna Capt i on Wi dt h Al i gnment- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -0 Nada 0 t aLef t1 I D 50 t aRi ght J ust i f y2 Ar t í cul o 150 t aLef t J ust i f y3 Uni dades 50 t aRi ght J ust i f y4 Pr eci o 50 t aRi ght J ust i f y5 Tot al 50 t aRi ght J ust i f y

La primera columna de un ListView es especialmente utilizada para guardar eltítulo de una fila o una imagen asociada como veremos más adelante. Como laprimera columna que quiero mostrar es el ID y la quiero alinear a la derechaentonces no me sirve. Lo que hago es llamar a la primera columna Nada y ledoy un ancho de 0 para que no se vea. Mas adelante le daremos utilidad a lamisma.

Una vez definidas las columnas vamos a ver como añadir filas.

AÑADIENDO ELEMENTOS AL LISTADO

Todas las columnas que se dan de alta en un objeto ListView son de tipo

String, con lo cual si deseamos mostrar otro tipo de información debemosutilizar las típicas rutinas de conversión FloatToStr, BoolToStr, etc.

Page 191: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 191/306

Page 192: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 192/306

Access violation at address ....

ELIMINANDO FILAS DE LA LISTA

Eliminar un registro de la lista es algo tan secillo de hacer como:

Li st Vi ew. I t ems[ 0] . Del et e;

Eso elimina la primera fila. Y si deseamos eliminar todos los elementos de lalista:

Li st Vi ew. I t ems. Cl ear ;

CAMBIANDO EL ASPECTO Y EL COMPORTAMIENTO DEL LISTADO

En la lista que hemos creado no podemos seleccionar ninguna fila, esta algoasí como bloqueado. Para poder hacerlo hay que activar la propiedadRowSelect a True.

Otra característica interesante es la de mostrar un separador de filas ycolumnas al estilo de la rejilla DBGrid. Para ello activa la propiedad GridLinesa True.

También ocurre que si tenemos una fila seleccionada y pasamos el foco a otrocontrol parece que se pierde la selección de dicha fila, aunque realmente

sigue seleccionada si volvemos al mismo. Para evitar esto lo que debemoshacer es desactivar la propiedad HideSelection de tal manera que cuandopasemos a otro control en vez de estar la fila seleccionada en azul lo hace encolor gris pero sigue estando seleccionada para el usuario.

Y si queremos seleccionar más de una fila activamos la propiedad Multiselect,de tal manera que si hay una fila seleccionada puede saberse de la siguientemanera:

i f Li st Vi ew. Sel ect ed <> ni l t hen  . . .

Siendo Selected el Item de la lista que ha sido seleccionado.

Si hay muchas seleccionadas la propiedad SelCount nos lo dirá el número defilas seleccionadas en todo momento. Para averiguar que filas estánseleccionadas lo que hacemos es recorrer la lista y ver aquellos elementos quetienen a True la propiedad Selected:

pr ocedur e TFormul ar i o. VerSel ecci onados;var  Sel ecci onados: TSt r i ngLi st ;  i : I nteger ;

begi n  Sel ecci onados : = TSt r i ngLi st . Cr eat e;

Page 193: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 193/306

  f or i : = 0 t o Li st Vi ew. I t ems. Count - 1 do  i f Li st Vi ew. I t ems[ i ] . Sel ected t hen  Sel ecci onados. Add( Li st Vi ew. I t ems[ i ] . SubI t ems[ 1] ) ;

  ShowMessage( Sel ecci onados. Text ) ;

  Sel ecci onados. Fr ee;end;

Lo que hace este procedimiento es crear un objeto StringList y volcar dentrodel mismo el nombre de los artículos seleccionados para después sacarlo porpantalla.

En el próximo artículo seguiremos viendo que más cosas se pueden hacer conun ListView.

Pruebas realizadas en Delphi 7.

Mostrando información en un ListView (II)Veamos que más le podemos hacer con el componente TListView.

ORDENANDO LOS ELEMENTOS DE LA LISTA

El objeto ListView dispone de dos tipos de ordenación parecidos. Por un ladotenemos la ordenación mediante la función CustomSort:

function CustomSort( SortProc: TLVCompare; lParam: Longint ): Boolean;

Esta función toma como primer parámetro la dirección de una funciónCALLBACK encargada de establecer los parámetros de la ordenación. Veamosun ejemplo de ordenación ascendente por la columna ARTÍCULO (la segunda):

Li st Vi ew. Cust omSor t ( @Or denaci on, 0 ) ;

Donde la función de ordenación sería la siguiente:

f unct i on Or denaci on( I t em1, I t em2: TLi st I t em; Par amSor t : I nt eger ) :

i nt eger; stdcal l ;begi n  Resul t : = CompareText ( I t em1. SubI t ems[ 1] , I t em2. SubI t ems[ 1] ) ;end;

Esta función compara los items 1 y 2 y le devuelve el resultado a CustomSort.Si queremos ordenarla descendentemente sería así:

f unct i on Or denaci on( I t em1, I t em2: TLi st I t em; Par amSor t : I nt eger ) :i nt eger; stdcal l ;begi n  Resul t : = - CompareText ( I t em1. SubI t ems[ 1] , I t em2. SubI t ems[ 1] ) ;

end;

Page 194: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 194/306

Por otro lado tenemos la función AlphaSort la cual no necesita una funciónCALLBACK porque para eso tenemos el evento OnCompare. Se haría de lasiguiente manera:

Li st Vi ew. Al phaSor t ;

y en el evento OnCompare del ListView:

pr ocedur e TFor mul ar i o. Li st Vi ewCompare( Sender : TObj ect ; I t em1, I t em2: TLi st I t em;  Data: I nt eger ; var Compare:I nt eger ) ;begi n  Compar e : = Compar eText ( I t em1. SubI t ems[ 1] , I t em2. SubI t ems[ 1] ) ;end;

Prácticamente es parecido a CustomSort (ya que un ListView desciende delcomponente CustomListView).

UTILIZANDO LA PRIMERA COLUMNA PARA CASOS ESPECIALES

Una de las cosas que se pueden meter en primera columna es un CheckBoxpara que el usuario marque filas. Antes de eso vamos a volver a mostrar laprimera columna (Nada) a 70 de ancho y le cambiamos el nombre aSeleccionado. Por último activamos en las propiedades del ListView el campoCheckBoxes.

Al ejecutar el programa y dar de alta filas en el listado veremos que por cadaelemento aparece un CheckBox a la izquierda para poder marcarlo. ¿Cómosabemos metiante código los elementos que han sido marcados? Con lapropiedad Checked de cada Item.

Veamos un ejemplo que comprueba los artículos marcados con CheckBox, losvuelca a un StringList y los saca a pantalla:

var  Sel ecci onados: TSt r i ngLi st ;  i : I nteger ;begi n

  Sel ecci onados : = TSt r i ngLi st . Cr eat e;

  f or i : = 0 t o Li st Vi ew. I t ems. Count - 1 do  i f Li st Vi ew. I t ems[ i ] . Checked t hen  Sel ecci onados. Add( Li st Vi ew. I t ems[ i ] . SubI t ems[ 1] ) ;

  ShowMessage( Sel ecci onados. Text ) ;

  Sel ecci onados. Fr ee;end;

Otra utilidad muy interesante que se le puede dar a la primera columna es

asociarle una imagen a cada fila. Para ello hay que añadir al formulario elcomponente ImageList que encuentra en la pestaña Win32.

Page 195: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 195/306

Después añadimos imágenes a la lista y asociamos las propiedadesLargeImages, StateImages y SmallImages con el componente ImageList. Consólo hacer eso todas las filas tendrán a la izquierda la primera imagen de lalista de imágenes. Para cambiar la imagen en una fila determinada se hace:

Li st Vi ew. I t ems[ 2] . I mageI ndex : = 2;

o bien cambiamos sólo la fila seleccionada por el usuario:

i f Li st Vi ew. Sel ect ed <> ni l t hen  Li st Vi ew. Sel ect ed. I mageI ndex : = 1;

ACTIVANDO LA SELECCIÓN AUTOMÁTICA

Si activamos en el componente ListView la propiedad HotTrack y pasamos elpuntero del ratón por las filas veremos como se iluminan en otro color,quedando seleccionadas fijamente si permanecemos con el ratón sobre lasmismas.

Esto puede ser de utilidad para crear programas visualizadores de imágenesque muestren fotografías con sólo posicionarse con el ratón encima delnombre de la foto JPG. O para crear un listado de URL para nuestras páginasweb preferidas.

También se pueden activar las propiedades:

ht HandPoi nt - > Cambi a el punt er o del r at ón a una mano cuando se

para por enci ma de l as f i l as.ht Underl i neCol d - > Subr aya l a f i l a que se va a sel ecci onarht Underl i neHol d - > Subr aya l a f i l a que hay sel ecci onada

En el próximo artículo vamos a ver como reprogramar el dibujado de las filasen tiempo real.

Pruebas realizadas en Delphi 7.

Mostrando información en un ListView (III)En esta última parte referida al componente ListView vamos a ver comomodificar los colores de las filas y las columnas según nuestro propio criterio.Para ello vamos a utilizar el evento OnCustomDrawSubItem.

En el primer ejemplo voy a mostrar como cambiar la columna 2 (la delnombre del artículo) poniendo la fuente azul oscuro y negrita:

pr ocedur e TFor mul ar i o. Li st Vi ewCust omDr awSubI t em( Sender : TCust omLi st Vi ew;  I t em: TLi st I t em; SubI t em: I nt eger ; St ate: TCust omDr awSt at e;

  var Def aul t Dr aw: Bool ean ) ;begi n

Page 196: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 196/306

  / / ¿Va pi nt ar l a segunda col umna?  i f SubI t em = 2 t hen  begi n  Sender . Canvas. Font . Col or : = cl Navy;  Sender. Canvas. Font . St yl e : = [ f sBol d] ;  end

  el se  Sender. Canvas. Font . Col or : = cl Bl ack;end;

Por el contrario, si en vez de modificar las características de una columnaqueremos modificar las de una fila entonces sería de la siguiente manera:

pr ocedur e TFor mul ar i o. Li st Vi ewCust omDr awSubI t em( Sender : TCust omLi st Vi ew;  I t em: TLi st I t em; SubI t em: I nt eger ; St ate: TCust omDr awSt at e;  var Def aul t Dr aw: Bool ean ) ;begi n  / / ¿Es l a pr i mer a f i l a?

  i f I t em. I ndex = 1 t hen  Sender. Canvas. Font . Col or : = cl Red  el se  Sender. Canvas. Font . Col or : = cl Bl ack;end;

En este ejemplo mostrado hemos puesto la segunda fila con la fuente de colorrojo. También se podría dibujar una fila según los datos contenidos en ella.Supongamos que deseo que se ponga la fuente de color rojo con un fondoamarillo en aquellos artículos cuyas unidades sean superiores a 3:

pr ocedur e TFor mul ar i o. Li st Vi ewCust omDr awSubI t em( Sender :

 TCust omLi st Vi ew;  I t em: TLi st I t em; SubI t em: I nt eger ; St ate: TCust omDr awSt at e;  var Def aul t Dr aw: Bool ean ) ;begi n  / / Si el númer o de uni dades es super i or a t r es i l umi namos l a f i l a der oj o con f ondo amari l l o  i f St r ToI nt ( I t em. SubI t ems[ 2] ) > 3 t hen  begi n  Sender. Canvas. Br ush. Col or : = cl Yel l ow;  Sender . Canvas. Font . Col or : = cl Red;  end  el se  begi n

  Sender . Canvas. Br ush. Col or : = cl Whi t e;  Sender. Canvas. Font . Col or : = cl Bl ack;  end;end;

Las posibilidades para realizar combinaciones de este tipo son enormes tantopara columnas como para filas.

PINTANDO FILAS Y COLUMNAS SEGÚN SU ESTADO

Hasta ahora lo que hemos dibujado ha sido para todas las filas y columnas

pero se podría modificar sólo la fila que tiene el foco en azul:pr ocedur e TFor mul ar i o. Li st Vi ewCust omDr awSubI t em( Sender :

Page 197: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 197/306

 TCust omLi st Vi ew;  I t em: TLi st I t em; SubI t em: I nt eger ; St ate: TCust omDr awSt at e;  var Def aul t Dr aw: Bool ean ) ;begi n  / / La f uent e de l a f i l a sel ecci onada en negr i t a  i f cdsFocused i n St at e t hen

  Sender. Canvas. Font . St yl e : = [ f sBol d]  el se  Sender. Canvas. Font . St yl e : = [ ] ;end;

Las posibilidades de la variable State son:

cdsSel ect ed - > La col umna o f i l a ha si do sel ecci onadacdsGr ayed - > La col umna o f i l a est a gri saceacdsDi sabl ed - > La col umna o f i l a esta deshabi l i t adacdsChecked - > La f i l a aparece con el CheckBox act i vadocdsFocused - > La col umna o f i l a est a enf ocadacdsDef aul t - > Por def ect o

cdsHot - > Se ha act i vado el HotTrack y est a enf ocadocdsMarked - > La f i l a est a marcadacdsI ndet ermi nate - > La f i l a no est a sel ecci onada ni desel ecci onada

Con lo que hemos visto ya podemos utilizar el componente ListView como unarejilla de datos sin tener que utilizar tablas temporales ni un DBGrid.

Pruebas realizadas en Delphi 7.

Trabajando con archivos de texto y binarios(I)Voy a mostrar a continuación las distintas maneras que tenemos para crear,editar o eliminar archivos de texto o binarios. También aprenderemos amovermos por los directorios y por dentro de los archivos obteniendo lainformación del sistema cuando sea necesario.

CREANDO UN ARCHIVO DE TEXTO

Los pasos para crear un archivo son los siguientes:

1º Se crea una variable de tipo TextFile, la cual es un puntero a un archivo detexto.

2º Se utiliza la función AssignFile para asignar el puntero F al archivo detexto.

3º A continuación abrimos el archivo en modo escritura mediante elprocedimiento Rewrite.

4º Escrimimos en el archivo mediante la función WriteLn.

5º Cerramos el archivo creado con el procedimiento CloseFile.

Page 198: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 198/306

Vamos a crear un procedimiento para crear el archivo prueba.txt al lado denuestro programa:

pr ocedur e TFormul ar i o. Cr ear Ar chi voTexto;

var  F: Text Fi l e;begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. t xt ') ;  Rewr i t e( F ) ;  Wr i t eLn( F, ' Est o es el cont eni do del ar chi vo de t exto. ' ) ;  Cl oseFi l e( F ) ;end;

MODIFICANDO UN ARCHIVO DE TEXTO

Cuando se utiliza la función Rewrite no comprueba si el archivo ya existíaanteriormente, lo que puede provocar que elimine la información anterior.Para prevenir esto lo que hacemos es preguntar si ya existe el archivo y si esasí entonces añadimos al contenido del archivo mediante el procedimientoAppend:

pr ocedur e TFormul ar i o. Anadi r Ar chi voTexto;var  F: Text Fi l e;  sAr chi vo: st r i ng;begi n  sAr chi vo : = Ext r act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. t xt ' ;

  Assi gnFi l e( F, sAr chi vo ) ;  i f Fi l eExi sts( sArchi vo ) then  Append( F )  el se  Rewr i t e( F ) ;

  Wr i t eLn( F, ' Añadi endo i nf or maci ón al ar chi vo de t exto. ' ) ;  Cl oseFi l e( F ) ;end;

ABRIENDO UN ARCHIVO DE TEXTO

Vamos a abrir el archivo de texto en modo lectura para volcar su contenido enun campo memo del formulario. Para ello abrimos el archivo con elprocedimiento Reset y leemos cada línea de texto mediante ReadLn:

pr ocedur e TFor mul ar i o. CargarAr chi voTexto;var F: Text Fi l e;  sLi nea: St r i ng;begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. t xt ') ;  Reset ( F ) ;

  whi l e not Eof ( F ) do  begi n  ReadLn( F, sLi nea ) ;

Page 199: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 199/306

  Memo. Li nes. Add( sLi nea ) ;end;

  Cl oseFi l e( F ) ;end;

El procedimiento ReadLn obliga a leer la línea de texto dentro de unavariable. Después incrementa automáticamente el puntero F hasta lasiguiente línea de texto, siendo la función Eof  la que nos dice si ha llegado alfinal del archivo. Aunque en este caso lo más fácil sería utilizar la propiedadLoadFromFile del objeto memo:

Memo. Li nes. LoadFromFi l e( Ext r actFi l ePath( Appl i cat i on. ExeName ) +' prueba. t xt ' ) ;

ELIMINANDO UN ARCHIVO

La eliminación de un archivo se hace con la función DeleteFile la cual estádentro de la unidad SysUtils:

pr ocedur e TFor mul ar i o. El i mi nar Ar chi voTexto;var sAr chi vo: St r i ng;begi n  sAr chi vo : = Ext r act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. t xt ' ;  i f Fi l eExi sts( sArchi vo ) then  Del et eFi l e( sAr chi vo ) ;end;

Todas estas funciones (Rewrite, Append, etc.) vienen del lenguaje estándar

de Pascal, ya que en Delphi hay maneras mucho más sencillas de realizarestas tareas, como veremos más adelante.

Pruebas ralizadas en Delphi 7.

Trabajando con archivos de texto y binarios(II)Vamos a ver otra manera de manejar ficheros de texto utilizando algunasclases que lleva Delphi para hacer nuestra labor mucho más fácil.

Para ello utilizaremos la clase TFileStream que hereda de la clase TStreampara manejar flujos de datos, en este caso archivos. Una de las ventanas deutilizar FileStream en vez de los clásicos métodos de pascal tales comoRewrite, WriteLn, etc. es que controla automáticamente los buffer en discosegún el tamaño de los mismos en Windows.

CREANDO UN ARCHIVO DE TEXTO USANDO LA CLASE TFILESTREAM

La clase TFileStream proporciona un método rápido y flexible para el manejode archivos. Veamos como crear un archivo detexto:

Page 200: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 200/306

pr ocedur e TFormul ar i o. Cr ear Ar chi voSt r eam;var F: TFi l eSt r eam;  s: St r i ng;begi n  F : = TFi l eSt r eam. Cr eate( Extr act Fi l ePath( Appl i cat i on. ExeName ) +

' pr ueba. t xt ' , f mCr eat e ) ;  s : = ' Añadi endo i nf ormaci ón al archi vo de t ext o. ' + #13 + #10;  F. Wr i t e( s[ 1] , Lengt h( s ) ) ;  F. Free;end;

El constructor Create de la clase TFileStream toma como primer parámetrola ruta del archivo y como segundo parámetro el tipo de acceso. Los tipos deacceso son:

f mCr eat e - > Cr ea un nuevo ar chi vo. Si el archi vo ya exi st e l osobr escr i be.f mOpenRead - > Abre el archi vo en modo de sol o l ect ura.f mOpenWr i t e - > Abre el archi vo en modo de escr i t ura.f mOpenReadWr i t e - > Abr e el archi vo en modo l ectura/ escr i t ur a.

Después se graba la información mediante el método Write el cual lee elcontenido de un buffer (puede ser texto o binario) pasando como segundoparámetro su longitud (Length). Finalmente liberamos la clase con Free y elmismo objeto cierra automáticamente el archivo.

Le hemos pasado como primer parámetro s[1] porque es la dirección dememoria donde comienza la variable s (los string empiezan por 1). Al final dela cadena le hemos metido cambién los caracteres de retorno de carro paraque pase a la siguiente línea, ya que un objeto FileStream lo trata todo comotexto continuo.

Cuando la cantidad de información a mover es muy grande, éste metodo esmucho más rápido que utilizar el clasico Rewrite, WriteLn, etc. (a menosclaro que creemos nuestro buffer).

AÑADIENDO TEXTO A UN ARCHIVO CON FILESTREAM

Para añadir líneas a nuestro archivo creado anteriormente abrimos el archivoen modo fmOpenWrite, nos vamos al final del archivo utilizando la propiedadPosition y añadimos el texto:

pr ocedur e TFPr i nci pal . Anadi r Ar chi voSt r eam;var F: TFi l eSt r eam;  s: St r i ng;begi n  F : = TFi l eSt r eam. Cr eate( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' pr ueba. t xt ' , f mOpenWr i t e ) ;  F. Posi t i on : = F. Si ze;  s : = ' Añadi endo i nf ormaci ón al archi vo de t ext o. ' + #13 + #10;  F. Wr i t e( s[ 1] , Lengt h( s ) ) ;

  F. Free;end;

Page 201: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 201/306

Si no se hubiese utilizado la propiedad Position habría machadado lainformación introducida anteriormente. Podemos movernos a nuestro antojocon la propiedad Position para guardar información en un fichero en cualquiersitio. Para irse al principio de un archivo sólo hay que hacer:

F. Posi t i on : = 0;

LEYENDO LOS DATOS MEDIANTE FILESTREAM

El siguiente procedimiento lee el contenido del archivo en modo fmOpenReaden la variable s que hace de buffer y posteriormente lo manda al memo:

pr ocedur e TFPr i nci pal . Car garAr chi voSt r eam;var F: TFi l eSt r eam;  s: St r i ng;begi n

  F : = TFi l eSt r eam. Cr eate( Ext r act Fi l ePath( Appl i cat i on. ExeName ) +' pr ueba. t xt ' , f mOpenRead ) ;  F. Read( s[1] , F. Si ze ) ;  Memo. Text : = s;  F. Free;end;

Aunque en este caso no tenemos la flexibilidad que nos aportaban lasfunciones ReadLn y WriteLn para archivos de texto.

BLOQUEANDO EL ARCHIVO A OTROS USUARIOS

Una de las cualidades de las que goza el objeto FileStream es la de bloquearel acceso a otros usuarios mientras estamos trabajando con un archivo. Laprotección se realiza cuando se abre el archivo. Por ejemplo, si queremosabrir el archivo en modo lectura exclusivamente para nosotros hacemos:

F : = TFi l eSt r eam. Cr eat e( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) +' pr ueba. t xt ' , f mReadOnl y or f mShar eExcl usi ve ) ;

Mediante el operador OR  mezclamos las opciones de apertura con las opcionesde bloqueo. Las posibles opciones de bloqueo son:

f mShareCompat - > Per mi t e compat i bi l i zar l a aper t ur a con ot r ousuar i o o progr amaf mShareExcl usi ve - > Sól o nosot r os t enemos der echo acceso del ectur a/ escri t ur a mi ent r as est e abi er t of mShar eDenyWr i t e - > Bl oqueamos el ar chi vo para que nadi e puedaescri bi r sal vo nosot r osf mShar eDenyRead - > Bl oqueamos el ar chi vo para que nadi e puedal eer l o sal vo nosot r osf mShareDenyNone - > Per mi t e l a l ect ur a/ escr i t ur a por part e deot r os usuar i os.

También se podrían manipular archivos de texto facilmente mediante objetosTStringList como vimos con anterioridad. Yo personalmente utilizo

TStringList, objetos Memo o RitchEdit ya que permiten una manipulación deltexto en memoria bastante flexible antes de guardarla en disco.

Page 202: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 202/306

En el próximo artículo veremos el tratamiento de archivos binarios.

Pruebas realizadas en Delphi 7.

Trabajando con archivos de texto y binarios(III)

Vamos a ver las distintas maneras que tenemos de manejar archivos binarios.

CREANDO UN ARCHIVO BINARIO

El crear un archivo binario utilizando AssignFile no es muy diferente de crearun archivo de texto:

pr ocedur e TFPr i nci pal . Cr ear Ar chi voBi nar i o;var  F: F i l e of byt e;  i : I nteger ;begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. dat ') ;  Rewr i t e( F ) ;

  f or i : = 1 t o 10 do  Wr i te ( F, i ) ;

  Cl oseFi l e( F ) ;end;

Como se puede apreciar, la variable F representa un puntero a un tipo dearchivo de bytes. Después utilizamos la función Write para ir guardando bytea byte dentro del archivo.

Cuando los archivos binarios son pequeños no hay problema pero cuando songrandes la lentitud puede ser insoportable (parece como si se hubiera colgadoel programa). Cuando ocurre esto entonces hay que crear un buffer temporalpara ir guardando los bytes en bloques de 1 Kb, 10 Kb, 20 Kb, etc. Hoy en díatodo lo que entra y sale de un disco duro para por la memoria caché delmismo (a parte de la memoria virtual de Windows).

Vamos a ver un ejemplo de como crear un archivo binario grande (100 Kb)utilizando un buffer. Vamos a guardar en el archivo bytes consecutivos (0, 1,2, .. 255, 0, 1, 2, ...):

pr ocedur e TFPr i nci pal . Cr earAr chi voBi nar i oConBuf f er;var  F: F i l e of byt e;

  i , j : I nt eger ;  b: Byte;  Buf f er : arr ay[ 1. . 1024] of byt e;

Page 203: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 203/306

begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. dat ') ;  Rewr i t e( F ) ;

  b : = 0;

  / / Guar damos 100 veces el buf f er de 1 KB ( 100 KB)  f or j : = 1 t o 100 do  begi n  f or i : = 1 t o 1024 do  begi n  Buf f er [ i ] : = b;  I nc( b ) ;  end;

  Bl ockWr i t e( F, Buf f er , 1024 ) ;  end;

  Cl oseFi l e( F ) ;end;

Hemos creado un buffer de 1024 bytes para guardar la información medianteel procedimiento BlockWrite que toma como primer parámetro el puntero F,como segundo parámetro el buffer del cual va a guardar la información ycomo tercer parámetro la longitud del buffer. Cuando más grande sea nuestrobuffer menos accesos a disco se necesita y más suelto va nuestro programa.

Ahora veremos como hacer lo mismo utilizando la clase TFileStream.

CREANDO UN ARCHIVO BINARIO CON LA CLASE TFILESTREAM

El parecido con la rutina anterior es casi idéntico salvo que hemos sustituidoun fichero de tipo File of byte por la clase TFileStream. El método Writetoma como parámetros el buffer y su longitud:

pr ocedur e TFPr i nci pal . Cr earSt r eamBi nar i o;var F: TFi l eSt r eam;  Buf f er : ar r ay[ 0. . 1023] of byt e;  i , j : I nt eger ;  b: Byt e;begi n  F : = TFi l eSt r eam. Cr eate( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' pr ueba. dat ' , f mCr eat e ) ;

  b : = 0;  / / Guar damos 100 veces el buf f er de 1 KB ( 100 KB)  f or j : = 1 t o 100 do  begi n  f or i : = 0 t o 1023 do  begi n  Buf f er [ i ] : = b;  I nc( b ) ;  end;

  F. Wr i t e( Buf f er , 1024 ) ;  end;

Page 204: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 204/306

  F. Free;end;

MODIFICANDO UN ARCHIVO BINARIO

En un archivo que no sea de tipoTextFile

 no se puede utilizar elprocedimiento Append para añadir datos al final del mismo. UsandoAssignFile se pueden utilizar dos métodos:

- Leer la información de todo el archivo en un buffer, añadir información alfinal del mismo y posteriormente guardarlo todo sobrescribiendo el archivocon Rewrite.

- Otro método sería crear una copia del archivo y añadirle datos al final delmismo. Luego habría que borrar el original y sustituirlo por este nuevo.

Ambos métodos no los voy a mostrar ya que sería algo primitivo en los tiemposque estamos. Para ello nada mejor que la clase TFileStream para tratamientode archivos binarios.

MODIFICANDO UN ARCHIVO BINARIO UTILIZANDO LA CLASE TFILESTREAM

Añadir datos a un archivo binario utilizando la clase TFileStream es tan fácilcomo irse al final del archivo y ponerse a escribir. De hecho sólo hemosañadido una línea de código al archivo anterior y hemos cambiado el métodode apertura:

pr ocedur e TFPr i nci pal . Anadi r St r eamBi nar i o;var F: TFi l eSt r eam;  Buf f er : ar r ay[ 0. . 1023] of byt e;  i , j : I nt eger ;  b: Byt e;begi n  F : = TFi l eSt r eam. Cr eate( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' pr ueba. dat ' , f mOpenWr i t e ) ;  F. Posi t i on : = F. Si ze;

  b : = 0;  / / Guar damos 100 veces el buf f er de 1 KB ( 100 KB)  f or j : = 1 t o 100 do

  begi n  f or i : = 0 t o 1023 do  begi n  Buf f er [ i ] : = b;  I nc( b ) ;  end;

  F. Wr i t e( Buf f er , 1024 ) ;  end;

  F. Free;end;

LEYENDO UN ARCHIVO BINARIO

Page 205: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 205/306

Para la lectura de un archivo binario también utilizaremos un buffer paraacelerar el proceso:

pr ocedur e TFPr i nci pal . Car garSt r eamBi nar i o;var F: TFi l eSt r eam;  Buf f er : ar r ay[ 0. . 1023] of byt e;begi n  F : = TFi l eSt r eam. Cr eate( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' pr ueba. dat ' , f mOpenRead ) ;

  / / ¿No ha l l egado al f i nal de ar chi vo?  whi l e F. Posi t i on < F. Si ze do  begi n  / / Leemos un bl oque de 1024 bytes  F. Read( Buf f er , 1024 ) ;  / / ya t enemos un bl oque de i nf or maci ón en el buf f er  / / podemos hacer l o que queramos con el ant es de cargar elsi gui ent e bl oque

  end;

  F. Free;end;

¿Cómo sabemos cuando se termina el archivo? Lo sabemos por la variablePosition que se va moviendo automáticamente a través del método Read.Cuando llegue al final (F.Size) cerramos el archivo.

Con esto llegamos a la conclusión de que para el manejo de archivos de textolo ideal es utilizar AssignFile, StringList o campos Memo, pero para archivos

binarios TFileStream cumple mejor su función.

En el próximo artículo seguiremos viendo más cosas sobre el tratamiento dearchivos.

Pruebas realizadas en Delphi 7.

Trabajando con archivos de texto y binarios(IV)

En el artículo anterior vimos como movernos por un archivo binario utilizandola propiedad Position de la clase TFileStream. Ahora vamos a ver lo mismoutilizando AssignFile.

RECORRIENDO UN ARCHIVO BINARIO EN MODO LECTURA

Si utilizamos AssignFile para leer un archivo en vez de TFileStream podemosmover el puntero en cualquier dirección utilizando el procedimiento Seek:

procedure Seek( var F; N: Longint );

Este procedimiento toma como primer parámetro el puntero al archivo (F:

Page 206: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 206/306

File of Byte) y como segundo parámetro la posición donde queremosmovernos, siendo cero la primera posición. Para irse al final del archivo sehace:

Seek( F, Fi l eSi ze( F) )

Vamos a abrir el archivo prueba.dat de 100 KB creado anteriormente y nosvamos a ir a la posición 50 para leer 10 bytes volcando la información en uncampo memo en formato hexadecimal:

var F: f i l e of byt e;  i : I nteger ;  Buf f er : arr ay[ 0. . 9] of Byt e;begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. dat ') ;

  Reset ( F ) ;  Seek( F, 50 ) ;  Bl ockRead( F, Buf f er , 10 ) ;

  f or i : = 0 t o 9 do  Memo. Text : = Memo. Text + I nt ToHex( Buf f er [ i ] , 2 ) + ' ' ;

  Cl oseFi l e( F ) ;end;

El resultado sería:

32 33 34 35 36 37 38 39 3A 3B

El único inconveniente que tiene la función Seek es que sólo funciona enmodo lectura (Reset). No se puede utilizar en modo escritura (Rewrite) parairse al final del archivo y seguir añadiendo datos.

Si no sabemos donde estamos se puede utilizar la función FilePos paraaveriguarlo:

ShowMessage( ' posi ci ón: ' + I nt ToSt r ( Fi l ePos( F ) ) ) ;

LEYENDO LAS PROPIEDADES DE UN ARCHIVO

Veamos de que funciones dispone Delphi para leer los atributos de un archivo(fecha, modo de acceso. etc.):

function FileAge( const FileName: string ): Integer;

Esta función devuelve la fecha y hora de última modificación de un archivo enformato TTimeStamp. Para pasar de formato TTimeStamp a formatoTDateTime utilizamos la función FileDateToDateTime. Por ejemplo:

Fi l eDat eToDat eTi me( Fi l eAge( ' pr ueba. t xt ' ) ) - > devuel ve l a f echa yhora de modi f i caci ón del archi vo pr ueba. t xt

Page 207: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 207/306

También disponemos de una función para obtener los atributos de un archivo:

function FileGetAttr( const FileName: string ): Integer;

Devuelve un valor entero conteniendo los posibles atributos de un archivo(puede tener varios a la vez). Por ejemplo, para averiguar si el archivo estaoculto se haría lo siguiente:

var  i At r i but os: I nt eger;begi n  i f ( i At r i but os and f aHi dden <> 0 ) and ( i At r i but os and f aAr chi ve<> 0 ) t hen  . . .end;

Esta función no sólo comprueba archivos, sino también directorios y unidadesde disco. Todos los posibles valores se encuentran en binario activando el bitcorrespondiente. Los valores posibles son:

Const ant e Val or Ti po de archi vo- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - -f aReadOnl y 1 Sól o l ectur af aHi dden 2 Ocul t of aSysFi l e 4 Ar chi vo del si st emaf aVol umeI D 8 Uni dad de di scof aDi r ectory 16 Di r ectori of aAr chi ve 32 Ar chi vo

f aSymLi nk 64 Enl ace si mból i cof aAnyFi l e 71 Cual qui er archi vo

Otra función interesante es FileSize la cual devuelve en bytes el tamaño deun archivo. El único inconveniente es que hay que abrir el archivo paraaveriguarlo:

var  F: F i l e of byt e;begi n  Assi gnFi l e( F, ' c: \ prueba. tx t ' ) ;  Reset ( F ) ;  ShowMessage( I nt ToSt r ( Fi l eSi ze( F ) ) + ' byt es' ) ;  Cl oseFi l e( F ) ;end;

MODIFICANDO LAS PROPIEDADES DE UN ARCHIVO

Las función para modificar las propiedad de un archivo es:

function FileSetAttr( const FileName: string; Attr: Integer ): Integer;

Page 208: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 208/306

Si se pudo modificar el atributo del archivo devuelve un 0 o en caso de errorel código del mismo. Por ejemplo para hacer un archivo de sólo lectura yoculto:

Fi l eSet At t r ( ' c: \ pr ueba. t xt ' , f aReadOnl y or f aHi dden ) ;

En el próximo artículo terminaremos de ver las funciones más importates parael manejo de archivos.

Pruebas realizadas en Delphi 7.

Trabajando con archivos de texto y binarios(V)Vamos a terminar de ver los recursos de los que dispone Delphi para eltratamiento de archivos.

PARTIENDO UN ARCHIVO EN DOS

En este ejemplo vamos a coger el archivo prueba.dat de 100 Kb y dejamossólo las primeras 30 Kb eliminando el resto:

pr ocedur e TFor mul ari o. Par t i r Ar chi vo;var F: Fi l e of byt e;  Buf f er : ar r ay[ 0. . 1023] of Byt e;  i : I nteger ;begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. dat ') ;  Reset ( F ) ;

  / / Leemos 30 Kb ut i l i zando un buf f er de 1 Kb  f or i : = 1 t o 30 do  Bl ockRead( F, Buf f er, 1024 ) ; 

 Tr uncat e( F ) ;  Cl oseFi l e( F ) ;end;

Hemos utilizado para ello la función Truncate, la cual parte el archivo queestamos leyendo según donde este el puntero F.

ASEGURANDO QUE SE GUARDE LA INFORMACIÓN EN ARCHIVOS DE TEXTO

Cuando abrimos un archivo de texto para escribir en él no se guardacompletamente toda la información hasta que se cierra con el procedimientoCloseFile. Esto puede dar problemas si en algún momento el procedimientoWriteLn provoca un error dejando el archivo abierto. Se perdería la mayor

parte de la información que supuestamente debería haberse guardado en elmismo.

Page 209: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 209/306

Para evitar esto disponemos de la función Flush:

function Flush( var t: TextFile ): Integer;

Esta función lo que hace es vaciar el buffer del archivo de texto en el discoduro. Es algo así como ejecutar CloseFile pero sin cerrar el archivo. Porejemplo:

var  F: Text Fi l e;  sAr chi vo: St r i ng;begi n  sAr chi vo : = Ext r act Fi l ePat h( Appl i cat i on. ExeName ) + ' pr ueba. t xt ' ;  Assi gnFi l e( F, sAr chi vo ) ;  Rewr i t e( F ) ;  Wr i t eLn( F, ' Est o es el cont eni do del ar chi vo de t exto. ' ) ;  Fl ush( F ) ;  / / aquí podemos segui r escr i bi endo en el mi smo  Cl oseFi l e( F ) ;end;

GUARDANDO OTROS TIPOS DE DATOS EN LOS ARCHIVOS

Hasta ahora sólo hemos guardado información de tipo texto y binaria enarchivos pero se puede guardar cualquier tipo de información utilizandocualquier tipo de dato. Por ejemplo para guardar una serie de números realesse haría de la siguiente manera:

pr ocedur e TFormul ar i o. GuardarReci bos;var  F: Fi l e of Real ;  r : Real ;begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' I mpor t eReci bos. dat ' ) ;  Rewr i t e( F ) ;  r : = 120. 45;  Wri t e( F, r ) ;  r : = 1800. 05;  Wri t e( F, r ) ;  r : = 66. 31;

  Wri t e( F, r ) ;  Cl oseFi l e( F ) ;end;

Si os fijais bien en el archivo resultante vemos que ocupa 24 bytes. Esto es asíporque el tipo real ocupa 8 bytes en memoria y como son 3 números reales loque hemos guardado entonces hace un total de 24 bytes. Hay que asegurarsede que tanto al leer como al guardar información en este tipo de archivos sehaga con el mismo tipo de variable. Si guardamos información con File of Realy la leemos con File of Single los resultados podrían ser catastróficos (a partede quedarse el archivo a medio leer).

También se pueden guardar estructuras de datos almacenadas en registros. En

Page 210: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 210/306

este ejemplo que voy a mostrar vamos a ver como guardar los datos de uncliente en un archivo:

type  TCl i ent e = r ecor d  I D: I nt eger ;

  sNombre: St r i ng[ 50] ;  r Sal do: Real ;  bPagado: Bool ean;  end;

pr ocedur e TFPr i nci pal . Cr ear Ar chi voRegi st r o;var  Cl i ent e: TCl i ent e;  F: Fi l e of TCl i ent e;begi n  Assi gnFi l e( F, Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' cl i entes . dat ' ) ;  Rewr i t e( F ) ;

  wi t h Cl i ent e do  begi n  I D : = 1;  sNombr e : = ' FRANCI SCO MARTI NEZ LÓPEZ' ;  r Sal do : = 1200. 54;  bPagado : = Fal se;  end;

  Wr i t e( F, Cl i ent e ) ;

  wi t h Cl i ent e do  begi n

  I D : = 2;  sNombr e : = ' MARI A ROJ O PALAZÓN' ;  r Sal do : = 622. 32;  bPagado : = Tr ue;  end;

  Wr i t e( F, Cl i ent e ) ;

  Cl oseFi l e( F ) ;end;

Unas de las cosas a tener en cuenta es que cuando se define una estructura dedatos no se puede definir una variable de tipo String sin dar su tamaño, yaque cada registro debe tener una longitud fija. Si ponemos String nos da unerror al compilar, por eso hemos puesto en el nombre un String[50].

Con esto finalizamos la parte básica de tratamiento de archivos en Delphi.

Pruebas realizadas en Delphi 7.

Page 211: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 211/306

Trabajando con documentos XML (I)Un documento XML (Extensible Markup Language) contiene un lenguaje deetiquetas para almacenar información de forma estructurada. Es similar a laspáginas web HTML, exceptuando que sólo guarda la información y no la formade visualizarla. Los documentos XML proporcionan una forma simple paraalmacenar la información que permite ser comprendida fácilmente.

Además se ha convertido de hecho en un estándar, siendo utilizado enaplicaciones web, comunicaciones, etc. Se trata de un lenguaje deintercambio de datos que ya es soportado por la mayoría de lenguajes deprogramación.

Los documentos XML manejan la información de forma jerárquica, siendo lasetiquetas las que describen la información de cada elemento así como loselementos hijos. Veamos un ejemplo de un documento XML:

Este ejemplo muestracomo está almacenada la información en un documento XML. La primera líneacontiene la declaración de un documento XML. Aunque declaración esopcional es recomendable introducirla en todos los documentos XML. Es estecaso nos dice que es un documento XML con versión 1.0 y que utiliza lacodificación de caracteres UTF-8.

La segunda línea nos informa que el documento se llama Clientes y que va aestar enlazado con un documento externo llamado cli.dtd. Los documentos

DTD se utilizan para definir las reglas de cómo tienen que estar estructuradoslos documentos XML. En nuestro caso define la regla de cómo deben estardefinida la estructura de un cliente. Sería algo así como la definición de unesquema para la jerarquía de los datos del cliente. No es obligatorio definirun DTD por cada documento XML.

La información que se guarda en los documentos XML comienza con un nodoraiz Clientes que a su vez tiene nodos hijos: Cliente, nombre, etc. Lasetiquetas a su vez pueden contener dentro valores predeterminados, porejemplo Cliente tiene el valor id="1".

Aunque es posible trabajar directamente con un documento XML utilizandocualquier procesador de textos (incluso el Bloc de Notas de Windows) existen

Page 212: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 212/306

aplicaciones para editar este tipo de documentos. El consorcio internacionalW3C define un conjunto de reglas estándar que definen como debe crearse undocumento XML cuyo nombre es DOM (Document Object Model).

USANDO DOM

Las interfaces denifidas por el estandar DOM contienen la implementación dediferentes documentos XML suministrados por terceras partes llamadosvendedores (vendors). Si no deseamos utilizar la implementación DOM deestos vendedores, Delphi permite utilizar un mecanismo de registro adicionalpara crearnos nuestra propia implementación de documentos XML.

La unidad XMLDOM incluye declaraciones para todas las interfaces DOMdefinidas por las especificaciones de nivel 2 para documentos XML según elconsorcio W3C. Cada vendedor DOM proporciona sus propias interfaces paramanejar documentos XML. Una interfaz DOM viene a ser algo así como un

driver para manejar documentos XML.

Para que Delphi pueda utilizar un documento DOM de algun vendedor enconcreto hay que añadir la unidad correspondiente a dicha implementaciónDOM. Estas unidades finalizan con la cadena 'xmldom'. Por ejemplo, la unidadpara la implementación de Microsoft es MSXMLDOM, la unidad paradocumentos de IBM es IBMXMLDOM y la unidad para implementaciones OpenXML es OXMLDOM.

Si quisieramos crear nuestra propia implementación DOM tendríamos que

crear una unidad que defina una clase descendiente de la clase TDOMVendor.Se haría de la siguiente manera:

- En nuestra clase descendiente, debemos sobrescribir dos métodos: elmétodo Description, el cual devuelve una cadena con el identificador delvenvedor y el método DOMImplementation donde define la interfazIDOMImplementation.

- Después registramos nuestra nueva unidad llamando al procedimientoRegistrerDOMVendor y ya estaría lista para poder ser utilizada.

- Y si queremos eliminarla de las implementaciones estándar DOM debemosanular el registro llamando al procedimiento UnRegisterDOMVendor, el cualdebería estar en la sección finalization.

Algunos vendedores suministran extensiones a las interfaces estandar DOM.Para que puedas usar esas extensiones la unidad XMLDOM define la interfazIDOMNodeEx, la cual es descendiente de la interfaz estandar IDOMNode.

Podemos trabajar directamente con interfaces DOM para leer y editardocumentos XML. Simplemente llamamos la la función GetDOM para obtenerel intefaz IDOMImplementation, el cual proporciona un punto de partida.

TRABAJANDO CON COMPONENTES XML

Page 213: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 213/306

El punto de partida para comenzar a trabajar con documentos XML esutilizando el componente de la clase TXMLDocument. Para poder crear undocumento XML hay que seguir los siguientes pasos:

1. Añadimos el componente XMLDocument a nuestro formulario o módulo dedatos. Este componente se encuentra en la pestaña Internet.

2. Seleccionamos en la propiedad DOMVendor la implementación deldocumento DOM que queremos usar. Por defecto aparece seleccionada la deMicrosoft (MSXML).

3. Dependiendo de la implementación seleccionada, podemos modificar susopciones en propiedad ParseOptions para configurar como va a procesarse eldocumento XML. Un parser viene a ser algo así como un analizador sintáctico.

4. Para trabajar con un documento XML que ya existe hay que especificar eldocumento de la siguiente manera:

- Si el documento esta almacenado en un archivo, ponemos en FileName elnombre del archivo.

- O bien podemos meter el texto XML dentro del mismo componenteXMLDocument utilizando su propiedad XML.

5. Activamos el documento XML poniendo la propiedad Active a True.

Una vez que tenemos activo el documento, podremos navegar por su jerarquíade nodos leyendo o modificando sus valores. El nodo raiz está disponible en lapropiedad DocumentElement.

En el próximo artículo veremos ejemplos de código fuente para leer ymodificar documentos XML con Delphi.

Pruebas realizadas en Delphi 7.

Trabajando con documentos XML (II)Anteriormente vimos que cada documento XML podía llevar asociado undocumento DTD. Los documentos DTD (Document Type Definition) definen laestructura y la sintaxis de un documento XML. Su misión es mantener laconsistencia entre los elementos del documento XML para que todos losdocumentos de la misma clase mantengan un mismo estilo.

Nuestro documento cli.dtd va a ser el siguiente:

Page 214: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 214/306

Veamos que hace cadalínea:

!ELEMENT Clientes (Cliente*)

Aquí establecemos que el documento XML se va a llamar Clientes y que cadaelemento se llamará Cliente. El asterístico significa que va a tener cero o máselementos de tipo Cliente. Si quisieramos que el nodo Cliente apareciese unao más veces utilizaríamos el caracter +.

!ELEMENT Cliente (nombre, nif, saldopte?, diaspago?)

La segunda línea nos dice de que campos está compuesto el nodo Cliente:nombre, nif , saldopte y diaspago. Todos los campos que no lleven el signo deinterrogación son obligatorios (en este caso nombre y nif ).

!ELEMENT nombre (#PCDATA)!ELEMENT nif (#PCDATA)!ELEMENT saldopte (#PCDATA)!ELEMENT diaspago (#PCDATA)

Por último se describe el tipo de dato que van a contener los nodos finales. El

atributo #PCDATA significa que puede contener cualquier valor.

Con este archivo al lado de nuestros documentos XML podemos hacer dobleclic sobre los mismos para que se vean en cualquier navegador.

NAVEGANDO POR LOS NODOS DEL DOCUMENTO XML

Una vez que el documento ha sido analizado sintácticamente por laimplementación DOM, los datos representados estarán disponibles en lajerarquía de nodos. Cada nodo corresponde a un elemento etiqueta deldocumento. Por ejemplo, para el documento de clientes.xml que hemos vistoanteriormente tendríamos la siguiente jerarquía: El nodo raiz es Clientes quea su vez tiene nodos hijos con el nombre Cliente. Cada uno de esos nodoshijos tienen a su vez otros nodos hijos tales como (nombre, nif , importepte ydiaspago). Esos nodos hijos actuan como los nodos finales de un arból ya notienen más nodos hijos.

Para acceder a cada nodo se utiliza la interfaz IXMLNode, comenzando por elnodo raiz a través de la propiedad DocumentElement del componenteXMLDocument.

LEYENDO EL VALOR DE LOS NODOS

Page 215: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 215/306

Vamos a ver un ejemplo de cómo leer el saldo pentiende (saldopte) delprimer cliente y mostrarlo por pantalla:

var

  Cl i ent e: I XMLNode;begi n  XML. LoadFr omFi l e( Ext r actFi l ePath( Appl i cat i on. ExeName ) +' cl i entes . xml ' ) ;  XML. Act i ve : = True;  Cl i ent e : = XML. Document El ement . Chi l dNodes[ 0] ;  ShowMessage( Cl i ent e. Chi l dNodes[ ' sal dopt e' ] . Text ) ;end;

Suponemos que el documento clientes.xml está en el mismo directorio quenuestro programa. Como se puede apreciar en el código hemos abierto eldocumento XML, lo hemos activado y a continuación hemos recogido el nodo

del primer cliente dentro de la variable Cliente que es de tipo IXMLNode.Después hemos accedido a su nodo hijo llamado saldopte para leer su valor.

MODIFICANDO EL VALOR DE LOS NODOS

Utilizando el mismo sistema podemos modificar el valor de cada nodo. En elsiguiente ejemplo voy a modificar el nombre del segundo cliente (ROSAMARTINEZ GUILLEN) por el de MARIA PEREZ ROJO:

var  Cl i ent e: I XMLNode;

begi n  XML. LoadFr omFi l e( Ext r actFi l ePath( Appl i cat i on. ExeName ) +' cl i entes . xml ' ) ;  XML. Act i ve : = True;  Cl i ent e : = XML. Document El ement . Chi l dNodes[ 1] ;  Cl i ent e. Chi l dNodes[ ' nombr e' ] . Text : = ' MARI A PEREZ ROJ O' ;  XML. SaveToFi l e( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' c l i ent es2. xml ' ) ;end;

Una vez modificado el nombre grabo todo el documento XML en otro archivollamado clientes2.xml (aunque podía haber utilizado el mismo).

AÑADIENDO Y BORRANDO NODOS

Mediante el método AddChild podemos crear nuevos nodos hijos dentro de undocumento XML. Veamos como dar de alta un nuevo cliente y guardar elarchivo como clientes3.xml:

var  Cl i ent e, Nodo: I XMLNode;begi n  XML. LoadFr omFi l e( Ext r actFi l ePath( Appl i cat i on. ExeName ) +' cl i entes . xml ' ) ;  XML. Act i ve : = True;  Cl i ent e : = XML. Document El ement . AddChi l d( ' Cl i ent e' ) ;  Cl i ente. At t r i but es [ ' i d' ] : = ' 3' ;

Page 216: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 216/306

  Nodo : = Cl i ent e. AddChi l d( ' nombr e' ) ;  Nodo. Text : = ' PABLO PALAZON ALCOLEA' ;  Nodo : = Cl i ent e. AddChi l d( ' ni f ' ) ;  Nodo. Text : = ' 79469163E' ;  Nodo : = Cl i ent e. AddChi l d( ' sal dopt e' ) ;  Nodo. Text : = ' 0. 00' ;

  Nodo : = Cl i ent e. AddChi l d( ' di aspago' ) ;  Nodo. Text : = ' 15' ;  XML. SaveToFi l e( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' c l i ent es3. xml ' ) ;end;

En esta ocasión utilizamos dos nodos: Cliente para crear el nodo padre y Nodopara crear cada uno de los nodos hijos.

Por último podemos eliminar elementos utilizando el método Delete cuyoparámetro es el número de nodo hijo que deseamos eliminar (siendo cero elprimer elemento). Para eliminar el primer cliente de la lista hacemos los

siguiente:

begi n  XML. LoadFr omFi l e( Ext r actFi l ePath( Appl i cat i on. ExeName ) +' cl i entes . xml ' ) ;  XML. Act i ve : = True;  XML. Document El ement . Chi l dNodes. Del ete( 0 ) ;  XML. SaveToFi l e( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' c l i ent es4. xml ' ) ;end;

Este código coge la lista de los dos clientes iniciales, elimina el primero y

guarda el documento como clientes4.xml. De esta manera podemos crear unapequeña base de datos utilizando documentos XML.

En el próximo artículo veremos como simplificar aún más el manejo dearchivos XML utilizando el asistente XML Data Binding.

Pruebas realizadas en Delphi 7.

Trabajando con documentos XML (III)Aunque es posible trabajar con un documento XML usando solamente el

componente XMLDocument y la interfaz IXMLNode es mucho más fácil utilizarel asistente XML Data Binding.

Dicho asistente toma los datos del esquema de un archivo XML a través de suarchivo asociado DTD y realiza una nueva unidad con una interfaz que manejanuestro documento XML sin tener que navegar por los nodos.

CREANDO UNA INTERFAZ PARA CLIENTES.XML

Para crear una unidad nueva para nuestro documento clientes.xml hay quehacer lo siguiente:

1. Seleccionamos en Delphi la opción File -> New -> Other...

Page 217: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 217/306

2. Estando situados en la pestaña New seleccionamos el icono XML DataBinding y pulsamos OK .

3. En la ventana siguiente seleccionamos el archivo cli.dtd y pulsamos Next.

4. Después nos aparece otra ventana donde se nos permite modificar elnombre de la clase a crear así como los nombres que van tener laspropiedades y métodos de la nueva clase. Lo dejamos como está y pulsamosNext.

Page 218: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 218/306

5. La última pantalla del asistente nos muestra como va a ser definitivamente

la interfaz que va a crear. Sólo nos queda pulsar Finish.

Va a generar la siguiente unidad:

{*********************************************************************

*****}{}{ XML Dat a Bi ndi ng}{}{ Generat ed on: 11/ 10/ 2007 11: 15: 57}{ Gener at ed f r om: D: \ Desar r ol l o\ Del phi Al Li mi t e\ XML\ cl i . dt d}{ Set t i ngs st or ed i n: D: \ Desar r ol l o\ Del phi Al Li mi t e\ XML\ cl i . xdb}

{}{*********************************************************************

Page 219: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 219/306

*****}

uni t UI nt er f azCl i ent es;

i nt erf ace

uses xml dom, XMLDoc, XMLI nt f ;type

{ For ward Decl s }

  I XMLCl i ent esType = i nt er f ace;  I XMLCl i ent eType = i nt erf ace;

{ I XMLCl i ent esType }

  I XMLCl i ent esType = i nt er f ace( I XMLNodeCol l ect i on)  [ ' {A21F5A80- EBA7- 48F5- BFE1- EB9F390DFBBD}' ]  { Proper t y Accessors }  f unct i on Get _Cl i ent e( I ndex: I nt eger ) : I XMLCl i ent eType;  { Met hods & Propert i es }  f unct i on Add: I XMLCl i ent eType;  f unct i on I nser t ( const I ndex: I nt eger ) : I XMLCl i ent eType;  pr opert y Cl i ent e[ I ndex: I nt eger ] : I XMLCl i ent eType r eadGet _Cl i ent e; def aul t ;  end;

{ I XMLCl i ent eType }

  I XMLCl i ent eType = i nt er f ace( I XMLNode)  [ ' {624B6C2E- 4E94- 4CE0- A8D4- FE719AA7D975}' ]  { Proper t y Accessors }  f unct i on Get_Nombr e: Wi deSt r i ng;  f unct i on Get _Ni f : Wi deSt r i ng;  f unct i on Get _Sal dopt e: Wi deSt r i ng;  f unct i on Get_Di aspago: Wi deSt r i ng;  pr ocedure Set _Nombr e(Val ue: Wi deSt r i ng) ;  pr ocedur e Set _Ni f ( Val ue: Wi deSt r i ng) ;  pr ocedur e Set _Sal dopt e(Val ue: Wi deSt r i ng) ;  pr ocedur e Set_Di aspago( Val ue: Wi deSt r i ng) ;  { Met hods & Propert i es }  proper t y Nombre: Wi deSt r i ng r ead Get _Nombre wr i t e Set _Nombre;  pr opert y Ni f : Wi deSt r i ng r ead Get_Ni f wr i t e Set _Ni f ;  pr opert y Sal dopt e: Wi deSt r i ng r ead Get _Sal dopt e wr i t eSet _Sal dopt e;

  pr oper t y Di aspago: Wi deSt r i ng r ead Get_Di aspago wr i t eSet _Di aspago;  end;

{ For ward Decl s }

  TXMLCl i ent esType = cl ass;  TXMLCl i ent eType = cl ass;

{ TXMLCl i ent esType }

  TXMLCl i ent esType = cl ass( TXMLNodeCol l ect i on, I XMLCl i ent esType)  prot ect ed

  { I XMLCl i ent esType }  f unct i on Get _Cl i ent e( I ndex: I nt eger ) : I XMLCl i ent eType;

Page 220: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 220/306

  f unct i on Add: I XMLCl i ent eType;  f unct i on I nser t ( const I ndex: I nt eger ) : I XMLCl i ent eType;  publ i c  pr ocedur e Af t erConst r uct i on; over r i de;  end;

{ TXMLCl i enteType }  TXMLCl i ent eType = cl ass( TXMLNode, I XMLCl i enteType)  prot ect ed  { I XMLCl i ent eType }  f unct i on Get_Nombr e: Wi deSt r i ng;  f unct i on Get _Ni f : Wi deSt r i ng;  f unct i on Get _Sal dopt e: Wi deSt r i ng;  f unct i on Get_Di aspago: Wi deSt r i ng;  pr ocedure Set _Nombr e(Val ue: Wi deSt r i ng) ;  pr ocedur e Set _Ni f ( Val ue: Wi deSt r i ng) ;  pr ocedur e Set _Sal dopt e(Val ue: Wi deSt r i ng) ;  pr ocedur e Set_Di aspago( Val ue: Wi deSt r i ng) ;  end;

{ Gl obal Funct i ons }

f unct i on GetCl i ent es( Doc: I XMLDocument ) : I XMLCl i ent esType;f unct i on LoadCl i ent es( const Fi l eName: Wi deSt r i ng) : I XMLCl i ent esType;f unct i on NewCl i ent es: I XMLCl i ent esType;

const  TargetNamespace = ' ' ;

i mpl ement at i on

{ Gl obal Funct i ons }

f unct i on GetCl i ent es( Doc: I XMLDocument ) : I XMLCl i ent esType;begi n  Resul t : = Doc. Get DocBi ndi ng( ' Cl i ent es' , TXMLCl i ent esType,

 Tar get Namespace) as I XMLCl i ent esType;end;

f unct i on LoadCl i ent es( const Fi l eName: Wi deSt r i ng) : I XMLCl i ent esType;begi n  Resul t : = LoadXMLDocument ( Fi l eName) . GetDocBi ndi ng( ' Cl i ent es' ,

 TXMLCl i ent esType, Tar get Namespace) as I XMLCl i entesType;end;

f unct i on NewCl i ent es: I XMLCl i ent esType;begi n  Resul t : = NewXMLDocument . GetDocBi ndi ng( ' Cl i ent es' , TXMLCl i ent esType,

 Tar get Namespace) as I XMLCl i ent esType;end;

{ TXMLCl i ent esType }

pr ocedur e TXMLCl i ent esType. Af t erConst r uct i on;begi n  Regi st erChi l dNode( ' Cl i ent e' , TXMLCl i ent eType) ;  I t emTag : = ' Cl i ent e' ;  I t emI nt er f ace : = I XMLCl i ent eType;

  i nher i t ed;end;

Page 221: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 221/306

f unct i on TXMLCl i ent esType. Get _Cl i ent e( I ndex: I nt eger ) :I XMLCl i ent eType;begi n  Resul t : = Li st [ I ndex] as I XMLCl i ent eType;end;

f unct i on TXMLCl i ent esType. Add: I XMLCl i ent eType;begi n  Resul t : = AddI t em( - 1) as I XMLCl i ent eType;end;

f unct i on TXMLCl i ent esType. I nser t ( const I ndex: I nt eger ) :I XMLCl i ent eType;begi n  Resul t : = AddI t em( I ndex) as I XMLCl i ent eType;end;

{ TXMLCl i enteType }

f unct i on TXMLCl i ent eType. Get _Nombr e: Wi deSt r i ng;begi n  Resul t : = Chi l dNodes[ ' nombr e' ] . Text ;end;

pr ocedur e TXMLCl i ent eType. Set _Nombr e( Val ue: Wi deSt r i ng) ;begi n  Chi l dNodes[ ' nombre' ] . NodeVal ue : = Val ue;end;

f unct i on TXMLCl i ent eType. Get_Ni f : Wi deSt r i ng;begi n  Resul t : = Chi l dNodes[ ' ni f ' ] . Text ;end;

pr ocedur e TXMLCl i ent eType. Set_ Ni f ( Val ue: Wi deSt r i ng) ;begi n  Chi l dNodes[ ' ni f ' ] . NodeVal ue : = Val ue;end;

f unct i on TXMLCl i ent eType. Get _Sal dopt e: Wi deSt r i ng;begi n  Resul t : = Chi l dNodes[ ' sal dopt e' ] . Text ;end;

pr ocedur e TXMLCl i ent eType. Set _Sal dopt e(Val ue: Wi deSt r i ng) ;

begi n  Chi l dNodes[ ' sal dopt e' ] . NodeVal ue : = Val ue;end;

f unct i on TXMLCl i ent eType. Get _Di aspago: Wi deSt r i ng;begi n  Resul t : = Chi l dNodes[ ' di aspago' ] . Text ;end;

pr ocedur e TXMLCl i ent eType. Set _Di aspago(Val ue: Wi deSt r i ng) ;begi n  Chi l dNodes[ ' di aspago' ] . NodeVal ue : = Val ue;end;

end.

Page 222: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 222/306

UTILIZANDO LA NUEVA INTERFAZ PARA CREAR DOCUMENTOS XML

La unidad creada por el asistente la he guardado con el nombreUInterfazClientes.pas lo que significa que hay que introducirla en la sección

uses del formulario donde vayamos a utilizarla.

Vamos a ver como damos de alta un cliente utilizando dicha interfaz:

var  Cl i ent es: I XMLCl i ent esType;begi n  XML. Fi l eName : = Ext r actFi l ePath( Appl i cat i on. ExeName ) +' cl i entes . xml ' ;  XML. Act i ve : = True;  Cl i ent es : = Get Cl i ent es( XML ) ;

  wi t h Cl i ent es. Add do  begi n  At t r i butes [ ' i d' ] : = ' 3' ;  Nombr e : = ' FERNANDO RUI Z BERNAL' ;  Ni f : = ' 58965478T' ;  Sal dopt e : = ' 150. 66' ;  Di aspago : = ' 45' ;  end;

  XML. SaveToFi l e( Extr act Fi l ePath( Appl i cat i on. ExeName ) +' c l i ent es5. xml ' ) ;end;

He cargado el documento XML como siempre y utilizando la interfazIXMLClientesType cargo la lista de clientes del documento clientes.xml.Después doy de alta un cliente y grabo el documento como clientes5.xml.

Una cosa cosa interesante que se puede hacer es activar la propiedaddoAutoSave que está dentro de la propiedad Options del componenteXMLDocument lo que implica que el documento se guardará automáticamenteconforme vayamos modificando los elementos del documento XML.

La ventaja de convertir nuestro documento XML en una interfaz persistente esque ahora podemos manejar la lista de clientes como si fuera una base de

datos utilizando los métodos get y set de cada uno de los campos así como lasrutinas para alta, baja o modificación de elementos dentro del documento.Sólo es cuestión de echarle una mirada a la nueva unidad creada para ver lasposibiliades de la misma.

Pruebas realizadas en Delphi 7.

Page 223: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 223/306

Creando informes con Rave Reports (I)Rave Reports es una herramienta externa asociada a Delphi que permite lacreación de informes a partir de una base de datos. Se pueden crear todavariedad de informes, desde un simple folio con una banda hasta otros máscomplejos con múltiples bandas. Incluye las siguientes características:

- Integración de gráficos.- Párrafos justificados.- Modificación de opciones de impresora.- Control de las fuentes.- Vista previa antes de imprimir.- Exportación a PDF, HTML, RTF y archivos de texto.

CREANDO UN INFORME PARA NUESTRA APLICACION

Vamos a suponer que tengo una base de datos creada en Firebird 2.0 cuyonombre es BaseDatos.fdb. Esta base de datos tiene la siguiente tabla:

CREATE TABLE CLIENTES (ID INTEGER NOT NULL,NOMBRE VARCHAR(100),DIRECCION VARCHAR(100),POBLACION VARCHAR(50),CP VARCHAR(5),PROVINCIA VARCHAR(50),IMPORTEPTE DOUBLE PRECISION,

NIF VARCHAR(15),ALTA TIMESTAMP,HORALLAMAR TIME,ULTIMOPAGO DATE,NUMPAGOS INTEGER,OBSERVACIONES BLOB);

Entonces vamos a crear un informe para sacar un listado de clientes. Para elloabrimos el diseñador de informes Rave desde Delphi a través de la opciónTools -> Rave Designer. Nos aparecera el siguiente entorno:

Page 224: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 224/306

Los informes que genera este diseñador de informes tienen extensión RAV.Por defecto, el nombre del informe que vamos a crear se llama Project1.rav.Le vamos a cambiar el nombre seleccionando File -> Save As... con el nombrelistado_clientes.rav.

Antes de comenzar a diseñar el listado tenemos que establecer una conexióncon nuestra base de datos Firebird llamada BaseDatos.fdb para poder extraerla información.

CONFIGURANDO LA CONEXION ODBC

Como vamos a utilizar una conexión ADO para vincularlo a este informe,primero tenemos que establecer el orígen de datos ODBC. Al ser mi base dedatos es Firebird me he instalado el driver ODBC que se encuentra en lapágina:

http://www.firebirdsql.org/

El driver se encuentra en el apartado Development -> Firebird ODBC Driver.

Aparte de utilizar este driver para conexiones ADO nos puede ser muy útil

para vincular nuestras bases de datos a prográmas ofimáticos tales comoWord, Excel y Access.

Una vez descargado e instalado el driver ODBC vamos a realizar los siguientespasos:

1. Abrimos el panel de control de Windows.

2. Hacemos doble clic en el icono Herramientas Administrativas.

3. Hacemos doble clic en el icono Orígenes de datos (ODBC).

4. Teniendo seleccionada la pestaña DSN de usuario pulsamos el botón

Page 225: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 225/306

Agregar.

5. Seleccionamos Firebird/Interbase(r) Driver y pulsamos el botón Finalizar.

6. Se abrirá la siguiente ventana:

7. Vamos a rellenar los siguientes parámetros:

Nombre de Origen de Datos (DSN): BaseDatos_RaveDescription: BaseDatos_RaveBase de Datos: D:\Desarrollo\DelphiAlLimite\Rave\BASEDATOS.FDB (dondeesté el archivo FDB)Cliente: C:\Firebird2\bin\fbclient.dll (donde esté instalado Firebird 2.0)Cuenta de Base de Datos: SYSDBAContraseña: masterkey

8. Pulsamos el botón Comprobar conexión y debe mostrar ¡La conexión fue

exitosa!

9. Pulsamos el botón Aceptar y veremos en la ventana anterior nuestra nuevaconexión: BaseDatos_Rave.

10. Pulsamos el botón Aceptar y cerramos el panel de control de Windows.

Una vez configurada la conexión ODBC en Windows vamos a vincularla anuestro informe.

CONECTANDO CON LA BASE DE DATOS DESDE RAVE REPORTS

Para establecer una conexión con nuestra base de datos Firebird nos tenemos

Page 226: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 226/306

que ir a la barra de botones que se encuentra en la esquina superior izquierdadel diseñador de informes Rave y pulsar el botón New Data Object:

Al pulsarlo nos aparecen las siguientes opciones:

Seleccionamos Database Connection y pulsamos Next. La conexión sólo sepuede realizar a través de ADO, BDE o DBExpress, aunque pueden añadirseotros drivers de conexión dentro del directorio:

C:\Archivos de programa\Borland\Delphi7\Rave5\DataLinks\

Seleccionamos ADO y pulsamos el botón Finish. Nos aparecerá la siguiente

ventana:

Seleccionamos la opción Use Connection String y pulsamos el botón [...] queaparece a la derecha. Aparecerá la ventana de las propiedades de vínculo dedatos:

Page 227: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 227/306

Seleccionamos la pestaña Conexión y en la opción Usar el nombre de origende datos seleccionamos BaseDatos_Rave. Pulsamos el botón Probar Conexióny nos saldrá el mensaje: La prueba de conexión fue satisfactoria. Pulsamosel botón Aceptar y en la ventana anterior pulsamos Ok.

Después de mucho sacrificio (es más fácil recompilar el núcleo de Linux) yatenemos en el diseñador Rave la conexión directa con nuestra base de datos:

Si seleccionamos Database1 a la izquierda veremos sus propiedades:

Page 228: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 228/306

Vamos a renombar las propiedades FullName y Name a BaseDatos.

En el próximo artículo vamos a vincular la tabla clientes y vamos a realizar elinforme.

Pruebas realizadas con Firebird 2.0 y Rave Reports 5.0.

Creando informes con Rave Reports (II)Una vez establecida la conexión con la base de datos vamos a crear un objetoDriverDataView para vincularlo a nuestro informe.

CREANDO UN VINCULO PARA LA TABLA CLIENTES

Para enlazar la tabla de clientes hay que hacer lo siguiente:

1. Pulsamos el botón NewDataObject y pulsamos el botón Next.

2. Seleccionamos Driver Data View y pulsamos el botón Next.

3. Seleccionamos BaseDatos y pulsamos el botón Finish. Se abrirá la siguienteventana:

4. Pulsamos el botón Editor y se mostrará de esta forma:

Page 229: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 229/306

Page 230: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 230/306

2. Seleccionamos Clientes y pulsamos Next:

3. Vamos a seleccionar los campos ID, NOMBRE, CP, PROVINCIA y NIF.Pulsamos Ok.

Page 231: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 231/306

4. En la siguiente ventanase nos da la opción de ordenar los campos a nuestro gusto. En este caso hepuesto el NIF después del NOMBRE. Al pulsar Next nos aparecera estaventana:

5. En el campo Report Title ponemos Listado de Clientes. También podemosconfigurar los márgenes a nuestro gusto, aunque en este caso lo dejamoscomo está y pulsamos el botón Next. La siguiente ventana es esta:

Page 232: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 232/306

6. Aquí podemos cambiar la fuente para el título, la cabecera de los campos yla fuente de los campos. En la cabecera de los campos le he configurado unafuente a Tahoma tamaño 10 y negrita. Para los campos lo he dejado enTahoma 10. Al pulsar el botón Generate y aparecerá lo siguiente:

Con esto ya tenemos hecho el listado. Si pulsamos el botón Execute Report(la impresora) y seleccionamos Preview podemos ver como queda la vista

previa del listado.Como puede verse a primera vista los campos nos aparecen demasiadopegados los unos de los otros. Lo que hay que hacer es ajustar a mano en eleditor las columnas para que queden a nuestro gusto.

Una de las cosas que mas me gustan de este editor es que se puedenseleccionar a la vez componentes de distintas bandas para moverlos oredimensionarlos. Con la tecla Mayúsculas pulsada y con los cursores delteclado podemos ampliar o reducir el tamaño de los campos. Y con la teclaControl pulsada se pueden mover varios campos a la vez. El comportamiento

es muy similar al editor de formularios de Delphi.

Page 233: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 233/306

ANALIZANDO EL INFORME CREADO

Si nos fijamos bien en el informe creado aparecen los siguientes objetos:

1. Primero crea un objeto Region (pestaña Report) para colocar todo el

diseño encima.

2. Dentro de la región creada introduce tres bandas mediante el componenteBand que se encuentra en la pestaña Report:

- ClientesTitleBand: La primera banda para el título Listado de Clientes.

- ClientesBand: La segunda banda contiene los títulos de los campos.

- ClientesDataBand: La tercera banda incluye los campos que se van aimprimir. Esta última banda es de tipo DataBand (también en la pestaña

Report).

Lo bueno de tenerlo todo dentro de un componente Region es que si tenemosproblemas con los márgenes de impresión, ajustando la región a nuestro gustono aperecán los típicos problemas de folios duplicados en impresoras laser omultifunción que hace que salgan dos copias.

En el siguiente artículo vamos a crear un listado para tablas maestro/detalle.

Pruebas realizadas con Firebird 2.0 y Rave Reports 5.0.

Creando informes con Rave Reports (III)Una vez que sabemos crear listados a partir de una tabla vamos a ver como seharía para tablas maestro/detalle, como puede ser una factura.

Nuestra factura consta de dos tablas. La cabecera de la factura estáimplementada en esta tabla:

CREATE TABLE FACTURAS (

I D I NTEGER NOT NULL,I DCLI ENTE I NTEGER,BASEI MP DOUBLE PRECI SI ON,I VA DOUBLE PRECI SI ON,I MPORTEI VA DOUBLE PRECI SI ON,

 TOTAL DOUBLE PRECI SI ON,PRI MARY KEY ( I D)) ;

Y el detalle de la misma en esta otra:

CREATE TABLE DETALLEFAC (I D I NTEGER NOT NULL,I DFACTURA I NTEGER,UNI DADES DOUBLE PRECI SI ON,

Page 234: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 234/306

ARTI CULO VARCHAR( 100) ,PRECI O DOUBLE PRECI SI ON,

 TOTALLI NEA DOUBLE PRECI SI ON,PRI MARY KEY ( I D)) ;

Cada línea de detalle está vinculada a la cabecera mediante el campoIDFACTURA.

VINCULANDO LAS TABLAS MAESTRO/DETALLE AL INFORME

Primero tenemos que incluir dos objetos DriverDataView para las tablas decabecera y detalle. Hacemos lo siguiente:

1. Pulsamos el botón New Data Object.

2. Seleccionamos Driver Data View y pulsamos el botón Next.

3. Seleccionamos BaseDatos y pulsamos el botón Finish.

4. En la ventana que aparece pulsamos el botón Editor.

5. Escribimos una SQL que nos permita traernos los datos del cliente:

SELECT * FROM FACTURASLEFT J OI N CLI ENTES ON FACTURAS. I DCLI ENTE=CLI ENTES. I D

6. Pulsamos el botón Ok.

7. Estando en el editor seleccionamos a la derecha el nuevo DriverDataView1creado y a la izquierda ponemos su propiedad Name a Facturas.

Siguiendo los mismos pasos tenemos que vincular la tabla DETALLEFAC con laSQL:

SELECT * FROM DETALLEFAC

CREANDO INFORMES PARA TABLAS MAESTRO/DETALLE

En esta ocasión vamos a ver como crear una factura utilizando directamenteel editor sin asistentes. El resultado sería el siguiente:

Page 235: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 235/306

Los pasos para crear esta factura serían los siguientes:

1. Insertamos un componente Region que esta situado en la pestaña Report.

2. Dentro de la región creada insertamos tres componentes de tipo DataBand(pestaña Report).

3. A las tres bandas creadas las vamos a llamar Cabecera, Detalle y Pie.

4. A la propiedad DataView de la bandas Cabecera y Pie le asignamos la tablaFacturas.

5. A la propiedad DataView de la banda Detalle le asignamos la tablaDetalleFac.

6. La cabecera se compone de:

- 6 etiquetas en negrita de tipo Text situadas en la pestaña Standard.

- 8 campos de tipo DataText situados en la pestaña Report para los campos:ID, NOMBRE, DIRECCION, POBLACION, CP, PROVINCIA y NIF.

- 4 líneas utilizando el componente Line situado en la pestaña Drawing.

7. El detalle se compone de:

- 4 componetes de tipo DataText para los campos UNIDADES, ARTICULO,

Page 236: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 236/306

Page 237: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 237/306

Creando informes con Rave Reports (IV)Después de crear los informes utilizando el programa Rave Designer ahoravamos a ver como abrir esos informes con extensión RAV dentro de nuestroproyecto de Delphi.

ABRIENDO INFORMES RAVE DESDE DELPHI

Para llamar a los informes creados desde Delphi vamos a insertar en nuestroformulario los componentes de la clase TRvProject y TRvSystem situados enla pestaña Rave. Al objeto RvProject lo vamos a llamar Proyecto y alcomponente RvSystem lo llamaremos Sistema.

También vamos a vincular el componente Sistema al componente Proyecto através de su propiedad Engine. Como estamos utilizando una conexión ADOtenemos que añadir en la sección uses de nuestro formulario la unidadRvDLADO, porque en caso contrario nos mostraría el error:

No DATA Li nk dr i ver s have been l oaded.

El objeto RvSystem realmente no es obligatorio utilizarlo, pero esrecomendable porque con el mismo podemos personalizar todo lo relacionadocon la impresión de documentos, desde el título, hasta los mensajes, la formade como mostrar la vista previa, etc.

Pues con esto ya podemos lanzar la vista previa del informe:

begi n  Pr oyect o. Pr oj ect Fi l e : = Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' f actura. rav' ;  Pr oyect o. Execut e;end;

Primero se nos abre una ventana para seleccionar el tipo de impresión y laimpresora por donde va a salir:

Page 238: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 238/306

Page 239: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 239/306

Naturalmente damos por hecho de que tiene instalado el motor de bases dedatos Firebird 2.0.

En el próximo artículo seguiremos viendo más cosas interesantes relacionadascon Rave Reports.

Pruebas realizadas en Delphi 7 y Firebird 2.0.

Creando informes con Rave Reports (V)Hasta ahora hemos visto como crear informes utilizando una conexión ADO,pero surgen los siguientes inconvenientes:

- Hay que instalar un driver ODBC en el cliente.

- Hay que configurar la conexión ODBC para que apunte a nuestra base de

datos.- Cada vez que el programa va a imprimir se abre una nueva conexión a labase de datos (imaginaos 20 usuarios trabajando a la vez).

- Puede faltar alguna DLL a la hora de instalar el programa en el clienterelacionada con conexiones ODBC.

Para solucionar esto vamos a ver como utilizar los objetos Direct Data Viewen el diseñador Rave Reports que nos van a evitar todos estos problemas.

PASANDO DE ADO, BDE y DBX

Los componentes Direct Data View que se encuentran en el editor RaveDesigner permiten vincular los informes directamente con los componentesTRvDataSetConnection que se encuentren en nuestro programa Delphi, sintener que establecer ninguna conexión de base de datos ni preocuparnos pordrivers externos.

Funciona exactamente igual que los informes generados por QuickReport,donde el origen de datos es nuestro mismo programa, ahorrándonosconexiones extras. Vamos a ver un ejemplo de cómo generar un listado declientes realizando una conexión directa entre Delphi y Rave Reports.

Los siguientes pasos serían para crear una conexión a Firebird desde Delphi:

1. Abrimos Delphi y creamos un formulario de prueba.

2. Insertamos un componente de la clase TIBDatabase en el formulario con elnombre BaseDatos.

3. Hacemos doble clic en el componente TIBDatabase y rellenamos los

siguientes datos:

Page 240: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 240/306

Connection: RemoteServer: 127.0.0.1Database: D:\Desarrollo\DelphiAlLimite\Rave\BaseDatos.fdbUserName: SYSDBAPassword: masterkey

Login Prompt: Desactivado

4. Añadimos un componente TIBTransaction y lo llamamos Transaccion.

5. Asociamos el componente Transaccion al componente BaseDatos a travésde su propiedad DefaultTransaction.

6. En la propiedad DefaultDatabase del objeto TIBTransaction seleccionamosBaseDatos.

7. Insertamos en el formulario un componente TIBQuery con el nombre

TClientes. En su propiedad Database seleccionamos BaseDatos. Y en supropiedad SQL ponemos:

SELECT * FROM CLI ENTES

8. Hacemos doble clic sobre el componente TIBQuery y pulsamos lacombinación de teclas CTRL + A para traernos los campos. Al hacer esto habrádejado la base de datos abierta (BaseDatos). La dejamos así por ahora.

9. Insertamos en el formulario un componente TRvProject y lo llamamosProyecto.

10. Insertamos otro componente de TRvSystem y lo llamamos Sistema.Asociamos este componente con TRvProject con su propiedad Engine.

11. Añadimos al formulario un componente TRvDataSetConnection quellamaremos DSClientes. En su propiedad DataSet elegimos TClientes.

Con toda esta movida ya tenemos una conexión a una base de datos Firebird yun DataSet especial para que Rave pueda recoger los datos.

CREANDO EL INFORME EN RAVE REPORTS

Dejando Delphi abierto con nuestra base de datos conectada (BaseDatos)ejecutamos el programa Rave Designer. Una vez estamos en el diseñadorvamos a efectuar los siguiente pasos:

1. Pulsamos el botón New Data Object.

2. Seleccionamos Direct Data View y pulsamos Next.

3. Nos aparecerá una lista de conexiones abiertas en Delphi donde se esténutilizando componente TRvDataSetConnection. En nuestro caso aparecera

Page 241: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 241/306

DSClientes (DT). Lo seleccionamos y pulsamos Finish.

4. Renombramos el objeto DataView1 creado y lo llamamos Clientes.

5. Seleccionamos en el menú de arriba Tools -> Report Wizards -> Simple

Table.

6. Seleccionamos Clientes y pulsamos Next.

7. Seleccionamos los campos ID, NOMBRE, DIRECCION, NIF, PROVINCIA ypulsamos Next.

8. Dejamos la ordenación como está y pulsamos Next.

9. En Report Title ponemos Listado de Clientes y pulsamos Next.

10. Seleccionamos la fuente a nuestro gusto y pulsamos Generate.

11. Guardamos el informe en el mismo directorio que nuestro proyecto deDelphi con el nombre Clientes.rav.

Si pulsamos el botón de la impresora y mostramos una vista previa veremoscomo se trae directamente de Delphi los datos y los imprime.

La potencia que nos da esta forma de trabajar es enorme, ya que es nuestroproyecto Delphi el que suministra los datos, siendo posible filtrar y ordenar

tablas de bases de datos a nuestro antojo sin que Rave Reports no tenga queenterarse de donde proceden los datos.

Ya podemos cerrar el programa Rave Designer. Ahora vamos a ver como lanzarel informe desde Delphi.

LANZANDO EL INFORME RAVE DESDE DELPHI

En Delphi ya podemos desconectar la base de datos en tiempo de diseño. Paralanzar el listado hacemos lo siguiente:

begi n  BaseDat os. DatabaseName : = ' 127. 0. 0. 1: ' + Ext r actFi l ePath(Appl i cat i on. ExeName ) + ' BaseDatos. f db' ;

  t r y  BaseDat os. Open;  except  rai se;  end;

  TCl i ent es. Open;  Pr oyect o. Pr oj ect Fi l e : = Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' cl i entes . rav' ;  Pr oyect o. Execut e;end;

Page 242: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 242/306

Page 243: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 243/306

  / / Lanzamos el i nf orme  Pr oyect o. Pr oj ect Fi l e : = Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' f actura. rav' ;  Pr oyect o. Execut e;end;

Se supone que hemos creado en el formulario de Delphi dos componentesTIBQuery llamados TFactura y TDetalleFac para la cabecera y detalle de lafactura.

Con esto finalizamos la introducción al editor de informes Rave Reports.

Pruebas realizadas en Delphi 7, Rave Reports 5.0 y Firebird 2.0.

Como manejar excepciones en Delphi (I)

Las excepciones son condiciones excepcionales que se producen en nuestraaplicación en tiempo de ejecución y que requieren un tratamiento especial.Un ejemplo de excepciones podría ser las divisiones por cero o losdesbordamientos de memoria. El tratamiento de excepciones proporciona unaforma estándar de controlar los errores, descubriendo anticipadamente losproblemas y posibilitando al programador anticiparse a los fallos que puedanocurrir.

Cuando ocurre un error en un programa, se produce una excepción, lo quesignifica que crea un objeto excepción y situa el puntero de la pila en elprimer punto donde se ha provocado la excepción. El objeto excepción

contiene información sobre todo lo que ha ocurrido.

Esto nos permite crear aplicaciones más robustas ya que se puede llegar aaveriguar el lugar en concreto donde se ha producido el error,particularmente en áreas donde los errores puedan causar la perdida de datosy recursos del sistema.

Cuando creamos una respuesta a la excepción tenemos que hacerlo en dentrode bloques de código, los cuales se llaman bloques de código protegidos.

DEFINIENDO BLOQUES DE CODIGO PROTEGIDOS

Los bloques de código protegidos comienzan con la palabra reservada try. Siocurre algún error dentro del bloque de código protegido, el tratamiento delerror se introduce en un bloque de código que comienza con except.

Vamos a ver un ejemplo que provoca una excepción al abrir un archivo que noexiste:

var

  F: Text Fi l e;begi n  Assi gnFi l e( F, ' c: \ nosexi ste. tx t ' ) ;

Page 244: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 244/306

  t r y  Reset ( F ) ;  except  on E: Except i on do  Appl i cat i on. MessageBox( PChar( E. Message ) , ' Er r or' , MB_I CONSTOP

) ;  end;end;

La primera parte de un bloque protegido comienza con la palabra try. Elbloque try contiene el código que potencialmente puede provocar laexcepción. Al provocar la excepción saltará directamente al comienzo delbloque de código que comienza con la palabra reservada except.

Como puede apreciarse en el código anterior hemos creado un objeto E querepresenta la excepción creada. El objeto E pertenece a la clase Exception

que a su vez hereda directamente de la clase TObject. Este objeto contienepropiedades y métodos para manejar la excepción provocada.

PROVOCANCO NUESTRA PROPIA EXCEPCION

Nosotros también podemos crear nuestras propias excepciones que heredende la clase Exception. Por ejemplo, voy a crear una excepción si una variablede tipo string está vacía. Primero defino el tipo especial de excepción:

type  ECadenaVaci a = cl ass( Except i on ) ;

Y ahora la provoco en el programa:

var  sCadena: St r i ng;begi n  i f sCadena = ' ' t hen  r ai se ECadenaVaci a. Cr eat e( ' Cadena vaci a. ' ) ;end;

El comando raise provoca a propósito la excepción para detener la ejecucióndel programa. No es necesario que creemos nuestros tipos de excepción.También podía haber sido así:

  i f sCadena = ' ' t hen  r ai se Except i on. Cr eat e( ' cadena vaci a' ) ;

Cuando se provoca una excepción la variable global ErrorAddr declaradadentro de la unidad System contiene un puntero a la dirección de memoriadonde se ha provocado el error. Esta variable es de sólo lectura a títuloinformativo.

CONTROLANDO UNA EXCEPCION SEGUN SU TIPO

Dentro de un mismo bloque de código podemos controlar que tipo deexcepción queremos controlar. Por ejemplo, si ejecutamos el código:

Page 245: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 245/306

var  s : s t r i ng;  i : I nteger ;begi n  s : = ' pr ueba' ;

  i : = St rToI nt ( s ) ;end;

Mostrará el error:

' pr ueba' not i s a val i d i nt eger val ue

Si queremos saber el tipo de excepción podemos sacarla por pantalla:

var  s : s t r i ng;  i : I nteger ;begi n  s : = ' pr ueba' ;

  t r y  i : = St rToI nt ( s ) ;  except  on E: Except i on do  Appl i cat i on. MessageBox( PChar( E. Cl assName + ' : ' + E. Message ) ,' Er r or ' , MB_I CONSTOP ) ;  end;end;

Hemos incluido en el mensaje de la excepción la clase y el mensaje de error.

Este sería el resultado:EConver t Er r or : ' pr ueba' not i s a val i d i nt eger val ue

Así, mediante la propiedad ClassName de la clase Exception podemosaveriguar la clase a la que pertenece la excepción. Ahora mediante lasentencia on podemos aislar la excepción de la forma:

on tipo do sentencia

En nuestro caso sería así:

  t r y  i : = St rToI nt ( s ) ;  except  on E: EConver t Er r or do  Appl i cat i on. MessageBox( ' Er r or de conver si ón' , ' Er r or ' ,MB_I CONSTOP )  el se  Appl i cat i on. MessageBox( ' Er r or desconoci do' , ' Er r or ' ,MB_I CONSTOP ) ;  end;

Si se produjera una excepción que no fuese de la clase EConvertError

mostraría el mensaje Error desconocido.

Page 246: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 246/306

De este modo podemos aislar dentro de un mismo bloque de código losdistintos tipos de excepción que se puedan producir.

Otro ejemplo podría ser la división de dos números enteros:

t r y  Resul t ado = b di v c;except  on EZer oDi vi de do Resul t ado : = MAXI NT;  on EI nt Over f l ow do Resul t ado : = 0;  on EI nt Under f l ow do Resul t ado : = 0;end;

Aquí hemos aislado cada uno de los casos que se puedan producir al dividir dosnúmeros enteros, alterando el resultado a nuestro antojo.

En el próximo artículo seguiremos viendo más características de las

excepciones.Pruebas realizadas en Delphi 7.

Como manejar excepciones en Delphi (II)Una vez que hemos visto lo que es una excepción y cómo proteger nuestrocódigo usando bloques protegidos vamos a pasar a ver como pueden meterseunas excepciones dentro de otras para dar más seguridad a nuestro código. Eslo que se llaman excepciones anidadas.

EXCEPCIONES ANIDADAS

Vamos a ver un ejemplo de como anidar una excepción dentro de otra:

var  F: Text Fi l e;begi n  Assi gnFi l e( F, ' C: \ noexi ste. t xt ' ) ;

  t r y  Reset ( F ) ;

  t r y  Cl oseFi l e( F ) ;  except  on E: Except i on do  ShowMessage( ' Excepci ón 2: ' + E. Message ) ;  end;

  except  on E: Except i on do  ShowMessage( ' Excepci ón 1: ' + E. Message ) ;

  end;end;

Page 247: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 247/306

En este ejemplo hemos metido un bloque protegido dentro de otro, dondecada uno controla su propia excepción. En este caso provocaría la excepción1 ya que el archivo no existe.

DETENIENDO LA EXCEPCION

Cuando se provoca una excepción, una vez la hemos procesado con lasentencia on E: Exception, la ejecución continua hacia el siguiente bloque decódigo. Si queremos detener la ejecución del programa debemos utilizar elcomando raise:

var  F: Text Fi l e;begi n  Assi gnFi l e( F, ' C: \ noexi ste. t xt ' ) ;  ShowMessage( ' 1' ) ;

  t r y  Reset ( F ) ;

  except  on E: Except i on do  rai se;  end;

  ShowMessage( ' 2' ) ;end;

En este ejemplo nunca llegaría a ejecutarse el segundo ShowMessage ya queraise detiene la ejecución del procedimiento.

FORZANDO A QUE FINALICE LA EJECUCION

Hay bloques de código en los cuales cuando se provoca una excepción nipodemos continuar con la ejecución ni podemos cortar la ejecución. Porejemplo, supongamos que abro un archivo en módo sólo lectura e intentoescribir en el mismo. Esto provocaría una excepción, pero lo que no puedohacer es detener en seco la ejecución del programa ya que hay que cerrar elarchivo que hemos abierto.

Para solucionar esto, los bloques protegidos permiten finalizar la ejecución enel caso de que se produzca una excepción mediante la claúsula finally. Ennuestro ejemplo nos interesa que se cierre el archivo abierto:

var  F: Text Fi l e;begi n  Assi gnFi l e( F, ' C: \ pr ueba. t xt ' ) ;

  t r y  Reset ( F ) ;

  t r y  Wr i t eLn( F, ' i nt ent ando escr i bi r ' ) ;

Page 248: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 248/306

  f i nal l y  ShowMessage( ' Fi nal i zando. . . ' ) ;  Cl oseFi l e( F ) ;  end;

  except

  on E: Except i on do  rai se;  end;end;

Tenemos dos excepciones anidadas: una para abrir el archivo con unasentencia except que detiene la ejecución y otra dentro que utiliza lasentencia finally para cerrar el archivo en el caso de que se produzca unerror.

TRATANDO EXCEPCIONES EN COMPONENTES VCL

Los componentes VCL también pueden provocar muchas excepciones si nosabemos utilizarlos correctamente. Un error típico es el intentar acceder a unelemento que no existe dentro de un componente ListBox llamado Lista:

begi n  Li st a. I t ems. Add( ' PABLO' ) ;  Li st a. I t ems. Add( ' MARI A' ) ;  Li st a. I t ems. Add( ' CARLOS' ) ;

  t r y  ShowMessage( Li st a. I t ems[ 4] ) ;  except

  on E: ESt r i ngLi st Er r or do  ShowMessage( ' La l i st a sól o t i ene t r es el ement os. ' ) ;  end;end;

En este caso se ha provocado una excepción de la clase EStringListError,aunque bien se podría haber controlado de este modo:

  t r y  ShowMessage( Li st a. I t ems[ 4] ) ;  except  on E: Except i on do  ShowMessage( ' La l i st a sól o t i ene t r es el ement os. ' ) ;  end;

Los componentes VCL disponen principalmente de estas clases deexcepciones:

EAbort: Finaliza la secuencia de eventos sin mostrar el mensaje de error.

EAccessViolation: Comprueba errores de acceso a memoria inválidos.

EBitsError: Previene intentos para acceder a arrays de elementos booleanos.

EComponentError: Nos informa de un intento inválido de registar o renombar

Page 249: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 249/306

un componente.

EConvertError: Muestra un error al convertir objetos o cadenas de textostring.

EDatabaseError: Especifica un error de acceso a bases de datos.

EDBEditError: Error al introducir datos incompatibles con una máscara detexto.

EDivByZero: Errores de división por cero.

EExternalException: Significa que no reconoce el tipo de excepción (viene defuera).

EIntOutError: Representa un error de entrada/salida a archivos.

EIntOverflow: Especifica que se ha provocado un desbordamiento de un tipode dato.

EInvalidCast: Comprueba un error de conversión de tipos.

EInvalidGraphic: Indica un intento de trabajar con gráficos que tienen unformato desconocido.

EInvalidOperation: Ocurre cuando se ha intentado realizar una operación

inválida sobre un componente.EInvalidPointer: Se produce en operaciones con punteros inválidos.

EMenuError: Controla todos los errores relacionados con componentes demenú.

EOleCtrlError: Detecta problemas con controles ActiveX.

EOleError: Especifica errores de automatización de objetos OLE.

EPrinterError: Errores al imprimir.

EPropertyError: Ocurre cuando se intenta asignar un valor erroneo a unapropiedad del componente.

ERangeError: Indica si se intenta asignar un número entero demasiado grandea una propiedad.

ERegistryExcepcion: Controla los errores en el resigtro.

EZeroDivide: Controla los errores de división para valores reales.

EXCEPCIONES SILENCIOSAS

Page 250: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 250/306

Para dar un toque profesional a un programa hay ocasiones en que nosinteresa controlar la excepción pero que no se entere el usuario delprograma. Lo que no se puede hacer es abandonar la excepción con loscomandos Break o con Exit ya que puede ser peor el remedio que la

enfermedad.

Para salir elegantemente de una excepción hay que utilizar el comandoAbort:

t r y  { sent enci as }except  Abort ;end;

De este modo se controla la excepción y el usuario no ve nada en pantalla.

Con esto finalizamos el tratamiento de excepciones en Delphi.

Pruebas realizadas en Delphi 7.

Creando aplicaciones multicapa (I)En este artículo que estará separado en varias partes vamos a ver como crearaplicaciones de bases de datos multicapa cliente/servidor. Este tipo deaplicaciones esta dividido en unidades lógicas, llamadas capas, las cuales seejecutan en distintas máquinas. Las aplicaciones multicapa distribuyen los

datos y se comunican en una red de área local o bien sobre Internet. Estoproporciona muchas ventajas tales como centralizar la lógica de negocio enun sólo servidor y donde varios clientes van tirando de él. Además podemoscrear aplicaciones que comuniquen varios centros de trabajo se esténseparados geográficamente a través de Internet.

Una aplicación multicapa queda particionada de la siguiente manera:

- Aplicación Cliente: se encarga de mostrar la interfaz de usuario.

- Servidor de aplicaciones: reside en la red local central y es accesible por

todos los clientes donde reciben datos directamente de este servidor.

- Servidor de bases de datos: en este servidor es donde está instalado elmotor de bases de datos (Interbase, Firebird, Oracle, etc.), aunque el servidorde aplicaciones y el servidor de bases de datos pueden ser la misma máquina.

En este modelo a tres capas los clientes sólo pueden comunicarse con elservidor de aplicaciones y en ningún caso directamente con el motor de basesde datos, como ocurre en las aplicaciones cliente/servidor habituales.

Este tipo de aplicaciones multicapa no tiene porque estar compuesto sólo detres capas, podría constar de varios servidores de bases de datos y servidores

Page 251: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 251/306

de aplicaciones.

VENTAJAS DE CREAR UN MODELO MULTICAPA

En este modelo de bases de datos la aplicación cliente sólo se dedica a

mostrar los datos al usuario, no sabe nada sobre como los datos sonactualizados y mantenidos.

El servidor de aplicaciones (capa media) coordina y procesa las peticiones yactualizaciones de múltiples clientes. El servidor maneja todos los detalles,define el conjunto de datos e interactua con el servidor de bases de datos.

Las ventajas de este modelo multicapa son las siguientes:

- Encapsulación de lógica de negocio. Diferentes clientes de la aplicacionpueden acceder al mismo servidor intermedio. Esto permite evitar la

redundancia (y coste de mantenimiento) de duplicar las reglas de negociopara cada aplicación cliente separada.

- Aplicaciones clientes pequeñas. Al delegar las tareas más pesadas en lacapa media las aplicaciones clientes ocupan menos y consumen menosprocesador y memoria, permitiendo instalarse en máquinas de bajorendimiento. Esto trae la ventaja de que por muchos clientes que accedan ala aplicación, el motor de bases de datos sólo tiene una conexión, que vadirectamente al servidor de aplicaciones, evitando así problemas deconcurrencia o latencia de datos entre distintas aplicaciones cliente. Estas

aplicaciones clientes también pueden funcionar a través de Internet ya que suconsumo de ancho de banda es mínimo, al contrario de conectar directamentecon el motor de bases de datos.

- Procesar datos distribuidos. Distribuir el trabajo de una aplicación entrevarias máquinas puede mejorar la ejecución, ya que el balanceo de cargapermite reducir la carga de las máquinas que funcionan como servidor deaplicaciones. Por ejemplo, si vemos que una aplicación de gestión se relentizapodemos distribuir en una máquina las compras, en otra las ventas y lagestión de recibos en otra.

- Incrementar la seguridad. Podemos aislar la funcionalidad en las capasdando restricciones de seguridad. Esto proporciona unos niveles de seguridadconfigurables y flexibles. Las capas intermedias pueden limitar los puntos deentrada a material protegido, permitiendo controlar el control de acceso másfácilmente. Si usamos HTTP o COM+, podemos utilizar los modelos deseguridad que soportan.

COMPOSICION DE LAS APLICACIONES DE BASES DE DATOS MULTICAPA

Las aplicaciones multicapa usan los componentes de la pestaña DataSnap, losde la pestaña Data Access y a veces los de la pestaña WebServices, más un

módulo de datos remoto que es creado por el asistente de la pestañaMultitier o WebServices del cuadro de diálogo New Items. Estos componentes

Page 252: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 252/306

Page 253: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 253/306

4. El cliente decodifica el paquete recibido y muestra los datos al usuario.

5. Cuando el usuario de la aplicación cliente realiza modificaciones sobre losdatos (añadir, borrar o modificar registros) estas modificaciones sonalmacenadas en un log temporal.

6. Finalmente los clientes envian las actualizaciones al servidor deaplicaciones, el cual responde normalmente a cada acción del usuario. Paraaplicar los cambios, los paquetes de los clientes leerán y cambiarán el logantes de enviar sus paquetes de datos al servidor.

7. El servidor de aplicaciones decodifica el paquete y efectua los cambios (enel contexto de una transacción cuando sea apropiada). Si un registro no puedeser actualizado (por ejemplo, porque otra aplicación cambie el registro antesde que el cliente lo solicite y después de que el cliente haya aplicado loscambios), el servidor de aplicaciones intenta reconciliar los cambios de los

clientes con los datos actuales, y guarda los registros que podrían no seractualizados. Este proceso de guardar y resolver problemas con los registroses llamado resolving.

8. Cuando el servidor de aplicaciones finaliza el proceso de actualización deregistros, devuelve los registros no actualizados al cliente para que puedaresolverlos por el mismo.

9. El cliente resuelve los registros que el servidor no ha podido actualizar. Haymuchas maneras de que un cliente pueda resolver estos registros.

Generalmente el cliente intenta corregir la situación asegurándose de que losregistros estén validados antes de enviarlos. Si la situación puede serrectificada, entonces vuelve a enviar los datos al servidor.

10. El cliente desempaqueta los datos que vienen del servidor y refresca sucaché local.

En la siguiente parte de este artículo veremos la estructura de una aplicacióncliente.

Pruebas realizadas en Delphi 7.

Creando aplicaciones multicapa (II)Después de tener una visión global de cómo funcionan las aplicacionesmulticapa vamos a ver cada una de sus partes.

LA ESTRUCTURA DE LA APLICACION CLIENTE

El usuario que va a utilizar la aplicación cliente no notará la diferencia entreuna aplicación cliete/servidor y una aplicación multicapa, ya que el acceso ala información se realiza a través de los componentes estándarTClientDataSet.

Page 254: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 254/306

El componente ClientDataSet se comunica con el proveedor de datos a travésde la interfaz IAppServer. Se pueden seleccionar diferentes protocolos decomunicación según el componente de conexión que se utilice, dondetenemos los siguientes componentes:

Component e Pr ot ocol o- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

 TDCOMConnect i on DCOM TSocket Connect i on Wi ndows socket s ( TCP/ I P) TWebConnect i on HTTP TSOAPConnect i on SOAP ( HTTP y XML)

LA ESTRUCTURA DE LA APLICACION SERVIDOR 

Una vez instalado el servidor de aplicaciones, cuando se ejecuta por primera

vez no establece una conexión con los clientes. Más bien son los clientes losque inician y mantienen la conexión con el servidor de aplicaciones. Todo estosucede automáticamente sin que tengamos que manejar solicitudes oadministrar interfaces.

La base de un servidor de aplicaciones es el módulo de datos remoto, el cualesta especializado en soportar la interfaz IAppServer (para los servidores deaplicaciones que tienen servicios web, el módulo de datos remoto soporta lainterfaz IAppServerSOAP, y usa como preferencia IAppServer).

Las aplicaciones cliente usan las interfaces de módulos de bases de datos

remotos para comunicarse con los componentes Provider del servidor deaplicaciones. Cuando el módulo de datos remoto usa IAppServerSOAP, elcomponente de conexión se adapta a este para la interfaz IAppServer que elque usa el componente ClientDataSet.

Hay tres tipos de módulos de datos remotos:

TRemoteDataModule: Se usa este tipo si los clientes van a utilizan protocolosDCOM, HTTP, sockets o una conexión OLE hacia el servidor de aplicaciones, amenos que queramos instalar el servidor de aplicaciones con COM+.

TMTSDataModule: Se utiliza este tipo si vamos a crear un servidor deaplicaciones como librería DLL que esté instalada con COM+ (or MTS). Sepuede utilizar este módulo de datos remoto MTS con protocolos DCOM, HTTP,sockets u OLE.

TSoapDataModule: Este es un módulo de datos que implementa una interfazIAppServerSOAP en una aplicación de servicios web. Utilizaremos este tipo demódulo de datos para proveer datos a los clientes con acceso a servicios web.

Si el servidor de aplicaciones es distribuido mediante COM+ (o MTS), el

módulo de datos incluye eventos para cuando el servidor de aplicaciones seaactivado o desactivado. Esto permite conectar automáticamente con los

Page 255: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 255/306

motores de bases de datos cuando se active y desconectarlas cuando sedesactive.

EL CONTENIDO DEL MODULO DE BASES DE DATOS REMOTO

Como cualquier otro módulo de datos, se puede incluir cualquier componenteno visual en el módulo de datos remoto. Pero hay que tener en cuenta ciertosaspectos:

- Para cada dataset que tiene el módulo de datos remoto en los clientes,debemos incluir un DataSetProvider. Un DataSetProvider parte lainformación en paquetes que son enviados a los ClientDataSet y aplican lasactualizaciones de las bases de datos contra el servidor de aplicaciones.

- Para cada documento XML que el módulo de datos remoto envía al cliente,debemos incluir un proveedor XML. Un proveedor XML actua como un

DataSetProvider, exceptuando que la actualización de los datos se efectua através de documentos XML en vez de ir al motor de bases de datos.

No hay que confundir los componentes de conexión a bases de datos con loscomponentes de conexión a servidores de aplicaciones, ya que estos últimosutilizan los componentes de las pestañas DataSnap y WebServices.

MODULOS DE DATOS REMOTOS TRANSACCIONALES

Si vamos a crear un servidor de aplicaciones que va a utilizar los protocolos

COM+ o MTS entonces podemos sacar ventaja de esto creando módulos dedatos transaccionales (Transactional Data Module) en lugar de un módulo dedatos remoto ordinario (Remote Data Module). Esto sólo se puede hacer ensistemas opetarivos Windows 2000 en adelante.

Al utilizar un módulo de datos transaccional tenemos las siguientes ventajas:

- Seguridad: COM+ (o MTS) proporciona unas reglas de seguridad en nuestroservidor de aplicaciones. A los clientes se les asignan reglas, las cualesdeterminan como pueden acceder a la interfaz MTS del módulo de datos.

- Los módulos de datos transaccionales permiten mantener la conexión de losclientes abierta cuando se conectan y desconectan muchas veces hacia elservidor de aplicaciones. De este modo, mediante unas pocas conexionespodemos controlar a muchos clientes que se conectan y se desconectancontinuamente (como si fuera un servidor web).

- Los módulos de datos transaccionales pueden participar en transaccionesque abarquen múltiples bases de datos o incluir funciones que no estánimplementadas en las bases de datos.

- Pudemos crear nuestro servidor de aplicaciones como un módulo de datos

remoto cuya instancia es activada y desactivada según se necesite. Cuando seusa la activación en tiempo real, nuestros módulos de datos remotos son

Page 256: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 256/306

instanciados solamente si los necesitan los clientes. Esto evita de gastarrecursos que no se vayan a utilizar.

Pero no todo son ventajas. Con una simple instancia de un módulo de datos elservidor de aplicaciones se puede manejar todas las llamadas a bases de datos

a través de una simple conexión de bases de datos. Pero si se abusa de esto sepuede crear un cuello de botella y puede impactar en la ejecución cuando haymuchos clientes, con lo tenemos que mantener un equilibrio entre el númerode clientes que van a acceder al servidor de aplicaciones y el número demódulos de datos remotos que se van instanciar.

También puede ocurrir el efecto contrario: si se utilizan múltiples instanciasde un módulo de bases de datos remotas, cada instancia puede mantener unaconexión a bases de datos independiente. De modo que si hay muchasinstancias del módulos de datos remotos se abrirían demasiadas conexionescon la base de datos, disminuyendo el rendimiento del motor de bases de

datos como si fuera la típica aplicación cliente/servidor con muchos usuarios.

AGRUPAMIENDO DE MODULOS DE DATOS REMOTOS

El agrupamiento de objetos (pooling) nos permite crear una caché de módulosde datos remotos que estarán disponibles en memoria para las llamadas de lasaplicaciones cliente. De esta manera se conservarán recursos en el servidor yevitará el tener que instanciar y destruir de memoria los módulos de datosremotos cuando se utilicen o se dejen de utilizar.

Cada vez que no estamos usando el módulo de datos transaccional obtenemostodas las ventajas que proporciona el tener una cache de objetos agrupadostanto si la conexión se efectua a través de DCOM como si es mediante elcomponente TWebConnection. Además podemos limitar el número conexionesa bases de datos.

Cuando el servidor de aplicaciones web recibe una petición del cliente, estelas pasa a su primer módulo de datos remoto disponible en la caché demódulos de datos remotos. Si no hay disponible ninguna instancia de módulode datos remoto, el servidor crea una nueva (no sobrepasando el máximo demódulos especificado por nosotros). Esto proporciona una gran flexibilidad

permitiendo manejar varios clientes a través de un simple instancia demódulo de datos remoto (el cual puede actuar como un cuello de botella) eirá creando nuevas instancias según se vaya necesitando.

Cuando una instancia de un módulo de datos remoto que está en la caché norecibe ninguna petición de clientes durante un tiempo prolongado, esautomáticamente liberado. Esto evita que el servidor de aplicaciones sesature con muchas instancias abiertas.

En la siguiente parte de este artículo abarcaremos los tipos de conexión quese pueden realizar entre las aplicaciones clientes y el servidor de

aplicaciones.

Page 257: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 257/306

Pruebas realizadas en Delphi 7.

Creando aplicaciones multicapa (III)Vamos seguir con la base teórica de como funcionan las aplicacionesmulticapa antes de comenzar a crear un ejemplo práctico.

ELIGIENDO EL PROTOCOLO DE CONEXION

Cada protocolo de comunicación que podemos usar para conectar de lasaplicaciones cliente a las aplicaciones servidor tienen sus propias ventajas einconvenientes. Antes de elegir un protocolo tenemos que considerar queservicio va a prestar la aplicación, cuantos clientes va a ser atendidos, quereglas de negocio se van a establecer, etc.

USANDO CONEXIONES DCOM

DCOM es el protocolo que proporciona el acceso más directo y rápido decomunicación, no requiriendo aplicaciones externas aparte del servidor deaplicaciones.

Este protocolo proporciona servicios de seguridad cuando se utiliza un módulode datos transaccional. Cuando usamos DCOM podemos identificar quien llamaal servidor de aplicaciones (ya sea con COM+ o MTS). Por tanto, es posibledeterminar que reglas de acceso vamos a asignar a las aplicaciones cliente.

Son los clientes los que instancian directamente el módulo de datos remoto

para realizar la comunicación, no requiriendo ninguna aplicación externa quehaga de intermediario.

USANDO CONEXIONES SOCKET

La comunicación mediante sockets nos permiten crear clientes muy ligeros. Sesuele utilizar este protocolo si no tenemos la seguridad que los sistemasclientes soporten DCOM. Los sockets proporcionan un sistema comunicaciónsimple para establecer conexiones entre los clientes y el servidor.

En lugar de instanciar el módulo de datos remoto desde el cliente (comosucede con DCOM), los sockets usan una aplicación separada en el servidor(ScktSrvr.exe), la cual acepta las peticiones de los clientes e instancia elmódulo de datos remoto usando COM. El componente de conexión en elcliente y ScktSrvr.exe en el servidor son los responsables de organizar lasllamadas mediante la interfaz IAppServer.

El programa ScktSrvr.exe también puede ejecutarse como un servicio NT.Para ello habría que registrarlo como servicio usando línea de comandos.También se puede eliminar de la lista de servicios del mismo modo.

Uno de los inconvenientes que tienen los sockets es que no hay protección enel servidor contra fallos en la conexión con los clientes. Mientras este

Page 258: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 258/306

Page 259: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 259/306

CONSTRUYENDO UNA APLICACION MULTICAPA

Resumiendo a grandes rasgos, los pasos generales para crear una aplicación debases de datos multicapa son los siguientes:

1. Crear el servidor de aplicaciones.

2. Registrar el servidor de aplicaciones como un servicio o instalarlo yejecutarlo como una aplicación (recomendado).

3. Crear la aplicación cliente.

El orden de creación es importante. Debemos crear y ejecutar el servidor deaplicaciones antes de crear el cliente. Esto se debe a que cuando vayamos aconstruir la aplicación cliente, en tiempo de diseño tenemos que conectar conel servidor de aplicaciones para realizar pruebas de conexión. Aunque se

también se podría un crear un cliente sin especificar el servidor deaplicaciones en tiempo de diseño, pero no lo recomiendo porque es másincómodo.

Si no estamos creando la aplicación cliente en el mismo equipo que elservidor, y estamos usando una conexión DCOM, podemos registrar el servidorde aplicaciones en la máquina cliente. Esto hace que los componentes deconexión se enteren de donde está el servidor de aplicaciones en tiempo dediseño, así podemos elegir el componente Provider desde el inspector deobjetos.

CREANDO UN SERVIDOR DE APLICACIONES DCOM

La mayor diferencia entre crear un servidor de aplicaciones y la típicaaplicación de bases de datos cliente/servidor reside en el módulo de datosremoto. Vamos a crear una aplicación EXE que va a hacer de servidor deaplicaciones para una base de datos Firebird 2.0 que tiene una tabla llamadaCLIENTES.

Para crear un servidor de aplicaciones, hay que seguir los pasos:

1. Crear un nuevo proyecto: File -> New -> Application.

2. Guardar el proyecto como ServidorDatos.exe

3. Añadimos un módulo de datos remoto al proyecto: File -> New -> Other.

4. Nos vamos a la pestaña Multitier, seleccionamos Remote Data Module ypulsamos Ok.

5. Rellenamos los campos:

CoClass Name: ServidorDCOM

Page 260: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 260/306

Instancing: Multiple Instance

Threading Model: Apartment

6. Pulsamos Ok. Nos aparecerá una nueva ventana que representa el nuevo

módulo de datos remoto creado.

7. Guardamos el módulo de datos remoto en disco con el nombreUServidorDCOM.pas

8. Insertamos en el módulo de datos remoto el componente TIBDatabase conel nombre BaseDatos.

9. Hacemos doble clic sobre el componente BaseDatos y configuramos losiguiente:

Connection: RemoteProtocol: TCPDatabase:127.0.0.1:D:\Desarrollo\DelphiAlLimite\Multicapa\DCOM\BaseDatos.fdbLoginPrompt: Desactivado

10. Pulsamos Ok.

11. Añadimos al módulo de datos remoto un componente TIBTransactionllamado Transaccion.

12. Vinculamos el objeto Transaccion al componente TIBDatabase a través desu propiedad DefaultTransaction.

13. Asignamos BaseDatos en la propiedad DefaultDatabase del componenteTransaccion.

14. Insertamos un componente TIBQuery llamado TClientes. En su propiedadDatabase ponemos BaseDatos. Y su propiedad SQL escribimos:

SELECT * FROM CLI ENTES

15. Hacemos doble clic en el componente TClientes y pulsamos lacombinación de teclas CTRL + A para añadir todos los campos.

16. Insertamos un componente TDataSetProvider llamado DSPClientes. En supropiedad DataSet seleccionamos TClientes.

Con esto ya tenemos nuestro propio servidor de aplicaciones conectado a labase de datos Firebird. Como puede apreciarse es casi lo mismo que crear unaaplicación cliente/servidor.

La diferencia está en que no es necesario instanciar en memoria el módulo de

Page 261: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 261/306

Page 262: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 262/306

- Un componente TDCOMConnection que lo llamaremos Conexion.

Ahora vamos a vincular unos componentes a otros:

- Vinculamos el componente TDataSource llamado DSClientes con la rejilla

ListadoClientes a través de su propiedad DataSource.

- Vinculamos el componente TClientDataSet llamado TClientes alcomponente TDataSource llamado DSCliente en su propiedad DataSet.

- Asociamos el componente TDOMConnection llamado Conexion alcomponente TClientDataSet llamado TClientes utilizando su propiedadRemoteServer.

Aquí nos detenemos para analizar un problema. El componenteTClientDataSet no mostrará nada que no venga de un componente

TDataSetProvider. Como dicho componente se encuentra en el servidor deaplicaciones, lo primero que hay que hacer es conectar con el servidor deaplicaciones para poder vincular su DataSetProvider:

- Seleccionamos el componente TDOMConnection y en su propiedadServerName elegimos ServidorDatos.ServidorDCOM. Al hacer esto deberellenarnos automáticamente el campo ServerGUID, el cual es unidentificador de la interfaz del módulo de datos remoto que creamos en elservidor de aplicaciones.

- Activamos la propiedad Connected en el componente TDOMConnection yveremos que se ejecuta automáticamente el servidor de aplicacionesmostrándonos su formulario principal.

- Dejando el servidor de aplicaciones en ejecución seleccionamos elcomponente TClientDataSet y en su propiedad ProviderName seleccionamosDSPClientes.

Con sólo realizar estos pasos, si activamos la propiedad Active delcomponente TClientDataSet nos mostrará en tiempo de diseño los datos de latabla clientes:

Page 263: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 263/306

Al compilar y ejecutar nuestro programa cliente ya tenemos un programa quemaneja los datos del servidor sin preocuparse del motor de bases de datos ode otros usuarios conectados.

El componente TDOMConnection tiene la propiedad llamada ComputerNamela cual contiene el nombre del equipo donde se va a alojar el servidor deaplicaciones. Si no seleccionamos ninguno se asume que el servidor deaplicaciones y la aplicación cliente residen en la misma máquina.

Si intentamos cerrar el servidor de aplicaciones mientras está conectado el

cliente saldrá este mensaje:There are still active COM objects in this application. One o more clients mayhave references to these objects, so manually closing this application maycause those client application(s) to fail.

 Are you sure want to close this application? 

Lo que traducido al castellano sería:

Todavía hay objetos COM activos en esta aplicación. Uno o más clientes

 podrían estar utilizando estos objetos, así que si cierra esta aplicación podríacausar un error de conexión en los clientes.

 ¿Esta seguro de cerrar esta aplicación? 

Esto significa que nunca debemos cerrar el servidor mientras quede alguncliente conectado a nosotros. Además, si el último cliente que estabaconectado al servidor de aplicaciones desconecta entonces el servidor deaplicaciones se cerrará automáticamente al ver que no hay ninguna conexiónabierta.

En la siguiente parte de este artículo veremos como crear un servidor de

Page 264: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 264/306

aplicaciones y una aplicación cliente utilizando otros protocolos distintos aDCOM.

Pruebas realizadas en Delphi 7.

Enviando un correo con INDYVamos a crear un procedimiento para mandar correos electrónicos utilizandoel componente TIdSMTP de la paleta de componentes INDY CLIENTS.

El componente no hace falta ponerlo en el formulario, ya que lo creo entiempo real dentro del procedimiento. Sólo hace falta añadir en el apartadoUSES de nuestro formulario lo siguiente:

uses  I dSMTP, I dMessage;

Vamos con el procedimiento que envía un mensaje de correo electrónico:

pr ocedur e Envi ar Mensaj e( sUsuar i o, sCl ave, sHost , sAdj unt o, sAsunt o,sDest i no, sMensaj e: St r i ng ) ;var SMTP: TI dSMTP;  Mensaj e: TI dMessage;  Adj unt o: TI dAt t achment ;begi n  / / Cr eamos el component e de conexi ón con el ser vi dor

  SMTP : = TI dSMTP. Cr eat e( ni l ) ;  SMTP. User name : = sUsuar i o;  SMTP. Passwor d : = sCl ave;  SMTP. Host : = sHost ;  SMTP. Port : = 25;  SMTP. Authent i cat i onType : = atLogi n;

  / / Cr eamos el cont eni do del mensaj e  Mensaj e : = TI dMessage. Cr eat e( ni l ) ;  Mensaj e. Cl ear ;  Mensaj e. Fr om. Name : = sDest i no;  Mensaj e. Fr om. Address : = sDest i no;  Mensaj e. Subj ect : = sAsunt o;

  Mensaj e. Body. Text : = sMensaj e;  Mensaj e. Reci pi ent s. Add;  Mensaj e. Reci pi ent s. I t ems[ 0] . Addr ess : = sDest i no;

  / / Si hay que met er un archi vo adj unt o l o creamos y l o asi gnamos almensaj e  i f sAdj unt o <> ' ' t hen  begi n  i f Fi l eExi sts( sAdj unt o ) t hen  Adj unt o : = TI dAt t achment . Cr eat e( Mensaj e. MessagePart s, sAdj unt o) ;  end  el se

  Adj unt o : = ni l ;

  / / Conect amos con el ser vi dor SMTP

Page 265: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 265/306

  t r y  SMTP. Connect ;  except  r ai se Except i on. Cr eat e( ' Er r or al conectar con el ser vi dor . ' ) ;  end;

  / / Si ha conect ado envi amos el mensaj e y desconect amos  i f SMTP. Connect ed t hen  begi n  t r y  SMTP. Send( Mensaj e ) ;  except  r ai se Except i on. Cr eat e( ' Er r or al envi ar el mensaj e. ' ) ;  end;

  t r y  SMTP. Di sconnect ;  except  r ai se Except i on. Cr eat e( ' Er r or al desconectar del ser vi dor . ' ) ;  end;  end;

  / / Li beramos l os obj etos cr eados  i f Adj unt o <> ni l t hen  FreeAndNi l ( Adj unt o ) ;

  Fr eeAndNi l ( Mensaj e ) ;  Fr eeAndNi l ( SMTP ) ;

  Appl i cat i on. MessageBox( ' Mensaj e envi ado cor r ectament e. ' ,  ' Fi n de proceso' , MB_I CONI NFORMATI ON ) ;end;

Y este es un ejemplo de envío de mensajes:Envi ar Mensaj e( ' j uani t o33' , ' dj euE21' , ' smt p. t er r a. es' ,  ' c: \ document o. zi p' , ' Te envi o mi document o' ,  ' f el i pe8843@t er r a. es' , ' Adj unt o ar chi vo: document o. zi p') ;

Con un poco de imaginación se puede hacer que muestre el estado de laconexión en la barra de estado e incluso una barra de progreso para vercuanto queda por terminar de enviar.

Pr uebas r eali zadas en Delphi 7.

Leyendo el correo con INDYVamos a dividir el proceso de descargar el correo en dos partes. Primeroleemos las cabeceras de nuestros mensajes y después descargamos losmensajes que nos interesen. Esto puede ser útil para descartar el correo spamdesde el mismo servidor.

Vamos a crear dos componentes de la paleta INDY en tiempo real, con lo cualhay que añadir al comienzo de nuestra unidad:

Page 266: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 266/306

Page 267: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 267/306

  sSer vi dor , sUsuar i o, sCl ave: St r i ng;  sAsunt o, sMensaj e: St r i ng;  Adj unt os: TSt r i ngLi st ;  sRut aAdj unt os: St r i ng; / / Rut a donde se guar dar an l os ar chi vosadj unt os

  const r uct or Cr eat e;  dest r uct or Dest r oy; over r i de;  f unct i on Descar gar: Bool ean;  end;

Y aquí viene la implementación de sus métodos:

const r uct or TMensaj e. Cr eat e;begi n  Adj unt os : = TSt r i ngLi st . Cr eat e;end;

dest r uct or TMensaj e. Dest r oy;

begi n  FreeAndNi l ( Adj unt os ) ;  i nher i t ed;end;

f unct i on TMensaj e. Descar gar : Bool ean;var  POP3: TI dPOP3;  Mensaj e: TI dMessage;  i : I nteger ;  sAdj unt o: St r i ng; / / Nombr e del ar chi vo adj unt obegi n  / / cr eamos el obj eto POP3

  POP3 : = TI dPOP3. Cr eat e( ni l ) ;  POP3. Host : = sSer vi dor ;  POP3. User name : = sUsuar i o;  POP3. Password : = sCl ave;  POP3. Port : = 110;

  / / Conect amos con el servi dor  t r y  POP3. Connect ;  except  r ai se Except i on. Cr eat e( ' Er r or al conectar con el ser vi dor . ' ) ;  end;

  / / Leemos t odo el mensaj e  Mensaj e : = TI dMessage. Cr eat e( ni l ) ;  t r y  POP3. Ret r i eve( i Numero, Mensaj e ) ;  except  r ai se Except i on. Cr eat e( ' Er r or al l eer el mensaj e. ' ) ;  end;

  / / y desconectamos del ser vi dor  POP3. Di sconnect ;

  / / Most r amos el mensaj e en ot r o f ormul ari o  sAsunt o : = Mensaj e. Subj ect ;

  / / ¿Ti ene mensaj es al gún mensaj e adj unto?  i f Mensaj e. MessagePar t s. Count > 0 t hen

Page 268: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 268/306

  begi n  / / Leemos t odas l as par t es del mensaj e  f or i : = 0 t o Mensaj e. MessagePar t s. Count - 1 do  begi n  / / ¿Est a par t e es de t ext o?  i f ( Mensaj e. MessagePar t s. I t ems[ i ] i s TI dText ) t hen

  sMensaj e : = TI dText ( Mensaj e. MessagePar t s. I t ems[ i ] ) . Body. Text  el se  / / ¿Est a part e es un ar chi vo bi nar o adj unt o?  i f ( Mensaj e. MessagePar t s. I t ems[ i ] i s TI dAt t achment ) t hen  begi n  / / Guardamos el nombr e del archi vo adj unt o en una var i abl epara hacer l o más l egi bl e  sAdj unt o : = TI dAt t achment ( Mensaj e. MessagePar t s. I t ems[ i ]) . Fi l eName;

  / / Si ya exi st e el ar chi vo adj unt o l o bor r amos par a que node error  i f Fi l eExi st s( sRut aAdj unt os + sAdj unt o ) t hen  Del et eFi l e( sRut aAdj unt os + sAdj unt o ) ;

  / / Guar damos el archi vo adj unt o y l o añadi mos a l a l i st a deadj unt os  TI dAt t achment ( Mensaj e. MessagePar t s. I t ems[ i ] ) . SaveToFi l e(sRut aAdj unt os + sAdj unt o ) ;  Adj unt os. Add( sAdj unt o ) ;  end  end  end  el se  / / Tomamos t odo el mensaj e como un mensaj e de t ext o  sMensaj e : = Mensaj e. Body. Text ;

  Fr eeAndNi l ( Mensaj e ) ;  Fr eeAndNi l ( POP3 ) ;

  Resul t : = True;end;

Si nos fijamos en el código fuente vemos que el método Descargar controla siel mensaje lleva archivos adjuntos o no. Aunque los mensajes de correoelectrónico suelen codificarse de cuarenta leches distintas, generalmente haydos tipos:

1º Texto plano o página web sin contenido.2º Multiparte, donde cada parte puede ser texto plano, página web, archivobinario, etc.

En ambos casos la codificación utilizada es MIME.

Si el archivo que nos envían tiene una plantilla de página web (casi todos hoyen día) hay que complicarse un poco la vida y sacarlo mediante un navegador(browser). Eso lo dejaré para otra ocasión.

Pues bien, por último creamos el método que hace doble clic en nuestra lista

de mensajes y muestra el contenido del mensaje en otro formulario:

Page 269: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 269/306

pr ocedur e TFFormPr i nci pal . Mensaj esDbl Cl i ck( Sender: TObj ect ) ;var i : I nt eger ;  Mensaj e: TMensaj e;begi n  / / ¿Ha sel eci onado un mensaj e de l a l i st a?

  i f Mensaj es. Sel ected <> ni l t hen  begi n  Mensaj e : = TMensaj e. Cr eat e;  Mensaj e. i Numero : = Mensaj es. Sel ect ed. I ndex+1;  Mensaj e. sSer vi dor : = Servi dor . Text;  Mensaj e. sUsuar i o : = Usuar i o. Text ;  Mensaj e. sCl ave : = Cl ave. Text ;  Mensaj e. sRut aAdj unt os : = Ext r act Fi l ePath( Appl i cat i on. ExeName ) +' Adj unt os\ ' ;

  i f Mensaj e. Descargar t hen  begi n  Appl i cat i on. Cr eat eForm( TFMensaj e, FMensaj e ) ;  FMensaj e. Asunt o. Text : = Mensaj e. sAsunt o;  FMensaj e. Mensaj e. Text : = Mensaj e. sMensaj e;

  f or i : = 0 t o Mensaj e. Adj unt os. Count - 1 do  FMensaj e. Adj unt os. I t ems. Add( Mensaj e. Adj unt os[ i ] ) ;

  FMensaj e. ShowModal ;  end;

  Fr eeAndNi l ( Mensaj e ) ;  end;end;

Esta es la base de un programa lector de correo POP3 aunque realmente hayque controlar muchas más cosas, como por ejemplo el borrar del servidor losmensajes ya descargados (Ahora no lo hace).

Pr uebas r eali zadas en Delphi 7.

Subiendo archivos por FTP con INDYPara subir archivos por FTP utilizaremos el objeto TIdFTP de la paleta decomponentes Indy Clients. Para poder utilizar dicho objeto debemos añadirloen la seccion interface:

uses  Wi ndows, Messages, . . . . . . , I dFTP, I dComponent ;

La unidad IdComponent la utilizaremos luego para controlar eventos delcomponente FTP. El objeto lo creamos en tiempo de ejecución:

pr ocedur e Subi r Ar chi vo( sAr chi vo: St r i ng ) ;var  FTP: TI dFTP;

begi n  FTP : = TI dFTP. Cr eat e( ni l ) ;

Page 270: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 270/306

Antes de subir un archivo hay que conectar con el servidor dando usuario ypassword:

  FTP. User name : = ' usuar i o' ;  FTP. Password : = ' mi cl ave' ;  FTP. Host : = ' mi f t p. mi domi ni o. com' ;

  t r y  FTP. Connect ;  except  r ai se Except i on. Cr eat e( ' No se ha podi do conect ar con el servi dor' + FTP. Host ) ;  end;

Ahora ya estamos listos para enviar el archivo, pero antes debemos ir aldirectorio del servidor donde deseamos subir el archivo:

  FTP. ChangeDi r ( ' / mi sar chi vos/ copi asegur i dad/ ' ) ;

Para subir un archivo tenemos el método Put, el cual toma como primerparámetro la ruta y nombre del archivo a subir, como segundo parámetro elnombre que va a tener el archivo en el servidor (se supone que el mismo) ycomo tercer parámetro si deseamos añadir a un archivo ya existente o crear elarchivo de nuevo:

  FTP. Put ( sAr chi vo, Extr act Fi l eName( sAr chi vo ) , Fal se ) ;

En nuestro caso hemos subido el archivo con el mismo nombre y si hubiera

otro igual lo sobrescribe. Por último nos desconectamos del servidor yeliminamos el objeto.

  FTP. Di sconnect ;  FTP. Free;end;

Como suelo comentar en artículos anteriores los componentes Indy nocontrolan bien la multitarea, por lo tanto si vamos a subir archivosrelativamente grandes recomiendo meter el método Put dentro de un hilo deejecución, ya que si no es así, mientras que el componente TIdFTP notermine de subir el archivo, nuestra aplicación da el aspecto de estar colgada(no se puede ni mover la ventana).

Tampoco estaría mal mostrar al usuario mediante una barra de progreso elestado de la subida del archivo. Para ello el objeto TIdFtp posee el eventoOnWork el cual nos informa de los bytes subidos al servidor. El cambio essencillo.

Primero creamos el evento OnWork:

procedur e TFVentana. FTPWor k( Sender : TObj ect ; AWor kMode: TWor kMode;const AWorkCount : I nt eger ) ;

begi n

Page 271: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 271/306

  Barr a. Posi t i on : = AWorkCount di v 1024;end;

Se supone que Barra es un componenete TProgressBar donde vamosacumulando el tamaño del archivo enviado. En este caso he dividido los bytessubidos entre 1024 para que me devuelva la información en kilobytes.

Y ahora asociamos el evento al componente después de crearlo:

  FTP : = TI dFTP. Cr eat e( ni l ) ;  FTP. OnWor k : = FTPWor k;

¿Cómo averiguamos el tamaño del archivo para medir el progreso? Muy fácil:

pr ocedur e Subi r Ar chi vo( sAr chi vo: St r i ng ) ;var  FTP: TI dFTP;  F: F i l e of byt e;begi n  Assi gnFi l e( F, sAr chi vo ) ;  Reset ( F ) ;  Bar r a. Max : = Fi l eSi ze( F ) di v 1024;  Cl oseFi l e( F ) ;  . . . .

Con esto le decimos a la barra de progreso que la longitud máxima de lamisma es la longitud del archivo en kilobytes. Una vez comience a subir elarchivo la barra de progreso se va incrementando sola según el eventoOnWork.

Y por supuesto nunca se os olvide controlar en todo momento el estado de laconexión, tanto para conectar, subir el archivo o desconectarse del servidorcapturando las excepciones oportunas e informando al usuario de lo queocurre (por ejemplo con una barra de estado en la parte inferior de laventana).

En el próximo artículo mostraré como descargar un archivo por FTP.

Y lo se, a veces los componentes Indy pueden sacar de quicio a cualquiera.

Pruebas realizadas en Delphi 7.

Descargando archivos por FTP con INDYEl compononente IdFTP que utilizamos en el artículo anterior para subirarchivos es el mismo que vamos a utilizar para descargarlos. Añadimos a lasección interface:

uses

  Wi ndows, Messages, . . . . . . , I dFTP, I dComponent ;

Page 272: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 272/306

Y creamos el procedimiento para descargar el archivo:

pr ocedur e Descar garAr chi vo( sAr chi vo: St r i ng ) ;var  FTP: TI dFTP;

begi n  FTP : = TI dFTP. Cr eat e( ni l ) ;  FTP. OnWor k : = FTPWor k;  FTP. User name : = ' usuar i o' ;  FTP. Password : = ' mi cl ave' ;  FTP. Host : = ' mi f t p. mi domi ni o. com' ;

  t r y  FTP. Connect ;  except  r ai se Except i on. Cr eat e( ' No se ha podi do conect ar con el servi dor' + FTP. Host ) ;  end;

  FTP. ChangeDi r ( ' / mi sar chi vos/ copi asegur i dad/ ' ) ;

Antes de comenzar la descarga hay que averiguar el tamaño del archivo en elservidor:

  Bar r a. Max : = FTP. Si ze( Ext r act Fi l eName( sAr chi vo ) ) di v 1024;

Donde Barra es un objeto TProgressBar que colocamos para mostrar alusuario el progreso de la descarga. Ahora nos aseguramos de que el archivo adescargar no haya sido descargado anteriormente, ya que podría producir un

error:  i f Fi l eExi sts( sArchi vo ) then  Del et eFi l e( sAr chi vo ) ;

Para descargar el archivo utilizaremos el método Get, el cual toma comoprimer parámetro la ruta y nombre del archivo a descargar en local, comosegundo parámetro el nombre que va a tener el archivo en el servidor, comotercer parámetro si deseamos añadir a un archivo ya existente y como últimoparámetro si deseamos la opción RESUME (en caso de interrumpirse laconexión si queremos que continue por donde iba, siempre y cuando elservidor soporte dicho modo).

  FTP. Get ( Ext r act Fi l eName( sAr chi vo ) , sAr chi vo, Fal se, Fal se ) ;

Para finalizar nos desconectamos del servidor y eliminamos el objeto.

  FTP. Di sconnect ;  FTP. Free;end;

También creamos el evento OnWork para controlar el progreso de ladescarga:

procedur e TFVentana. FTPWor k( Sender : TObj ect ; AWor kMode: TWor kMode;const AWorkCount : I nt eger ) ;

Page 273: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 273/306

Page 274: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 274/306

Ansi Cont ai nsSt r ( ' DELPHI AL LI MI TE' , ' LI MI TE' ) devuel ve Tr ueAnsi Cont ai nsSt r ( ' DELPHI AL LI MI TE' , ' LI MI Te' ) devuel ve Fal se

function AnsiContainsText( const AText, ASubText: string ): Boolean;

Esta función es igual a AnsiConstainsStr salvo que no diferencia mayúsculasde minúsculas. Veamos un ejemplo:

Ansi Cont ai nsText ( ' DELPHI AL LI MI TE' , ' LI MI TE' ) devuel ve Tr ueAnsi Cont ai nsText ( ' DELPHI AL LI MI TE' , ' LI MI Te' ) devuel ve Tr ueAnsi Cont ai nsText ( ' DELPHI AL LI MI TE' , ' LI MI TES' ) devuel ve Fal se

function AnsiEndsStr( const ASubText, AText: string ): Boolean;

La función nos devuelve True si la cadena AText termina en la cadenaASubText. He aquí un ejemplo:

Ansi EndsSt r ( ' . avi ' , ' C: \ Ar chi vos depr ogr ama\ Emul e\ I ncomi ng\ pel i cul a. avi ' ) devuel ve Tr ueAnsi EndsSt r ( ' . AVI ' , ' C: \ Ar chi vos depr ogr ama\ Emul e\ I ncomi ng\ pel i cul a. avi ' ) devuel ve Fal se

Para este caso es mejor utilizar la función:

function AnsiEndsText( const ASubText, AText: string ): Boolean;

Esta función obtiene el mismo resultado que AnsiEndsStr pero sin diferenciarmayúsculas de minúsculas. En el caso anterior:

Ansi EndsText ( ' . avi ' , ' C: \ Ar chi vos depr ogr ama\ Emul e\ I ncomi ng\ pel i cul a. avi ' ) devuel ve Tr ueAnsi EndsText ( ' . AVI ' , ' C: \ Ar chi vos depr ogr ama\ Emul e\ I ncomi ng\ pel i cul a. avi ' ) devuel ve Tr ue

Como vemos es ideal para comprobar extensiones de archivos.

En el próximo artículo seguiremos con muchas más funciones.

Pruebas realizadas en Delphi 7.

Operaciones con cadenas de texto (II)Continuamos viendo funciones para manejo de cadenas de caracteres:

function AnsiIndexStr( const AText: string; const AValues: array of string ):Integer;

Esta función comprueba si alguna de las cadenas contenidas en el arrayAValues coincide extactamente con la cadena AText (distingue mayúsculas yminúsculas). Si la encuentra nos devuelve el número de índice del array dondese encuentra (empieza por cero), en caso contrario nos devuelve -1. Porejemplo:

Page 275: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 275/306

Ansi I ndexSt r ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J UAN' , ' ROSA' ] ) devuel ve 2Ansi I ndexSt r ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J uan' , ' ROSA' ] ) devuel ve - 1

Como vemos en el primer ejemplo nos dice que JUAN se encuentra en laposición 2 del array. Sin embargo en el sedungo ejemplo devuelve -1 porqueJUAN es distinto de Juan.

function AnsiIndexText( const AText: string; const AValues: array of string): Integer;

Funciona exactamente igual que la función AnsiIndexStr salvo que nodistingue mayúsculas y minúsculas. Según el ejemplo anterior ambas llamadasa la función devuelven idéntico resultado:

Ansi I ndexText ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J UAN' , ' ROSA' ] ) devuel ve 2Ansi I ndexText ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J uan' , ' ROSA' ] ) devuel ve 2

function AnsiLeftStr( const AText: AnsiString; const ACount: Integer ):AnsiString;

Esta función devuelve la parte izquierda de la cadena AText según el númerode caracteres que le indiquemos en ACount. Sería algo así:

Ansi Lef t St r ( ' PROGRAMANDO CON DELPHI ' , 6 ) devuel ve PROGRAAnsi Lef t St r ( ' PROGRAMANDO CON DELPHI ' , 11 ) devuel ve PROGRAMANDOAnsi Lef t St r ( ' PROGRAMANDO CON DELPHI ' , 18 ) devuel ve PROGRAMANDO CONDE

function AnsiLowerCase( const S: string ): string;

Devuelve la cadena S convertida toda en minúsculas (si tiene letras,naturalmente). Veamos un par de ejemplos:

Ansi LowerCase( ' Pr ogr amando con Del phi ' ) devuel ve:

  programando con del phi

Ansi Lower Case( ' MANI PULANDO CADA CARÁCTER DE LA CADENA' ) devuel ve:

  mani pul ando cada caráct er de l a cadena

Como vemos nos ha respetado la tílde en la palabra carácter.

function AnsiMatchStr( const AText: string; const AValues: array of string ):Boolean;

Esta función nos dice si alguna de las cadenas contenidas en el array AValuescoincide exactamente con la cadena AText (comprobando mayúsculas yminúsculas). Aunque esta función pueda parecer igual a AnsiIndexStr sediferencia en que sólo responde True o False si la encontrado o no, al

contrario que AnsiIndexStr que nos devuelve que la posición donde la haencontrado. Con un ejemplo se ve mas claro:

Page 276: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 276/306

Ansi Mat chSt r ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J UAN' , ' ROSA' ] ) devuel ve Tr ueAnsi Mat chSt r ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J uan' , ' ROSA' ] ) devuel veFal se

Nota: La ayuda de Delphi 7 dice que esta función devuelve un Integer y

realmente devuelve un Boolean, será un error de documentación (ya estamosacostumbrados a la 'magnífica' documentación de Borland). En cambio si estácorregido en la función:

function AnsiMatchText( const AText: string; const AValues: array of string): Boolean;

Similar a la función anterior AnsiMatchStr pero sin diferenciar mayúsculas yminúsculas. Siguiendo el mismo ejemplo:

Ansi Mat chText ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J UAN' , ' ROSA' ] ) devuel ve

 Tr ueAnsi Mat chText ( ' J UAN' , [ ' CARLOS' , ' PABLO' , ' J uan' , ' ROSA' ] ) devuel ve Tr ue

function AnsiMidStr( const AText: AnsiString; const AStart, ACount: Integer): AnsiString;

Devuelve un trozo de la cadena AText cuya posición comienza en AStart (elprimer elemento es el 1) y cuyo número de caracteres viene determinado porACount. Por ejemplo:

Ansi Mi dSt r ( ' PROGRAMANDO CON DELPHI ' , 7, 5 ) devuel ve MANDOAnsi Mi dSt r ( ' PROGRAMANDO CON DELPHI ' , 17, 6 ) devuel ve DELPHI

function AnsiPos( const Substr, S: string ): Integer;

Devuelve la posición de la cadena Substr que está dentro de la cadena S. Sino la encuentra devuelve un cero (el primer elemento comienza por 1).También distingue entre mayúsculas y minúsculas. Veamos como funciona:

Ansi Pos( ' PALABRA' , ' BUSCANDO UNA PALABRA' ) devuel ve 14Ansi Pos( ' Pal abr a' , ' BUSCANDO UNA PALABRA' ) devuel ve 0

function AnsiReplaceStr( const AText, AFromText, AToText: string ):string;

Esta función nos devuelve la cadena AText reemplazando las palabras quecontenga según la variable AFromText sustituyéndolas por AToText. Tieneencuentra mayúsculas y minúsculas:

Ansi Repl aceSt r ( ' CORRI GI ENDO TEXTO DENTRO DE UNA FRASE' , ' TEXTO' , ' UNAPALABRA' ) devuel ve:

  CORRI GI ENDO UNA PALABRA DENTRO DE UNA FRASE

Ansi Repl aceSt r ( ' CORRI GI ENDO TEXTO DENTRO DE UNA FRASE' , ' Texto' , ' UNAPALABRA' ) devuel ve:

Page 277: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 277/306

Page 278: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 278/306

Page 279: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 279/306

Similar a la función CompareStr pero sin tener en cuenta mayúsculas yminúsculas. Según el ejemplo anterior:

CompareText ( ' HOLA' , ' HOLA' ) devuel ve 0CompareText ( ' HOLA' , ' HOLa' ) devuel ve 0CompareText ( ' HOLa' , ' HOLA' ) devuel ve 0

CompareText ( ' HOLA' , ' HOYA' ) devuel ve - 13

Esta función esta obsoleta. Se recomienda utilizar AnsiCompareText en sulugar.

function Concat( s1 [, s2,..., sn]: string ): string;

Esta función devuelve concatenadas un número indeterminado de cadenas detexto que le pasemos como parámetro, aunque se recomienda utilizar eloperador + en su lugar. Veamos un ejemplo:

Concat ( ' 12' , ' 34' , ' 56' , ' 78' ) devuel ve 12345678Concat ( ' COMO' , ' ESTÁN' , ' USTEDES' ) devuel ve COMO ESTÁN USTEDES

function Copy( S; Index, Count: Integer ): string;function Copy( S; Index, Count: Integer ): array;

Ambas funciones devuelven una subcadena de la cadena S en la posiciónIndex (el primer elemento es 1) y con una longitud de Count. Por ejemplo:

Copy( ' 123456' , 1, 3 ) devuel ve 123Copy( ' 123456' , 4, 2 ) devuel ve 45Copy( ' CAPTURANDO UNA PALABRA' , 16, 7 ) devuel ve PALABRA

También puede utilizarse para copiar elementos entre arrays de cualquiertipo. Por ejemplo vamos a crear un array de enteros y vamos a copiar unaparte del mismo a otro:

var Or i gen, Dest i no: ar r ay of I nt eger;  i : I nteger ;begi n  Set Lengt h( Or i gen, 6 ) ;  Or i gen[ 0] : = 10;  Or i gen[ 1] : = 20;  Or i gen[ 2] : = 30;

  Or i gen[ 3] : = 40;  Or i gen[ 4] : = 50;  Or i gen[ 5] : = 60;  Dest i no : = Copy( Or i gen, 2, 4 ) ;  f or i : = 0 t o Lengt h( Dest i no ) - 1 do  Memo. Li nes. Add( For mat ( ' Dest i no[%d] =%d' , [ i , Dest i no[ i ] ] ) ) ;end;

El resultado lo hemos volcado a pantalla dentro de un campo Memo mostrandolo siguiente:

Dest i no[ 0]=30

Dest i no[ 1]=40Dest i no[ 2]=50Dest i no[ 3]=60

Page 280: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 280/306

En el próximo artículo seguiremos con más funciones.

Pruebas realizadas en Delphi 7.

Operaciones con cadenas de texto (IV)Vamos a seguir con las funciones para tratamiento de cadenas de texto:

procedure Delete( var S: string; Index, Count:Integer );

Este procedimiento elimina de la cadena S (pasada por variable) un númerode caracteres situados en la posición Index y cuya longitud viene determinadapor Count. El primer elemento comienza por 1. Por ejemplo:

var  sText o: St r i ng;begi n  sText o : = ' CORTANDO UNA CADENA DE TEXTO' ;  Del et e( sTexto, 10, 14 ) ;end;

Ahora la variable sTexto contiene:

CORTANDO TEXTO

porque hemos eliminado UNA CADENA DE.

function DupeString( const AText: string; ACount: Integer ): string;

Esta función devuelve la cadena AText repetida tantas veces como se indiqueen ACount. Por ejemplo:

DupeSt r i ng( ' HOLA ' , 3 ) devuel ve HOLA HOLA HOLA

function High( X );

Devuelve el valor más alto de un tipo de variable. Vamos a ver un ejemploque utiliza esta función con distintos tipos de variable y muestra el resultadoen un Memo:

var  Numer os: ar r ay[ 1. . 4] of I nt eger ;begi n  Memo. Li nes. Add( ' Hi gh( Char ) =' + I nt ToSt r ( Or d( Hi gh( Char ) ) ) ) ;  Memo. Li nes. Add( ' Hi gh( I nt eger ) =' + I nt ToSt r ( Hi gh( I nt eger ) ) ) ;  Memo. Li nes. Add( ' Hi gh( Word ) =' + I nt ToSt r ( Hi gh( Word ) ) ) ;  Memo. Li nes. Add( ' Hi gh( DWord ) =' + I nt ToSt r ( Hi gh( DWord ) ) ) ;  Memo. Li nes. Add( ' Hi gh( Bool ean ) =' + Bool ToSt r ( Hi gh( Bool ean ) ,

 Tr ue ) ) ;  Numer os[ 1] : = 13;  Numer os[ 2] : = 6;

Page 281: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 281/306

  Numer os[ 3] : = 16;  Numer os[ 4] : = 5;  Memo. Li nes. Add( ' Hi gh( Numeros ) =' + I nt ToSt r ( Hi gh( Numeros ) ) ) ;end;

El resultado que muestra sería:

  Hi gh( Char ) =255  Hi gh( I nteger ) =2147483647  Hi gh( Wor d ) =65535  Hi gh( DWor d ) =4294967295  Hi gh( Bool ean ) =True  Hi gh( Numer os ) =4

Como vemos en el último caso, nos devuelve el último índice del del arrayNumeros.

procedure Insert( Source: string; var S: string; Index: Integer );

Este procedimiento inserta dentro de la cadena S (pasada como variable) lacadena Source en la posición Index. Por ejemplo:

var  sText o: St r i ng;begi n  sText o : = ' AMPLI ANDO TEXTO' ;  I nser t ( ' UNA CADENA DE' , sText o, 10 ) ;end;

Ahora sTexto contiene:

AMPLI ANDO UNA CADENA DE TEXTO

function LastDelimiter( const Delimiters, S: string ): Integer;

Esta función nos devuelve la posición del último delimitador determinado porDelimiters que encuentre en la cadena S. Con unos ejemplos se ve mas claro:

  Last Del i mi t er( ' , ' , ' PABLO, J UAN, MARI A, MARCOS' ) devuel ve 17  LastDel i mi t er( ' . ' , ' 1. 2. 3. 4. 5' ) devuel ve 8  LastDel i mi t er( ' - : ' , ' A: 1- B: 2- C: 3' ) devuel ve 10

En el último ejemplo hemos utilizado dos delimitadores. Esta función seríamuy interesante para crear un parser de código fuente HTML buscandodelimitadores <>.

function Length( S ): Integer;

Devuelve la longitud máxima de una cadena de texto o de un array. Porejemplo:

var  Val or es: arr ay of I nt eger ;

begi n  ShowMessage( Lengt h( ' HOLA' ) ) ;

Page 282: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 282/306

  Set Lengt h( Val or es, 3 ) ;  ShowMessage( I nt ToSt r ( Lengt h( Val ores ) ) ;end;

Los comandos ShowMessage nos mostraría los valores 4 para la cadena HOLAy el valor 3, que es la longitud del array dinámico.

function LowerCase( const S: string ): string;

Convierte los caracteres de la cadena S a minúsculas. Se recomenda utilizaren su lugar la función AnsiLowerCase para caracteres internacionales de 8bits. Por ejemplo:

LowerCase( ' Progr amando con Del phi ' ) devuel ve:

  programando con del phi

Lower Case( ' MANI PULANDO CADA CARÁCTER DE LA CADENA' )  mani pul ando cada carÁct er de l a cadena

Como vemos en este último ejemplo la palabra CARÁCTER  ha dejado la Á enmayúsculas. Por ello se recomienda utilizar la función AnsiLowerCase paraevitar estas incidencias.

procedure Move( const Source; var Dest; Count: Integer );

Este procedimiento (que debería llamarse quizás Copy) copia la cantidad debytes Count de Source a la variable Dest. Por ejemplo:

var  sOr i gen, sDest i no: St r i ng;begi n  sOr i gen : = ' COPI ANDO TEXTO' ;  sDest i no : = ' - - - - - - - - - - - - - - ' ;  Move( sOr i gen[ 10] , sDest i no[ 3] , 5 ) ;end;

El valor de la variable destino sería:

- - TEXTO- - - - - - -

function Pos( Substr: string; S: string ): Integer;

Devuelve la posición de la cadena Substr dentro de la cadena S (Distinguemayúsculas de minúsculas). Si no la encuentra nos devuelve un cero (el primerelemento es el 1). Por ejemplo:

Pos( ' PALABRA' , ' BUSCANDO UNA PALABRA' ) devuel ve 14Pos( ' pal abr a' , ' BUSCANDO UNA PALABRA' ) devuel ve 0

procedure ProcessPath( const EditText: string; var Drive: Char; var DirPart:

string; var FilePart: string );

Page 283: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 283/306

Este interesante procedimiento divide la cadena EditText la cual se suponeque es una ruta como:

C: \ Ar chi vos de programa\ MSN Messenger \ msnmsgr . exe

en unidad, directorio y archivo. Vamos un ejemplo volcando la información enun Memo:

var  sRut a, sDi r ector i o, sAr chi vo: St r i ng;  cUni dad: Char ;begi n  sRuta : = ' C: \ Ar chi vos de progr ama\ MSN Messenger \ msnmsgr . exe' ;  Pr ocessPat h( sRut a, cUni dad, sDi r ect or i o, sAr chi vo ) ;  Memo. Li nes. Add( ' sUni dad=' + cUni dad ) ;  Memo. Li nes. Add( ' sDi r ect or i o=' + sDi r ect or i o ) ;  Memo. Li nes. Add( ' sAr chi vo=' + sAr chi vo ) ;end;

El resultado sería:

sUni dad=CsDi r ect or i o=\ Ar chi vos de progr ama\ MSN MessengersAr chi vo=msnmsgr . exe

Nota: para que funcione esta función la ruta que le pasemos debe ser real, encaso contrario da un error.

En el próximo artículo terminaremos con este tipo de funciones.

Pruebas realizadas en Delphi 7.

Operaciones con cadenas de texto (V)Aquí finalizamos con las funciones de manipulación de cadenas de texto:

procedure SetLength( var S; NewLength: Integer );

Establece el tamaño de una cadena de texto o un array dinámico. Porejemplo:

var  sText o: St r i ng;begi n  sText o : = ' 123456789' ;  Set Lengt h( sText o, 4 ) ;end;

Después de ejecutar esto sTexto será igual a 1234 (hemos recortado lacadena).

También se utiliza para establecer el tamaño de un array dinámico antes deutilizarlo.

Page 284: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 284/306

var  Val or es: arr ay of I nt eger ;begi n  Set Lengt h( Val or es, 3 ) ;  Val ores[ 0] : = 10;

  Val ores[ 1] : = 20;  Val ores[ 2] : = 30;

  / / Ampl i amos el t amaño del ar r ay  Set Lengt h( Val or es, 5 ) ;  Val ores[ 3] : = 40;  Val ores[ 4] : = 50;end;

procedure SetString( var S: string; Buffer: PChar; len: Integer );

El procedimiento SetString fija la longitud de la cadena S antes de copiar el

número de caracteres determinado por len de la cadena Buffer. Vamos a verun ejemplo que copia el contenido de un array de caracteres a un string:

var  Or i gi nal : ar ray[ 1. . 3] of char ;  sDest i no: St r i ngbegi n  Or i gi nal [ 1] : = ' A' ;  Or i gi nal [ 2] : = ' B' ;  Or i gi nal [ 3] : = ' C' ;  Set St r i ng( sDest i no, PChar ( Addr ( Or i gi nal ) ) , 3 ) ;end;

Ahora la variable sDestino vale 'ABC'.

function StringOfChar( Ch: Char; Count: Integer ): string;

Esta función devuelve una nueva cadena de texto creada con el carácter Ch yrepetido tantas veces como determine el parámetro Count. Por ejemplo:

  St r i ngOf Char ( ' 0' , 5 ) devuel ve 00000  St r i ngOf Char( ' A' , 6 ) devuel ve AAAAAA

Esta función viene bien para completar con ceros por la derecha o por la

izquierda los números que se pasan a formato texto.

function StringReplace( const S, OldPattern, NewPattern: string; Flags:TReplaceFlags ): string;

Esta función devuelve una cadena de texto reemplazando las subcadenasencontradas dentro de la cadena S que coincidan con OldPattern y seránsustituidas por la cadena NewPattern. Los valores del parámetro Flagspueden ser:

rfReplaceAll: reemplaza todas las cadenas encontradas

rfIgnoreCase: no distingue mayúsculas de minúsculas

Page 285: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 285/306

Page 286: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 286/306

Si nos fijamos en el segundo ejemplo hemos sustituido UN TEXTO EN UNA porUNA (15 caracteres).

function Trim( const S: string ): string; overload;

function Trim( const S: WideString ): WideString; overload;

La función Trim devuelve la cadena S eliminando los espacios en blanco y loscaracteres de control que halla a la izquierda y derecha (pero no en medio).Por ejemplo:

  Tr i m( ' ESTO ES UNA PRUEBA ' ) devuel ve ' ESTO ES UNA PRUEBA'

function TrimLeft( const S: string ): string; overload;function TrimLeft( const S: WideString ): WideString; overload;

Esta función es similar a Trim salvo que sólo elimina los espacios en blanco ycaracteres de control por la izquierda. Por ejemplo:

 Tr i mLef t ( ' ESTO ES UNA PRUEBA ' ) devuel ve ' ESTO ES UNA PRUEBA '

function TrimRight( const S: string ): string; overload;function TrimRight( const S: WideString ): WideString; overload;

Esta función es igual a Trim salvo que sólo elimina los espacios en blanco ycaracteres de control por la derecha. Por ejemplo:

 Tr i mRi ght ( ' ESTO ES UNA PRUEBA ' ) devuel ve ' ESTO ES UNA PRUEBA'

function UpCase( Ch: Char ): Char;

Convierte un carácter a mayúsculas. Por ejemplo:

UpCase( ' a' ) devuel ve AUpCase( ' ú' ) devuel ve ú

Como vemos en el segundo ejemplo no funciona correctamente con caracteresAnsi. Mejor utilizar en su lugar la función AnsiUpperCase.

function UpperCase( const S: string ): string;

Convierte la cadena S a mayúsculas. Por ejemplo:

UpperCase( ' Hol a' ) devuel ve HOLAUpperCase( ' Pr ogr amaci ón en Del phi ' ) devuel ve PROGRAMACI óN EN DELPHI

El segundo ejemplo no respeta las vocales con tilde. Mejor utilizarAnsiUpperCase.

function WrapText( const Line, BreakStr: string; nBreakChars:

TSysCharSet; MaxCol: Integer ):string; overload;function WrapText( const Line, MaxCol: Integer = 45 ):string; overload;

Page 287: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 287/306

La función WrapText parte la cadena Line en múltiples líneas de textoseparadas por defecto con los caracteres de control #13 y #10, en tamañosmáximos de palabra definidos por MaxCol. Veamos algunos ejemplos:

Wr apText ( ' Par t i endo una cadena de t ext o' , 15 ) devuel ve  Par t i endo una  cadena de t exto

Wr apText ( ' Par t i endo una cadena de t ext o' , 10 ) devuel ve:

  Par t i endo  una  cadena de  texto

Wr apText ( ' Par t i endo una cadena de t ext o' , 5 ) devuel ve:

  Par t i endo  una  cadena  de  texto

Es decir, parte la cadena en frases cuyo tamaño máximo es definido porMaxCol pero respetando en ancho de cada palabra (aunque se pase dellímite). También podemos definir con que caracteres vamos a separar laspalabras así como que deseamos de separador. Si quisieramos separar la frasemediante por punto sería de la siguiente manera:

Wr apText ( ' Par t i endo. una. cadena. de. t ext o' , ' - ' , [ ' . ' ] , 5 ) devuel ve

  Par t i endo. - una. - cadena. - de. - t ext o

Hemos definido el separador (quitando #13 y #10) y también le hemosindicado el carácter que divide cada palabra (el punto).

Con esto finalizan las funciones de manipulación de texto.

Pruebas realizadas en Delphi 7.

Page 288: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 288/306

Page 289: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 289/306

  PEDRO SANCHEZ GARCI A  MARI A PALAZÓN PÉREZ  CARLOS ABENZA MARTI NEZ  ROSA GUI LLÉN LUNA

También podemos obtener cada elemento separado por comas:ShowMessage( Cl i ent es. CommaText ) nos devuel ve:

"PEDRO SANCHEZ GARCI A", "MARI A PALAZÓN PÉREZ" , "CARLOS ABENZAMARTI NEZ" , "ROSA GUI LLÉN LUNA"

Nos ha devuelto el resultado separando cada elemento con comas además deencerrar cada uno con comillas dobles. Si nos interesa cambiar las comillasdobles por comillas simples, un StringList dispone de la propiedad QuoteCharla cual define el carácter delimitador de la cadena. Después solo hay queconsultar el resultado mediante el valor DelimitedText en lugar de Text:

  Cl i ent es. QuoteChar : = ' ' ' ' ; / / o t ambi én Cl i ent es. Quot eChar : = #39;  ShowMessage( Cl i ent es. Del i mi t edText ) ;

El resultado sería:

' PEDRO SANCHEZ GARCI A' , ' MARI A PALAZÓN PÉREZ' , ' CARLOS ABENZAMARTI NEZ' , ' ROSA GUI LLÉN LUNA'

Y por supuesto se puede cambiar el separador de los elementos de la lista (lacoma):

  Cl i ent es. Del i mi t er : = ' - ' ;  ShowMessage( Cl i ent es. Del i mi t edText ) ;

Lo cual mostraría:

' PEDRO SANCHEZ GARCI A' - ' MARI A PALAZÓN PÉREZ' - ' CARLOS ABENZA MARTI NEZ' -' ROSA GUI LLÉN LUNA'

ALMACENANDO VALORES DOBLES

Otra característica que hace extremadamente potente a los StringList es la

posibilidad de almacenar valores dobles, es decir, cada elemento de la listapuede contener un nombre y un valor a la vez. Veamos un ejemplo:

var  Di cci onar i o: TSt r i ngLi st ;begi n  Di cci onar i o : = TSt r i ngLi st . Cr eat e;  Di cci onar i o. CommaText : = ' BOOK=LI BRO, MOUSE=RATON, TABLE=MESA' ;  Memo. Li nes. Add( ' BOOK = ' + Di cci onari o. Val ues[ ' BOOK' ] ) ;  Memo. Li nes. Add( ' MOUSE = ' +Di cci onar i o. Val ues[ ' MOUSE' ] ) ;  Memo. Li nes. Add( ' TABLE = ' +Di cci onar i o. Val ues[ ' TABLE' ] ) ;  Di cci onar i o. Free;end;

El resultado que nos mostraría dentro del campo Memo sería:

Page 290: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 290/306

  BOOK = LI BRO  MOUSE = RATON  TABLE = MESA

Se podría también utilizar para almacenar un registro de una base de datos en

memoria antes de ejecutar la SQL:

var  Tabl a: TSt r i ngLi st ;begi n  Tabl a : = TSt r i ngLi st . Cr eat e;  Tabl a. CommaText : = ' I D=1, NOMBRE=CARLOS, DNI =65872841R,SALDO=130. 25' ;  Memo. Li nes. Add( ' I D = ' + Tabl a. Val ues[ ' I D' ] ) ;  Memo. Li nes. Add( ' NOMBRE = ' + Tabl a. Val ues[ ' NOMBRE' ] ) ;  Memo. Li nes. Add( ' DNI = ' +Tabl a. Val ues[ ' DNI ' ] ) ;  Memo. Li nes. Add( ' SALDO = ' + Tabl a. Val ues[ ' SALDO' ] ) ;  Tabl a. Free;

end;

Su resultado sería:

  I D = 1  NOMBRE = CARLOS  DNI = 65872841R  SALDO = 130. 25

OPERACIONES QUE SE PUEDEN REALIZAR DENTRO DE UNA LISTA

Una de las operaciones más básicas de una lista es la ordenación mediante elcomando Sort:

  Cl i ent es. Sor t ;  Memo. Li nes. Add( Cl i ent es. Text ) ;

La lista quedaría de la siguiente manera:

  CARLOS ABENZA MARTI NEZ  MARI A PALAZÓN PÉREZ  PEDRO SANCHEZ GARCI A  ROSA GUI LLÉN LUNA

Para añadir elementos hemos visto el comando Add:

Cl i ent es. Add( ' LUI S GARCI A ROJ O' ) ;

También se puede utilizar el comando Append:

Cl i ent es. Append( ' MANUEL ESCUDERO BERNAL' ) ;

La diferencia esta en que Add devuelve la posición donde se ha insertado elelemento, en cambio Append no devuelve nada. La lista quedaría de lasiguiente manera:

CARLOS ABENZA MARTI NEZMARI A PALAZÓN PÉREZ

Page 291: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 291/306

PEDRO SANCHEZ GARCI AROSA GUI LLÉN LUNALUI S GARCI A ROJ OMANUEL ESCUDERO BERNAL

Para añadir elementos a la lista también disponemos del comando Insert el

cual toma como primer parámetro en que posición de la lista deseamosinsertar el elemento y como segundo parámetro el elemento en cuestión. Porejemplo vamos a introducir un cliente entre CARLOS y MARIA:

Cl i ent es. I nser t ( 1, ' PASCUAL CAMPOY FERNANDEZ' ) ;

Y la lista quedaría así:

CARLOS ABENZA MARTI NEZPASCUAL CAMPOY FERNANDEZMARI A PALAZÓN PÉREZPEDRO SANCHEZ GARCI A

ROSA GUI LLÉN LUNALUI S GARCI A ROJ OMANUEL ESCUDERO BERNAL

En el próximo artículo seguiremos viendo más cosas que se pueden hacer conun StringList.

Pruebas realizadas en Delphi 7.

El objeto StringList (II)Vamos a seguir viendo las características de la clase TStringList.

ELIMINANDO ELEMENTOS DE LA LISTA

Con el método Delete se elimina un elementos de la lista la cual vuelve areagrupar sus elemento asignando un nuevo índice. Actualmente la lista declientes contiene:

CARLOS ABENZA MARTI NEZPASCUAL CAMPOY FERNANDEZ

MARI A PALAZÓN PÉREZPEDRO SANCHEZ GARCI AROSA GUI LLÉN LUNALUI S GARCI A ROJ OMANUEL ESCUDERO BERNAL

Vamos a eliminar a ROSA de la lista:

Cl i ent es. Del et e( 4 );

Ahora el cliente LUIS tiene un índice de 4 (en vez de 5 como antes), es decir,al contrario de un array dinámico, si en un objeto StringList se elimina un

elemento de la lista no queda ningún hueco, ya que vuelven a agruparse suselementos.

Page 292: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 292/306

Un error común para eliminar todos los elementos de la lista sería hacer losiguiente:

var  i : I nteger ;begi n  f or i : = 0 t o Cl i ent es. Count - 1 do  Cl i ent es. Del et e( i ) ;end;

Nos daría el error:

List index out of bounds(3)

¿Por qué ocurre esto? Pues por la sencilla razón de que cada que se elimina unelemento, al reagruparse el resto de los elementos se va también

disminuyendo el número de los mismos y sus índices, con lo cual cuandointenta acceder a un elemento que ya no existe provoca un error. La maneracorrecta de hacerlo sería:

var  i : I nteger ;begi n  f or i : = 0 t o Cl i ent es. Count - 1 do  Cl i ent es. Del et e( 0 ) ;end;

Como vemos arriba se elimina sólo el primer elemento y como se van

eliminando como si fuera una pila de platos al final desaparecen todos.Naturalmente una clase StringList dispone del método Clear que eliminatodos los elementos de la lista:

Cl i ent es. Cl ear ;

MOVIENDO ELEMENTOS EN LA LISTA

Supongamos que tenemos la anterior lista de clientes:

CARLOS ABENZA MARTI NEZPASCUAL CAMPOY FERNANDEZMARI A PALAZÓN PÉREZPEDRO SANCHEZ GARCI ALUI S GARCI A ROJ OMANUEL ESCUDERO BERNAL

Vamos a mover a MARIA a la primera posición de la lista:

Cl i ent es. Move( 2, 0 ) ;

El comando Move acepta como primer parámetro en que posición esta elelemento que deseamos mover y como segundo parámetro su destino. En el

caso anterior llevamos a MARIA de la posición 2 a la 0:MARI A PALAZÓN PÉREZ

Page 293: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 293/306

CARLOS ABENZA MARTI NEZPASCUAL CAMPOY FERNANDEZPEDRO SANCHEZ GARCI ALUI S GARCI A ROJ OMANUEL ESCUDERO BERNAL

Ahora bien, no hay que confundirse el mover elementos en la lista conintercambiarlos. El mover un elemento sólo lo elimina de la posición actual ylo lleva a la posición deseada, pero mantiene el orden natural de la lista. Si loque queremos es intercambiar dos elementos para ello disponemos delmétodo Exchange el cual tiene los mismos parámetros que Move salvo que loque hace es intercambiar las posiciones de ambos elementos.

Vamos a ver como cambiar la posición de MANUEL (que es la 5) con la deCARLOS (que es la 1):

Cl i ent es. Exchange( 5, 1 );

Su resultado sería:

MARI A PALAZÓN PÉREZMANUEL ESCUDERO BERNALPASCUAL CAMPOY FERNANDEZPEDRO SANCHEZ GARCI ALUI S GARCI A ROJ OCARLOS ABENZA MARTI NEZ

BUSCANDO ELEMENTOS EN LA LISTA

Se puede averiguar la posición de un elemento en la lista con con los métodosIndexOf  e IndexOfName:

function IndexOf( const S: string ): Integer; override;function IndexOfName(const Name: string): Integer; virtual;

El método IndexOf  toma como parámetro el nombre del elemento que sedesea buscar y devuelve su índice si lo encuentra (el primer elemento es elcero) y -1 si no lo encuentra. Por ejemplo:

Cl i ent es. I ndexOf ( ' PEDRO SANCHEZ GARCI A' ) devuel ve 3Cl i ent es. I ndexOf ( ' PEDRO SANCHEZ' ) devuel ve - 1

La función IndexOf  sólo busca el primer elemento encontrado en la lista, demodo que si hay dos elementos iguales sólo muestra el índice del primero (deahí la importancia de tener la lista ordenada).

Después tenemos la función IndexOfName destinada a buscar elementos detipo NOMBRE = VALOR como vimos anteriormente con el diccionario:

Di cci onar i o. I ndexOf Name( ' BOOK' ) devuel ve 0Di cci onar i o. I ndexOf Name( ' MOUSE' ) devuel ve 1Di cci onar i o. I ndexOf Name( ' TABLE' ) devuel ve 2

Page 294: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 294/306

Por último tenemos la función Find utilizada para buscar elementos sólo si lalista esta ordenada. Por ejemplo:

var  i Posi ci on: I nt eger;  bEncont r ado: Bool ean;begi n  Cl i ent es. Sor t ; / / ordenamos l a l i st a  bEncont r ado : = Cl i ent es. Fi nd( ' PEDRO SANCHEZ GARCI A' , i Posi ci on ) ;end;

La función Find es similar a función IndexOf  añadiendo como segundoparámetro una variable donde depositar la posición del elemento buscado ycuyo valor devuelto en la función será True o False dependiendo si haencontrado el elemento en la lista.

Después de ejecutar este código la lista quedaría de la siguiente manera:CARLOS ABENZA MARTI NEZLUI S GARCI A ROJ OMANUEL ESCUDERO BERNALMARI A PALAZÓN PÉREZPASCUAL CAMPOY FERNANDEZPEDRO SANCHEZ GARCI A

La variable bEncontrado valdría True y iPosicion sería igual a 5.

En el próximo artículo terminaremos de ver todas las funcionalidades de un

StringList.Pruebas realizadas en Delphi 7.

El objeto StringList (III)IMPORTAR Y EXPORTAR ELEMENTOS DE LA LISTA

Se pueden guardar los elementos de la lista en un archivo de texto utilizandopara ello el método SaveToFile:

procedure SaveToFile( const FileName: string ); virtual;

Por ejemplo vamos a guardar nuestra lista de clientes en el mismo directoriodonde ejecutamos el programa:

Cl i ent es. SaveToFi l e( Extr act Fi l ePat h( Appl i cat i on. ExeName ) +' cl i ent es . t xt ' ) ;

Si más adelante deseamos recuperar el listado utilizamos el procedimiento:

procedure LoadFromFile( const FileName: string ); virtual;

Page 295: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 295/306

Por ejemplo:

Cl i ent es. LoadFromFi l e( Ext r act Fi l ePat h( Appl i cat i on. ExeName ) +' cl i ent es . t xt ' ) ;

Si tenemos dos listas en memoria también podemos copiar elementos de unalista a otra. Por ejemplo:

var  Ani mal es, Ani mal es2: TSt r i ngLi st ;begi n  Ani mal es : = TSt r i ngLi st . Cr eat e;  Ani mal es2 : = TSt r i ngLi st . Cr eate;

  Ani mal es. Add( ' PERRO' ) ;  Ani mal es. Add( ' GATO' ) ;  Ani mal es. Add( ' LEÓN' ) ;

  Ani mal es2. Add( ' VACA' ) ;  Ani mal es2. Add( ' PATO' ) ;  Ani mal es2. Add( ' ÁGUI LA' ) ;

  / / Añadi mos a l a l i st a de ANI MALES el cont eni do de ANI MALES2 yor denamos l a l i st a  Ani mal es. AddSt r i ngs( Ani mal es2 ) ;  Ani mal es. Sor t ;

  / / Most r amos el r esul t ado en un campo Memo  Memo. Li nes. Add( Ani mal es. Text ) ;

  Ani mal es2. Fr ee;  Ani mal es. Free;end;

El resultado de la lista Animales después de ordenarla sería:

ÁGUI LAGATOLEÓNPATOPERROVACA

Otra de las cosas que se pueden hacer es asignar los elementos de una lista aotra, eliminando los elementos de la lista original (sería algo así como copiar ypegar). Por ejemplo si queremos sustituir todos los elementos de la listaAnimales por los de Animales2 habría que hacer:

Ani mal es. Assi gn( Ani mal es2 ) ;

lo cual elimina todos los elementos de la lista Animales e inserta todos los deAnimales2.

LISTAS ORDENADAS AUTOMÁTICAMENTE

Anteriormente vimos como una lista podía ordenarse con el método Sort, pero

Page 296: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 296/306

Page 297: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 297/306

AUDIBMWPEUGEOT

Otra de las operaciones que no se pueden hacer en una lista ordenada es

añadir elementos duplicados. En el caso anterior si hacemos:

Vehi cul os. Add( ' BMW' )

no hará absolutamente nada. Para poder añadir elementos duplicados en unalista ordenada hay que modificar la propiedad Duplicates la cual puedecontener los siguientes valores:

dupIgnore -> Ignora el añadir elementos duplicados (así es como está pordefecto)dupAccept -> Acepta elementos duplicados

dupError -> Da un error al añadir elementos duplicados

Si hacemos lo siguiente:

Vehi cul os. Dupl i cat es : = dupAccept ;Vehi cul os. Add( ' BMW' ) ;

Entonces si funcionará correctamente:

AUDIBMWBMW

PEUGEOT

Pero si activamos:

Vehi cul os. Dupl i cat es : = dupEr r or;Vehi cul os. Add( ' BMW' ) ;

Nos mostrará el mensaje:

String list does not allow duplicates (StringList no acepta duplicados).

ASOCIANDO OBJETOS A LOS ELEMENTOS DE UNA LISTAUna de las características más importantes de una lista TStringList es la deasociar a cada elemento de la lista la instacia de un objeto creado. Vamos aver un ejemplo donde voy a crear la clase TProveedor y voy a utilizarla paracrear una lista de proveedores donde cada elemento tiene su propio objeto:

type  TPr oveedor = cl ass  sNombre: St r i ng;  cSal do: Cur r ency;  bPagado: Bool ean;

  end;var

Page 298: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 298/306

  Pr oveedor es: TSt r i ngLi st ;  Proveedor : TPr oveedor ;begi n  Pr oveedor es : = TSt r i ngLi st . Cr eat e;

  Proveedor : = TPr oveedor . Cr eat e;

  wi t h Proveedor do  begi n  sNombr e : = ' SUMI NI STROS PALAZÓN' ;  cSal do : = 1200. 24;  bPagado : = Fal se;  end;

  Proveedores. AddObj ect ( ' B78234539' , Proveedor ) ;

  Proveedor : = TPr oveedor . Cr eat e;  wi t h Proveedor do  begi n  sNombre : = ' ELECTRI DAUTON, S. L. ' ;  cSal do : = 2460. 32;  bPagado : = Tr ue;  end;

  Proveedores. AddObj ect ( ' B98654782' , Proveedor ) ;

  Proveedor : = TPr oveedor . Cr eat e;  wi t h Proveedor do  begi n  sNombre : = ' FONTANERI A MARTI NEZ, S. L. ' ;  cSal do : = 4800. 54;  bPagado : = Fal se;  end;

  Proveedores. AddObj ect ( ' B54987374' , Proveedor ) ;end;

Hemos creado una lista de proveedores e instanciado tres proveedoresasociandolos a la lista mediante el C.I.F. del proveedor. Ahora bien, ¿comoaccedemos a cada proveedore de la lista? Pues muy fácil. El objeto StringListdispone de la propiedad Objects la cual nos devuelve la referencia a unobjeto en cuestión. Por ejemplo vamos a buscar a FONTANERIA MARTINEZpor su C.I.F.:

var

  i Pr oveedor : I nt eger ;begi n  i Proveedor : = Proveedores. I ndexOf ( ' B54987374' ) ;

  i f i Pr oveedor > - 1 t hen  begi n  Proveedor : = Proveedores. Obj ect s[ i Proveedor ] as TPr oveedor ;  Memo. Li nes. Add( ' ' ) ;  Memo. Li nes. Add( ' BUSCANDO AL PROVEEDOR: B54987374' ) ;  Memo. Li nes. Add( ' ' ) ;  Memo. Li nes. Add( ' NOMBRE: ' + Pr oveedor . sNombre ) ;  Memo. Li nes. Add( ' SALDO: ' + Cur r ToSt r ( Proveedor . cSal do ) ) ;  Memo. Li nes. Add( ' PAGADO: ' + Bool ToSt r ( Proveedor . bPagado, True )

) ;  end;end;

Page 299: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 299/306

El resultado sería:

BUSCANDO AL PROVEEDOR: B54987374

NOMBRE: FONTANERI A MARTI NEZ, S. L.

SALDO: 4800, 54PAGADO: Fal se

Esto es algo mucho más útil que utilizar un array:

var  Proveedores: ar r ay of TProveedor;  . . . .

ya que es mucho más flexible y no hay que preocuparse por los huecos alañadir o eliminar registros. Esto nos permite volcar información de registrosde la base de datos a memoria mapeando el contenido de un registro(Clientes, Facturas, etc) dentro de su clase correspondiente (Por ejemplo: sepodrían cargar una cantidad indeterminada de albaranes del mismo clientepara sumarlos y pasarlos a factura).

Para añadir objetos a un StringList aparte de la función AddObject se puedeninsertar objetos mediante el procedimiento:

procedure InsertObject( Index: Integer; const S: string; AObject: TObject );override;

el cual inserta el elemento en la posición que deseemos desplazando el restode la lista. Otra de las ventajas de asociar objetos a un StringList en vez deutilizar un array es que al liberarlo de memoria también elimina cadainstancia de objeto asociado a cada elemento de la lista:

Proveedores. Fr ee;

no teniendo así que preocuparnos con errores de liberación de memoria.

Con esto terminados de vez las características más importantes de la claseTStringList.

Pruebas realizadas en Delphi 7.

Page 300: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 300/306

Convertir cualquier tipo de variable a String(I)Vamos a ver de que funciones dispone Delphi para pasar cualquier tipo devariable a una cadena de texto (String):

function CurrToStr( Value: Currency ): string; overload;

Convierte un valor Currency a String. Por ejemplo:

Curr ToSt r ( 1234. 5678 ) devuel ve 1234, 5678

function CurrToStrF( Value: Currency; Format: TFloatFormat; Digits:Integer ): string; overload;

Convierte un valor Currency a String usando un formato específicodeterminado por el parámetro Format y por el número de dígitos Digits. Porejemplo:

Cur r ToSt r F( 1234. 5678, f f Cur r ency, 2 ) devuel ve 1. 234, 57 €Cur r ToSt r F( 1234. 5678, f f Cur r ency, 4 ) devuel ve 1. 234, 5678 €Cur r ToSt r F( 1234. 5678, f f Cur r ency, 0 ) devuel ve 1. 235 €

function DateTimeToStr( DateTime: TDateTime ): string; overload;

Convierte un valor TDateTime a String. Por ejemplo:

var  dt : TDat eTi me;begi n  dt : = St r ToDateTi me( ' 20/ 06/ 2007 11: 27' ) ;  ShowMessage( Dat eTi meToSt r ( dt ) ) ;  dt : = St r ToDateTi me( ' 20/ 06/ 2007 00: 00' ) ;  ShowMessage( Dat eTi meToSt r ( dt ) ) ;end;

El resultado de ejecutar esto sería:

20/ 06/ 2007 11: 27: 0020/ 06/ 2007

procedure DateTimeToString( var Result: string; const Format: string;DateTime: TDateTime ); overload;

Convierte un valor TDateTime a String usando un formato especificado por elparámetro Format y devolviendo el valor en la variable Result. Por ejemplo:

var  dt : TDat eTi me;  s: St r i ng;

begi n  dt : = St r ToDateTi me( ' 20/ 06/ 2007 11: 27' ) ;  Dat eTi meToSt r i ng( s, ' d/ m/ y' , dt ) ;

Page 301: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 301/306

Page 302: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 302/306

hh = Los dos dí gi t os de l a hor a ( compl etando con cero por l ai zqui er da)n = Los dos dí gi t os de l os mi nut os ( si n compl etar con cer o por l ai zqui er da)nn = Los dos dí gi t os de l os mi nut os ( compl etando con cero por l ai zqui er da)

s = Los dos dí gi t os de l os segundos ( si n compl etar con cero por l ai zqui er da)ss = Los dos dí gi t os de l os segundos ( compl etando con cero por l ai zqui er da)z = Los dí gi t os de l os mi l i segundos ( si n compl et ar con cer o por l ai zqui er da)zzz = Los 3 dí gi t os de l os segundos ( compl et ando con cero por l ai zqui er da)t = For mat o abr evi ado de hor a ( 11: 27)t t = Format o extendi do de hora ( 11: 27: 00)

am/ pm = For mat o de hor a am/ pma/ p = Format o de hor a a/ pampm = I gual que a/ p per o con Ti meAMSt r i ng, Ti mePMSt r i ng/ = Sust i t ui do por el val or de Dat eSeparat or: = Sust i t ui do por el val or de Ti meSeparat or

Y estas son las variables de los formatos predeterminados para fecha/hora:

Dat eSepar at or = / Ti meSepar at or = :Shor t Dat eFor mat = dd/ mm/ yyyyLongDat eFor mat = dd mmm yyyy

 Ti meAMSt r i ng = AM Ti mePMSt r i ng = PMShor t Ti meFor mat = hh: mm

LongTi meFor mat = hh: mm: ssShor t Mont hNames = J an Feb . . .LongMonthNames = Ener o, Febrer o . . .Shor t DayNames = Lun, Mar . . .LongDayNames = Lunes, Mar t es . . .

 TwoDi gi t Cent uryWi ndow = 50

function DateToStr( Date: TDateTime ): string; overload;

Convierte un valor TDateTime a String. Por ejemplo:

var

  d: TDat e;begi n  d : = St r ToDate( ' 20/ 08/ 2007' ) ;  ShowMessage( Dat eToSt r ( d ) ) ; / / Most r ar í a 20/ 08/ 2007end;

En el próximo artículo continuaremos viendo más funciones de conversión.

Pruebas realizadas en Delphi 7.

Page 303: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 303/306

Page 304: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 304/306

f = Punt o f i j og = Gener alm = Monedan = Númer o ( Real )p = Puntero ( La di r ecci ón de memor i a)s = St r i ng

u = Deci mal si n si gnox = Hexadeci mal

Dentro de una misma cadena de formato se pueden introducir distintosparámetros:

var  sSer i e: St r i ng;  i Numero: I nt eger ;  r I mpor t e: Real ;begi n  sSer i e : = ' A' ;  i Numer o : = 5276;

  r I mpor t e : = 120. 35;  ShowMessage( Format ( ' Fact ur a nº %s- %d - > I mpor t e %f ' , [ sSer i e,i Numer o, r I mport e] ) ) ;end;

Al ejecutarlo muestra:

  Factur a nº A- 5276 - > I mpor t e 120, 35

De otra manera tendríamos que hacer:

ShowMessage( ' Fact ur a nº ' + sSer i e + ' - ' + I nt ToSt r ( i Numero ) + ' - >

I mpor t e ' + Fl oat ToSt r ( r I mpor t e ) ) ;

function FormatCurr( const Format: string; Value: Currency ): string;overload;

Convierte un valor Currency a String según el formato determinado por elparámetro Format. Por ejemplo:

For mat Cur r ( ' #####' , 1234. 5678 ) devuel ve 1235Format Cur r ( ' 00000' , 1234. 5678 ) devuel ve 01235For mat Cur r ( ' ########' , 1234. 5678 ) devuel ve 1235Format Cur r ( ' 00000000' , 1234. 5678 ) devuel ve 00001235

Format Cur r ( ' 0. ####' , 1234. 5678 ) devuel ve 1234, 5678Format Cur r ( ' 0. 00000' , 1234. 5678 ) devuel ve 1234, 56780

function FormatDateTime( const Format: string; DateTime: TDateTime ):string; overload;

Esta función es similar al procedimiento DateTimeToString para dar formatoa un campo TDateTime. Por ejemplo:

var  dt : TDat eTi me;begi n  dt : = St r ToDateTi me( ' 20/ 06/ 2007 11: 27' ) ;  Format Dat eTi me( ' d/ m/ y' , dt ) ; / / devuel ve 20/ 6/ 07

Page 305: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 305/306

  For matDateTi me( ' dd/ mm/ yyyy' , dt ) ; / / devuel ve 20/ 06/ 2007  For matDateTi me( ' dd- mm- yyyy' , dt ) ; / / devuel ve 20- 06- 2007end;

function FormatFloat( const Format: string; Value: Extended ): string;overload;

Convierte un valor Extended a String utilizar el formato determinado porFormat. Es similar a la función FormatCurr. Por ejemplo:

Format Fl oat ( ' #####' , 1234. 5678 ) devuel ve 1235Format Fl oat ( ' 00000' , 1234. 5678 ) devuel ve 01235For mat Fl oat ( ' ########' , 1234. 5678 ) devuel ve 1235Format Fl oat ( ' 00000000' , 1234. 5678 ) devuel ve 00001235Format Fl oat ( ' 0. ####' , 1234. 5678 ) devuel ve 1234, 5678For matFl oat ( ' 0. 00000' , 1234. 5678 ) devuel ve 1234, 56780

function IntToHex( Value: Integer; Digits: Integer ): string; overload;function IntToHex( Value: Int64; Digits: Integer ): string; overload;

Estas dos funciones convierten un valor Integer o Int64 a String en formatohexadecimal, especificando el número de dígitos a través del parámetroDigits. Por ejemplo:

I ntToHex( 123456, 6 ) devuel ve 01E240I ntToHex( 123456, 5 ) devuel ve 1E240I ntToHex( 123456, 4 ) devuel ve 1E240

function IntToStr( Value: Integer ): string; overload;function IntToStr( Value: Int64 ): string; overload;

Ambas funciones convierten un valor Integer o Int64 a String en formatodecimal. Por ejemplo:

I ntToSt r ( 123456 ) devuel ve 123456

procedure Str( X [: Width [: Decimals ]]; var S );

Este procedimiento da un formato a la cadena de texto S la cual esta en unavariable. Además da la posibilidad de especificar el ancho en dígitos delnúmero y sus decimales. Por ejemplo:

var  sText o: St r i ng;begi n  St r ( 1234, sText o ) ; / / sTexto = ' 1234'  St r ( 1234: 10, sTexto ) ; / / sTexto = ' 1234'  St r ( 1234. 5678: 10: 2, sText o ) ; / / sTexto = ' 1234. 57'  St r ( 1234. 5678: 10: 4, sText o ) ; / / sTexto = ' 1234. 5678'

end;

Page 306: Delphi Al Limite

7/18/2019 Delphi Al Limite

http://slidepdf.com/reader/full/delphi-al-limite-56d676657cbf4 306/306