Boletín Pascal #9
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
![]() |
Boletín Pascal #9 INDICE 1. UNAS PALABRAS DEL EDITOR 2. PROGRAMACION DE BASES DE DATOS EN CODIGO (III) * CONSULTAS DE ACCION - UPDATE - INSERT - DELETE * LENGUAJE DE DEFINICION DE DATOS (DATA DEFINITION LANGUAGE - DDL) - CREATE TABLE - ALTER TABLE - DROP TABLE - CREATE INDEX - DROP INDEX * SUBCONSULTAS - EN LA CLAUSULA FROM - EN LAS CLAUSULAS WHERE O HAVING - Exists - In - COMO VALOR 3. ALGUNAS NOTAS ACERCA DE LA PROGRAMACION EN ENTORNOS DE RED * BASES DE DATOS PARADOX * MEJORANDO EL RENDIMIENTO - ACTUALIZACIONES CACHEADAS - COMPONENTES QUE CACHEAN - RANGOS DE CLAVES - PREPARAR CONSULTAS - COMPACTAR TABLAS - TABLAS LOCALES Y DATASETS CLIENTES 4. REDONDEANDO NUMEROS 5. ENLACES KYLIX ________________________________________________________________________ 1. UNAS PALABRAS DEL EDITOR Acerca del artículo "KYLIX FAQ (PREGUNTAS FRECUENTES)" de la edición pasada, un suscriptor nos informó que la versión beta de Delphi para Linux corre en un número selecto de distribuciones Linux y que los ejecutables que produce requieren grades bibliotecas de tiempo de ejecución. Nosotros realmente esperamos que la versión final corra en todas las distros y que produzca ejecutables independientes, pero no podemos asegurar nada pues toda la información proporcionada por Borland acerca del proyecto Kylix no es oficial y está sujeta a cambios hasta cerca de la fecha de lanzamiento, cuando se conozcan los hechos reales. Borland anunció el lugar y las fechas de la BorCon 2001. La XII Conferencia Anual de Borland (Americas) tendrá lugar del 21 al 25 de Julio del 2001 en Long Beach, California. Quienes deseen más información podrán encontrarla aquí: http://www.borland.com/conf2001/ Hay un par de trucos nuevos en nuestra sección de trucos Delphi: ¿Cómo... * Cambiar la resolución de la pantalla? * Hacer que una aplicación se ejecute automáticamente cuando Windows se inicie? Saludos, Ernesto De Spirito eds2004 @ latiumsoftware.com ________________________________________________________________________ JfControls Lib. Multilenguaje. Multiapariencia. Skins. Privilegios. Más de 40 componentes integrados y personalizables. Múltiples problemas de programación resueltos. Administración centralizada de recursos. Para Delphi 3-7 y C++ Builder 3-6. http://www.jfactivesoft.com/spindex.htm ________________________________________________________________________ 2. PROGRAMACION DE BASES DE DATOS EN CODIGO (III) CONSULTAS DE ACCION =================== UPDATE ------ UPDATE nomtabla SET campo1 = valor1 [, campo2 = valor2 [, ...]] [WHERE condicion] Esta sentencia SQL le permite modificar los valores de uno o más campos en uno o más registros de una tabla. En xBase: USE custoly REPLACE ALL Country WITH "USA" FOR Country = "U.S.A." En SQL: UPDATE custoly SET Country = "USA" WHERE Country = "U.S.A." En Delphi: procedure TForm1.Button1Click(Sender: TObject); var Query1: TQuery; begin Query1 := nil; try Query1 := TQuery.Create(nil); Query1.DatabaseName := 'DBDEMOS'; Query1.SQL.Add('UPDATE custoly SET Country = "USA"'); Query1.SQL.Add('WHERE Country = "U.S.A."'); Query1.ExecSQL; except Query1.Free; raise; end; Query1.Free; end; INSERT ------ INSERT INTO nomtabla (campo1 [, campo2 [, ...]]) VALUES (valor1 [, valor2 [, ...]]) INSERT INTO nomtabla (campo1 [, campo2 [, ...]]) SELECT ... Esta sentencia SQL agrega un registro o registros a una tabla. El siguiente ejemplo muestra como agregar un registro a una tabla: En xBase: USE custoly APPEND BLANK REPLACE Last_Name WITH "Doe", First_Name WITH "John", ; EMail WITH "johndoe@nn.com" En SQL: INSERT INTO custoly (Last_Name, First_Name, EMail) VALUES ("Doe", "John", "johndoe@nn.com"); En Delphi: ... Query1.SQL.Add('INSERT INTO custoly (Last_Name, First_Name,'); Query1.SQL.Add(' EMail) VALUES ("Doe", "John", "johndoe@nn.com");'); Query1.ExecSQL; ... El siguiente ejemplo muestra como agregar varios registros: En xBase: USE custoly APPEND FROM custoly2 FOR Country = "USA" ; FIELDS Last_Name, First_Name, EMail En SQL: INSERT INTO custoly (Last_Name, First_Name, EMail) SELECT Last_Name, First_Name, EMail FROM custoly2 WHERE Country = "USA"; En Delphi: ... Query1.SQL.Add('INSERT INTO custoly (Last_Name, First_Name, EMail)'); Query1.SQL.Add('SELECT Last_Name, First_Name, EMail FROM custoly2'); Query1.SQL.Add('WHERE Country = "USA";'); Query1.ExecSQL; ... DELETE ------ DELETE FROM nomtabla [WHERE condicion] Esta sentencia SQL borra uno o más registros de una tabla. En xBase: USE custoly DELETE ALL FOR Country = "US" En SQL: DELETE FROM custoly WHERE Country = "US" En Delphi: ... Query1.SQL.Add('DELETE FROM custoly WHERE Country = "US"'); Query1.ExecSQL; ... El siguiente ejemplo muestra como borrar todos los registros de una tabla: In xBase: USE custoly DELETE ALL In SQL: DELETE FROM custoly LENGUAJE DE DEFINICION DE DATOS (DATA DEFINITION LANGUAGE - DDL) ================================================================ Local SQL soporta unas pocas sentencias DDL que permiten crear, modificar y borrar tablas, así como crear y borrar índices. CREATE TABLE ------------ CREATE TABLE nomtabla (campo1 tipo1 [(escala1 [,precision1])] [, campo2 tipo2 [(escala2 [,precision2])] [, ...] ] [, [CONSTRAINT restriccion] PRIMARY KEY (campoclave1 [, campoclave2 [, ...]]) ] ) Esta sentencia SQL permite crear una tabla en una forma mucho más simple que usando el método CreateTable que hemos introducido en una edición pasada. Ejemplo (SQL): CREATE TABLE "Productos.DBF" (IDProducto SMALLINT, Descripcion VARCHAR(25), Precio FLOAT(10,2), PRIMARY KEY (IDProducto)) Ejemplo (Delphi): ... Query1.SQL.Add('CREATE TABLE "Productos.DBF" (IDProducto SMALLINT,'); Query1.SQL.Add('Descripcion VARCHAR(25), Precio NUMERIC(10,2),'); Query1.SQL.Add('PRIMARY KEY (IDProducto))'); Query1.ExecSQL; ... ALTER TABLE ----------- ALTER TABLE nomtabla { DROP [COLUMN] campo1 | ADD [COLUMN] campo1 type1 [(escala1 [,precision1])] } [, { DROP [COLUMN] campo2 | ADD [COLUMN] campo2 type2 [(escala2 [,precision2])] } [,...] ] Esta sentencia SQL permite añadir y/o remover columnas (campos) de una tabla. Por ejemplo: ALTER TABLE "Productos.DBF" ADD Costo NUMERIC(10,2), ADD Stock INTEGER ALTER TABLE "Productos.DBF" DROP Stock, ADD EnStock FLOAT(6,1) En Delphi: ... Query1.SQL.Add('ALTER TABLE "Productos.DBF" ADD Costo NUMERIC(10,2),'); Query1.SQL.Add('ADD Stock INTEGER'); Query1.ExecSQL; Query1.SQL.Clear; Query1.SQL.Add('ALTER TABLE "Productos.DBF" DROP Stock,'); Query1.SQL.Add('ADD EnStock FLOAT(6,1)'); Query1.ExecSQL; ... DROP TABLE ---------- DROP TABLE nomtabla Esta sentencia SQL borra una tabla. Ejemplo: ... Query1.SQL.Text := 'DROP TABLE "Productos.DBF"'; Query1.ExecSQL; ... CREATE INDEX ------------ CREATE [UNIQUE] [ASC | DESC] INDEX nomindice ON nomtabla (campoclave1 [, campoclave2 [,...]]) Esta sentencia SQL crea un índice en la tabla especificada. Por ejemplo: En xBase: USE vendors INDEX ON VendorName TO PorNombre En SQL: CREATE INDEX PorNombre ON vendors (VendorName) En Delphi: ... Query1.SQL.Text := 'CREATE INDEX PorNombre ON vendors (VendorName)'; Query1.ExecSQL; ... DROP INDEX ---------- DROP INDEX nomtabla.nomindice Esta sentencia SQL borra un índice de la tabla especificada. Para borrar la clave primaria tiene que escribir PRIMARY en lugar del nombre del índice. Ejemplo: ... Query1.SQL.Text := 'DROP INDEX vendors.PorNombre'; Query1.ExecSQL; ... SUBCONSULTAS ============ Una subconsulta es una consulta de selección dentro de otra consulta. Las subconsultas se encierran entre paréntesis. Hay varios lugares donde se puede usar una consulta... EN LA CLAUSULA FROM ------------------- La mayoría de los motores SQL permiten que una subconsulta sea usada como una tabla en la cláusula FROM, pero Local SQL no se encuentra entre ellos. Por ejemplo, esto no funcionaría: SELECT Company, OrderNo, ItemsTotal FROM (SELECT OrderNo, CustNo, ItemsTotal FROM Orders WHERE ItemsTotal > 50000) Ord INNER JOIN Customer ON Ord.CustNo = Customer.CustNo WHERE Country = "US" En el caso del ejemplo, la consulta puede modificarse para obtener el resultado deseado sin usar subconsultas: SELECT Company, OrderNo, ItemsTotal FROM Orders INNER JOIN Customer ON Orders.CustNo = Customer.CustNo WHERE Country = "US" AND ItemsTotal > 50000 Cuando esto no es posible, la solución es: 1) Crear una tabla temporal: CREATE TABLE tmp (OrderNo NUMERIC(8), CustNo NUMERIC(8), ItemsTotal MONEY); 2) Hacer que contenga los resultados de la que era la subconsulta: INSERT INTO tmp (OrderNo, CustNo, ItemsTotal) SELECT OrderNo, CustNo, ItemsTotal FROM Orders WHERE ItemsTotal > 50000; 3) Usa esta tabla temporal en la consulta original: SELECT Company, OrderNo, ItemsTotal FROM tmp Orders INNER JOIN Customer ON Orders.CustNo = Customer.CustNo WHERE Country = "US" 4) Borrar la tabla temporal cuando ya no se la necesite: DROP TABLE tmp EN LAS CLAUSULAS WHERE O HAVING ------------------------------- Una subconsulta puede ser parte de una condición usando diferentes "predicados" (operadores). Estas subconsultas pueden usar valores provenientes de la consulta dentro de la cual se encuentra y en este caso son procesadas por cada registro de esa consulta. Exists ------ EXISTS subconsulta Este predicado devuelve True si la subconsulta devuelve al menos una fila. Por ejemplo, la siguiente consulta devolverá los nombres de todos los empleados que no hicieron ventas entre 1990 y 1994: SELECT LastName, FirstName FROM employee WHERE NOT EXISTS (SELECT OrderNo FROM orders WHERE orders.EmpNo = employee.EmpNo AND EXTRACT(YEAR FROM SaleDate) BETWEEN 1990 AND 1994) In -- valor IN (conjunto) Este predicado devuelve True si el valor se encuentra en un conjunto. Dicho conjunto puede ser una lista de valores (por ejemplo "CA", "FL", VA") o una subconsulta que devuelva 0, 1 o más registros. Para invertir la condición puede usar NOT IN. El siguiente ejemplo devolvería los clientes de Sudamérica: SELECT CustNo, Company, Country FROM customer WHERE Country IN (SELECT Name FROM Country WHERE Continent = "South America") COMO VALOR ---------- En general, puede usar una subconsulta en cualquier otra situación donde puede usar un simple valor. La limitación es que la subconsulta debe devolver una sola fila y sólo el primer campo es usado (otros motores SQL no limitan la cantidad de filas). Por ejemplo, puede usar una subconsulta para calcular el valor de un campo: SELECT (SELECT SUM(Qty) FROM Items WHERE OrderNo = 1004 GROUP BY OrderNo) AS Qty1004, (SELECT SUM(Qty) FROM Items WHERE OrderNo = 1005 GROUP BY OrderNo) AS Qty1005 FROM Customer WHERE CustNo = 1221 No usamos el cliente #1221 en la consulta, pero es un truco para producir un solo registro en el resultado (otros motores SQL permiten la omisión de la cláusula FROM para el mismo propósito). El resultado sería: Qty1004 Qty1005 ------- ------- 33 21 Para dar otro ejemplo, la siguiente consulta devuelve todas las órdenes cuyos montos están por sobre el promedio: SELECT OrderNo, ItemsTotal FROM orders WHERE ItemsTotal > (SELECT AVG(ItemsTotal) FROM orders) El código fuente completo de los ejemplos de este boletín está disponible aquí: http://www.latiumsoftware.com/descarga/p0009.zip ________________________________________________________________________ 3. ALGUNAS NOTAS ACERCA DE LA PROGRAMACION EN ENTORNOS DE RED BASES DE DATOS PARADOX ====================== Todas las aplicaciones que acceden tablas de una misma base de datos Paradox deben usar el mismo archivo PDOXUSRS.NET. El bloqueo de registros y otras características dependen de esto. No usar el mismo archivo PDOXUSRS.NET puede resultar en datos corruptos y en pérdida de datos. El programa de instalación instala el archivo PDOXUSRS.NET en el direc- torio raíz del primer disco duro de cada computadora donde lo corra ("C:\"), y establece la configuración NET DIR en su archivo IDAPI.CFG en "C:\". Por consiguiente, de modo predeterminado, todas las aplica- ciones de una misma estación de trabajo (EDT) comparten el mismo archivo PDOXUSRS.NET, pero no todas las aplicaciones corriendo desde la red! Hay dos formas de corregir esto. Una forma es cambiar el archivo IDAPI.CFG en cada EDT: 1) Ejecute el BDE Administrator 2) En el menú "Object" elija "Open configuration..." 3) Abra el archivo IDAPI.CFG 4) En la solapa "Configuration" vaya a Configuration/Drivers/Native/ Paradox 5) Cambie la entrada NET DIR para apuntar a la ubicación del archivo PDOXUSRS.NET compartido. Este puede ser por ejemplo F:\ en una EDT y E:\ en otra, dependiendo de como esté configurado el mapeo de unidades en cada EDT. Para evitar la dependencia en el mapeo de unidades y usar un nombre uniforme en todas las EDTs puede usar el camino de red \\MAQUINA\RECURSO\[CAMINO\], donde MAQUINA es el nombre del servidor donde está localizado el archivo, RECURSO es el nombre del recurso compartido (un directorio) y opcionalmente CAMINO es el camino del subdirectorio donde está ubicado el archivo PDOXUSRS.NET. Si el archivo está localizado en C:\TODOS\BASEDATOS\PDOXUSRS.NET por ejemplo, en un servidor llamado "SERVIDOR" que comparte el directorio C:\TODOS bajo el nombre "COMPARTIDO", entonces el NET DIR en todas las EDT (incluyendo el servidor) debería ser \\SERVIDOR\COMPARTIDO\BASEDATOS\ 6) Guardar los cambios (Object / Save As configuration). La otra forma de hacerlo es establecer la propiedad NetFileDir del objeto Session de sus aplicaciones antes de abrir cualquier tabla o consulta. Por ejemplo: Session.NetFileDir := '\\SERVIDOR\COMPARTIDO\BASEDATOS\'; MEJORANDO EL RENDIMIENTO ======================== La BDE tiene problemas serios en cuanto a rendimiento en una red, y esta es una de las razones por las cuales hay varios reemplazos de la BDE dando vuelta: * BDE Alternatives Guide http://www.kylecordes.com/bag/index.html No es mucho lo que se puede hacer para mejorar el rendimiento de la BDE, pero puede intentar algunos de los siguientes consejos para ver si ayudan. ACTUALIZACIONES CACHEADAS ------------------------- Las actualizaciones cacheadas (cached updates) aparentemente no sólo cachea las actualizaciones a una tabla, sino que también cachea los registros leídos de la tabla. El problema es que hace las cosas más complicadas y tiene algunas desventajas... Puede encontrar información sobre actualizaciones cacheadas en la ayuda de Delphi: Developing Database Applications Working with cached updates COMPONENTES QUE CACHEAN ----------------------- Si busca en la Internet debe poder encontrar reemplazos para algunos componentes de acceso a datos (como TTable y TQuery) y algunos controles de datos (como TDBGrid, TDBLookupListBox y TDBLookupComboBox) que realizan algo de cacheo para brindar un mejor rendimiento. Estos componentes habitualmente no son freeware, pero le sugerimos que los pruebe porque bien pueden valer su costo de registración. RANGOS DE CLAVES ---------------- Los rangos de claves son por lejos más veloces que los filtros. Debe aplicar rangos de claves en vez de filtros cuando sea posible para reducir la cantidad de registros y luego si es necesario usar filtros para obtener los registros que desee. Por ejemplo, si su condición de filtro es como esta: CustNo = X AND SaleDate > D1 AND SaleDate < D2 AND Code = Y y tiene un índice por CustNo y SaleDate, entonces debería aplicar un rango de claves ( SetRange([X,D1], [X,D2]); ) y usar un filtro con la condición Code = Y. PREPARAR CONSULTAS ------------------ Si usa una consulta más o menos frecuentemente puede prepararla (con el método Prepare) para hacer que la BDE la compile y la guarde compilada hasta que la libere (con el método Unprepare). Cuando una consulta está preparada se ejecuta más rápido. COMPACTAR TABLAS ---------------- Los registros borrados ocupan espacio, y cuando el borrado de registros sucede más o menos a menudo, tarde o temprano los registros borrados pasan a ser más del 50% del total del número de registros. El espacio no utilizado no es en sí el problema, sino que se desperdicia ancho de banda de la red transfiriendo registros que no se usan. Por esta razón debe compactar las tablas removiendo los registros borrados cada cierto tiempo. El ejemplo de DbiPackTable en el archivo de ayuda muestra claramente como hacer esto. TABLAS LOCALES Y DATASETS CLIENTES ---------------------------------- Tal vez la mejor y más simple forma de reducir el tráfico de la red y mejorar el rendimiento sea teniendo una copia local de algunas tablas de la base de datos y usar esas copias locales para leer registros. Cuando realice una actualización (ABC), debe actualizar tanto la tabla local como la tabla en el servidor. Esto asegura que la tabla del servidor siempre esté actualizada. El problema es que los cambios hechos por otros usuarios no serán visibles en su aplicación hasta que "refresque" las tablas locales (copiándolas nuevamente desde el servidor), y por lo tanto esta técnica sólo debe usarse con tablas para las cuales es bastante improbable que dos o más usuarios vayan a agregar o editar el mismo registro con datos diferentes. Debe refrescar las tablas locales cada tanto: cuando Windows inicia, cuando su aplicación inicia, una vez al día, una vez a la semana, cuando el usuario presione un botón Refrescar, o cuando sea... Dependerá de la tabla, y puede juzgar caso por caso: algunas tablas pueden requerir refrescos más frecuentes que otras, y debe sacar ventaja de este hecho para no refrescar las tablas con mayor frecuencia que la necesaria. Si usa tablas Paradox, tiene que usar una sesión diferente para las tablas locales con su propio NetFileDir conteniendo la ubicación del archivo PDOXUSRS.NET local. Si tiene la Edición Empresarial de Delphi, puede usar Datasets Clientes (Client Datasets) para algunas de las tablas locales. Los Datasets Clientes guardan los registros en memoria (así que no debería usarlos con grandes tablas) y son muy rápidos y potentes. ________________________________________________________________________ 4. REDONDEANDO NUMEROS La función Round que viene con Delphi realiza lo que se llama "redondeo bancario", significando que un número con una parte fraccional de 0.5 se redondea a veces para arriba y otras para abajo, siempre hacia el número par más cercano. Es decir que por ejemplo Round(3.5) devuelve 4 mientras que Round(2.5) devuelve 2. Aquí va un conjunto de funciones para redondear números, incluyendo RoundN que redondea un número "normalmente" (es decir que RoundN(3.5) devuelve 4 y RoundN(2.5) devuelve 3). function Sgn(X: Extended): Integer; // Devuelve -1, 0 o 1 de acuerdo al signo del argumento begin if X < 0 then Result := -1 else if X = 0 then Result := 0 else Result := 1; end; function RoundUp(X: Extended): Extended; // Devuelve el primer entero mayor que o // igual al número dado en valor absoluto // (se preserva el signo). // RoundUp(3.3) = 4 RoundUp(-3.3) = -4 begin Result := Int(X) + Sgn(Frac(X)); end; function RoundDn(X: Extended): Extended; // Devuelve el primer entero menor que o // igual al número dado en valor absoluto // (se preserva el signo). // RoundDn(3.7) = 3 RoundDn(-3.7) = -3 begin Result := Int(X); end; function RoundN(X: Extended): Extended; // Redondea un numero "normalmente": si la parte fraccional // es >= 0.5 el número se redondea para arriba (ver RoundUp). // En caso contrario, si la parte fraccional es < 0.5, el // número se redondea para abajo (ver RoundDn). // RoundN(3.5) = 4 RoundN(-3.5) = -4 // RoundN(3.1) = 3 RoundN(-3.1) = -3 begin (* if Abs(Frac(X)) >= 0.5 then Result := RoundUp(X) else Result := RoundDn(X); *) Result := Int(X) + Int(Frac(X) * 2); end; function Fix(X: Extended): Extended; // Devuelve el primer entero menor // o igual al número dado. // Int(3.7) = 3 Int(-3.7) = -3 // Fix(3.7) = 3 Fix(-3.1) = -4 begin if (X >= 0) or (Frac(X) = 0) then Result := Int(X) else Result := Int(X) - 1; end; function RoundDnX(X: Extended): Extended; // Devuelve el primer entero menor // o igual al número dado. // RoundDnX(3.7) = 3 RoundDnX(-3.7) = -3 // RoundDnX(3.7) = 3 RoundDnX(-3.1) = -4 begin Result := Fix(X); end; function RoundUpX(X: Extended): Extended; // Devuelve el primer entero mayor // o igual al número dado. // RoundUpX(3.1) = 4 RoundUpX(-3.7) = -3 begin Result := Fix(X) + Abs(Sgn(Frac(X))) end; function RoundX(X: Extended): Extended; // Redondea un numero "normalmente", pero // tenindo en cuenta el signo. // Si la parte fraccional es >= 0.5 el número se // redondea para arriba (ver RoundUpX). // En caso contrario, si la parte fraccional es < 0.5, // el número se redondea para abajo (ver RoundDnX). // RoundX(3.5) = 4 RoundX(-3.5) = -3 begin (* if Abs(Frac(X)) >= 0.5 then Result := RoundUpX(X) else Result := RoundDnX(X); *) Result := Fix(X + 0.5); end; Estas funciones que acabamos de presentar siempre redondean el último dígito entero, pero a veces necesitamos redondear por ejemplo la segunda cifra decimal o en miles, millones y billones. Puede sobrecargar la función RoundN con esta versión que toma un parámetro adicional para indicar el dígito que se redondeará: function RoundN(x: Extended; d: Integer): Extended; // RoundN(123.456, 0) = 123.00 // RoundN(123.456, 2) = 123.46 // RoundN(123456, -3) = 123000 const t: array [0..12] of int64 = (1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000); begin if Abs(d) > 12 then raise ERangeError.Create('RoundN: valor debe estar en -12..12'); if d = 0 then Result := Int(x) + Int(Frac(x) * 2) else if d > 0 then begin x := x * t[d]; Result := (Int(x) + Int(Frac(x) * 2)) / t[d]; end else begin // d < 0 x := x / t[-d]; Result := (Int(x) + Int(Frac(x) * 2)) * t[-d]; end; end; ________________________________________________________________________ 5. ENLACES EN ESPAÑOL ========== * Diego Muñoz Programador Freelance en Delphi. Artículos, trucos y Mi Sistema http://www.crosswinds.net/~diegodjm KYLIX (En Inglés) ================= http://www.borland.com/kylix/ http://slashdot.org/search.pl?query=kylix http://linuxtoday.com/search.php3?query=kylix http://www.drbob42.com/kylix/index.htm http://www.delphi4linux.org/ http://www.oreilly.com/news/kylix_0400.html http://www.oreilly.com/news/kylix_0800.html http://www.delphi32.com/info_facts/kylix/index.asp http://www.hadp.org/kylix.htm http://www.pinpub.com/delphi/kylix1.htm http://www.pinpub.com/delphi/kylix2.htm http://www.linuxworld.com/linuxworld/lw-2000-07/lw-07-newslint_1.html http://www.delphizine.com/opinion/2000/10/di200010fn_o/di200010fn_o.asp http://www.elists.org/mailman/listinfo/kylix http://www.delphi-jedi.org/Jedi:VOYJEDIX:646728132 http://209.35.83.42/bbug/kylix/main.htm http://www.delphizine.com/search/results.asp?QU=kylix http://delphi.about.com/compute/delphi/library/weekly/aa031400a.htm ________________________________________________________________________ Si no has recibido el archivo con el código fuente completo de los ejemplos que se presentan en este boletín, puedes descargarlo de la siguiente dirección: http://www.latiumsoftware.com/descarga/p0009.zip ________________________________________________________________________ Página principal: http://www.latiumsoftware.com/es/pascal/index.php Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/ Para suscribirse / apuntarse: boletin-pascal-subscribe@gruposyahoo.com Para cancelar / removerse: boletin-pascal-unsubscribe@gruposyahoo.com Para reportar problemas con la suscripción: eds2004 @ latiumsoftware.com ________________________________________________________________________ Este boletín se provee "TAL Y COMO ESTA", sin garantía de ninguna clase. Su uso implica la aceptación de nuestros términos de licencia y de la ausencia de garantía que puedes leer en nuestro sitio web. Allí también encontrarás una nota sobre marcas registradas. Te animamos a que redis- tribuyas este boletín, siempre y cuando lo hagas en forma completa (incluyendo la información de copyright), sin modificaciones y de manera gratuita. Los artículos son copyright de sus respectivos autores y se reproducen aquí con el permiso de los mismos. ________________________________________________________________________ Latium Software http://www.latiumsoftware.com/es/index.php Copyright (c) 2000 por Ernesto De Spirito. Todos los derechos reservados ________________________________________________________________________ |
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
¿Errores? ¿Omisiones? ¿Comentarios? Por favor contáctanos!






