Boletín Pascal #15
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
![]() |
Boletín Pascal #15 - 31-ENE-2001 INDICE 1. UNAS PALABRAS DEL EDITOR 2. LIBRERIA JFCONTROLS - DESCARGA - INSTALACION - EJECUTANDO LA DEMO - MI PRIMER EJEMPLO - ADMINISTRACION CENTRALIZADA DE RECURSOS - CARACTERISTICAS DE LA LIBRERIA JFCONTROLS - COSTO Y FORMAS DE PAGO 3. GESTION DE ERRORES EN DELPHI 5 (y II) - GESTOR DE EXCEPCIONES PREDETERMINADO - ERRORES API - ERRORES API COMO EXCEPCIONES - EXCEPCIONES A MEDIDA - OTROS ERRORES 4. WINDOWSE - ANALIZADOR AVANZADO DE VENTANAS! - GREATIS SOFTWARE - WINDOWSE - LICENCIA 5. UN DBGRID CON CASILLAS DE VERIFICACION 6. ¡BORLAND ACABA DE ANUNCIAR LA DISPONIBILIDAD DE KYLIX! 7. ENLACES ________________________________________________________________________ 1. UNAS PALABRAS DEL EDITOR En este número me complace presentar la Librería JfControls, un paquete de componentes que lo ayudará a desarrollar aplicaciones de apariencia y funcionalidad muy profesional. ------------------------------------- A N U N C I O I M P O R T A N T E ------------------------------------- Antes de escribir ese artículo, me puse en contacto con los fabricantes de la librería y los convencí de sortear una licencia de su librería con tres meses de actualizaciones gratuitas, y como una cortesía especial, a los suscriptores de este boletín que participen en dicho sorteo se les dará dos (2) números, y podrán obtener más números refiriendo otros participantes. Para participar, o para obtener más información: http://www.latiumsoftware.com/jfcontrols/index.php?lang=es 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. LIBRERIA JFCONTROLS Buscando enlaces a sitios Delphi para publicar en el número pasado, me encontré con JfActiveSoft: http://www.jfactivesoft.com/spindex.htm Mi primer pensamiento fue "sólo otro más paquete de componentes", pero comencé a leer las características y se veían interesantes, así que me decidí a descargarlo y darle una probadita.. Bueno, les puedo anticipar que no es simplemente otro paquete más de componentes! Realmente me impresionó tanto que decidí escribir acerca de estos controles, pero no les puedo decir con palabras como se ven. Tendrán que verlos por ustedes mismos... DESCARGA ======== http://www.jfactivesoft.com/spdownload.htm Descargue los siguientes archivos: * Programa general para instalaciones (JfSetup) * Librería JfControls Trial (disponible para D3, D4 y D5) * Programa de demostración de la librería JfControls Eso es suficiente para ver la demo, pero ya que está allí, también bájese los siguientes archivos que muy probablemente querrá tener después de ver la demo: * JfControls Library Help (disponible para D3, D4 y D5) * Tutorial en formato PDF de la librería JfControls Ambos archivos están disponibles en Inglés y en Español INSTALACION =========== La instalación es realmente fácil. Primero tiene que instalar JfSetup (el programa general para instalaciones). Esto creará un grupo llamado JfActiveSoft en su menú Inicio donde encontrará JfSetup. Luego tiene que descomprimir el resto de los archivos en un directorio temporal y ejecutar JfSetup para instalar los archivos .stp en su sistema (usando el botón 'Añadir'). Y eso es todo. No tiene que configurar Delphi, editar archivos .inc, compilar unidades, instalar paquetes ni nada... EJECUTANDO LA DEMO ================== Ahora que tiene los componentes instalados, ejecute Delphi y abra el proyecto demo, que habitualmente se encuentra en: C:\Archivos de programa\JfActiveSoft\JfControls_Demo\JfDemoSt.dpr Presione F9 y comience a explorar la demo... Le recomiendo que haga clic en "Components for menus" ("Componentes para Menús") y luego seleccione "TJfMainMenu". Use el ratón para navegar por TODAS las opciones en los menús y submenús porque hay algunas cosas interesantes para ver... También puede jugar un poquito con las opciones en el cuadro de diálogo. Haga clic en "Configuration" ("Configuración") en el menú superior, seleccione "Language selection" ("Selección de Idioma") y luego escoja por ejemplo "Spanish" y haga clic en "OK". Haga clic en "Configuration" nuevamente y esta vez Seleccione "Colors configuration (Themes)" ("Configuraciones de colores (Temas)"), elija un esquema de colores y haga clic en "Aceptar". MI PRIMER EJEMPLO ================= ¿Cuán difícil puede ser hacer algo como esa demo con esos componentes? Inicie un nuevo proyecto y compruébelo por usted mismo... En la Paleta de Componentes de Delphi verá tres nuevas fichas: Jf Standard, Jf Additional y Jf DataControls que contienen cerca de 40 componentes en total, pero en este ejemplo vamos a usar solamente unos pocos componentes de la ficha Jf Standard. Primero que todo, coloque un componente TJfCApplication y un componente TJfCForm en su formulario. El formulario principal de su aplicación debería tener un componente TJfCApplication y cada formulario debería tener un componente TJfCForm. En realidad esto no es completamente necesario, pero algunas características dependen de esto. Ahora vamos a agregar un fondo al formulario: * Haga clic en el componente JfCForm1 que acaba de colocar * En el Inspector de Objetos busque la propiedad SetBackground y haga clic en el botón "elipsis" (...). Verá el diálogo "SetsBackGround". * Haga clic en "Add". Verá el diálogo "Add BackGround". * Si lo desea, puede proveer un nombre y una descripción para el fondo (por ejemplo "Fondo estándar 1" y "Simple degradé" respectivamente). * En el combo Style elija "bgsHorzOut" * Seleccione los colores de inicio (Start color) y fin (End color) (puede hacer clic en los botones selectores de colores). En el área "Result Background" podrá previsualizar su fondo. * Haga clic en "OK" para agregar el nuevo fondo. Lo verá en la lista. * Haga clic en "OK" para seleccionar este fondo para el formulario. En el caso de fondos de formularios, éstos sólo son visibles en tiempo de ejecución, así que presione F9 para verlo. Cierre la aplicación y ahora coloque un componente JfCLabel en su formulario. Ahora vamos a establecer el tipo de letra de la etiqueta. Puede verse algo complicado al principio, pero se dará cuenta de la ventaja después: * Haga clic en el componente JfCLabel1 que acaba de colocar * En el Inspector de Objetos busque la propiedad Text y haga clic en el botón "más" (+). * Busque la propiedad SetFont y haga clic en su botón "elipsis" (...). Verá el diálogo "SetsFont". * Haga clic en "Add". Verá el diálogo "Add Font". * Si lo desea, puede proveer un nombre y una descripción para la fuente (por ejemplo "Fuente1" y "Times New Roman 14 Bold" respectivamente). * Haga clic en el botón "triángulo" para abrir el diálogo "Fuente". * Seleccione "Times New Roman", "Bold" y "14", y haga clic en "Aceptar". * Haga clic en "OK" para agregar la nueva fuente. La verá en la lista. * Haga clic en "OK" para seleccionar esta fuente para la etiqueta. Ahora establezca un fondo para la etiqueta siguiendo un procedimiento similar al realizado con el componente JfCForm1, agregando así un nuevo fondo a la lista. Ahora establezca la forma de la etiqueta: * Establezca AutoSize en False. * Abra el propiedad-objeto Shape (haciendo clic en su botón "más" (+)). * Establezca la propiedad Visible de la forma (Shape) en True. * Establezca la propiedad Shape de la forma (Shape) en shaArrow. * Abra la propiedad objeto Brush (brocha) de la forma (Shape). * Haga clic en el botón "elipsis" (...) de la propiedad SetBackground de la brocha (Brush). Verá el diálogo "SetsBackground". * Seleccione el fondo que anteriormente le dio al formulario y haga clic en el botón "OK". * Cambie el tamaño de la etiqueta para que el texto quede dentro de la flecha. Coloque un JfCBitBtn en su formulario y establezca su fuente: * Haga clic en el componente JfCBitBtn1 que acaba de colocar * En el Inspector de Objetos busque la propiedad Text y haga clic en el botón "más" (+) para abrir las opciones. * Busque la propiedad SetFont y haga clic en su botón "elipsis" (...). Verá el diálogo "SetsFont". * Puede agregar una nueva fuente como ha hecho antes, pero esta vez simplemente seleccione la fuente que usó para la etiqueta y haga clic en "OK". Ahora establezca el fondo del botón con la propiedad SetBackground, pero esta vez seleccione un fondo existente (el que usó para la etiqueta o el que usó para el formulario). El código fuente completo está disponible para descargar en: http://www.latiumsoftware.com/descarga/p0015.zip ADMINISTRACION CENTRALIZADA DE RECURSOS ======================================= Como puede ver, hay una lista centralizada de fondos y también una lista de fuentes que actúan como repositorios para esos recursos. Hay también repositorios para imágenes, colores y constantes de cadenas. Estos repositorios se llaman "paquetes" (no los confunda con los paquetes de Delphi). Toma algunos pasos agregar un recurso nuevo, pero una vez que lo hecho, son fáciles de usar, acelerando el desarrollo de su aplicación. Este modelo tiene una efecto colateral importante, que es una de las razones por la que los JfControls fueron diseñados de esa forma: si se modifica un recurso, todos los componentes que comparten ese recurso serán automáticamente "actualizados". Por ejemplo, haga clic en la propiedad SetFont del botón, luego seleccione la fuente que usó para el botón y la etiqueta, haga clic en el botón "Modify" y cambie la tipografía a Arial y haga clic en "OK" para aplicar los cambios y vuelva a hacer en "OK" para seleccionar la fuente. Como podrá observar, los cambios también se aplicaron a la etiqueta ya que ambos comparten el mismo recurso de fuente. En el caso de constantes de cadena, también hay un repositorio ("paquete") para ellas, y por ejemplo esto permite la traducción en tiempo de ejecución simplemente cambiando el archivo de paquete durante la ejecución del programa. Hay otro efecto colateral importante, que seguramente es una de las razones principales para la existencia de los JfControls: en el caso del repositorio de imágenes, cada imagen se carga una sola vez y se usa muchas veces, siendo "amigable" con los recursos del sistema. CARACTERISTICAS DE LA LIBRERIA JFCONTROLS ========================================= * Gestión de paquetes para la configuración. La librería dispone de un sistema especial mediante el cual es posible almacenar todo tipo de configuraciones para los componentes. Estos almacenes pueden salvarse como ficheros aparte de la aplicación o en los ficheros DFM de Delphi que más tarde serán integrados en el propio ejecutable. A este sistema especial se lo denomina "paquete". Los "paquetes" cuando son guardados en ficheros independientes del ejecutable tienen la ventaja de poder ser seleccionados en tiempo de ejecución, por lo que a través de ellos una aplicación puede cambiar de idioma o de apariencia. * Multilenguaje en tiempo de ejecución y diseño. La tecnología de paquetes da la posibilidad de desarrollar aplica- ciones con soporte para varias lenguas. Fácilmente podrá construir aplicaciones que cambien en tiempo de ejecución o de diseño de uno a otro idioma. Vienen incorporados editores de propiedades especiales que facilitan la traducción. * Multiconfiguración en tiempo de ejecución y diseño para colores, fuentes, fondos e imágenes. Una aplicación realizada con esta librería de componentes tiene la capacidad de cambiar su apariencia tanto en tiempo de ejecución como de diseño. * Configuración centralizada de colores, fuentes, fondos, imágenes, cadenas, mensajes y textos. La tecnología de paquetes permite hacer conjuntos de elementos comunes. Basándose en ella se han creado paquetes para configuración de colores, fuentes, fondos e imágenes. De esta manera, un mismo color, fuente o imagen puede ser utilizado en diversos lugares de la aplicación, ventanas, controles, etc. * Gestión de transparencias en todos los componentes. Todos los controles de la librería JfControls, paneles, campos de edición, RadioGroups, listas de selección, casillas de verificación, etc., permiten hacer transparencias con el fondo del contenedor en el que se encuentren. * Paquete contenedor de imágenes que admite simultáneamente formatos gráficos de cualquier tipo. El paquete de imágenes es capaz de contener todo tipo de formatos gráficos que estén instalados o puedan estarlo en Delphi. La librería JfControls por defecto soporta los formatos ICO, BMP, WMF, JPG, no obstante, la instalación de otros paquetes freeware o shareware hará que soporte formatos adicionales, para lo cual se han dispuesto unas funciones especiales para efectuar su registro dentro de la librería. * Paquetes de variables para configuraciones manuales y automáticas de la aplicación. Hay un tipo especial de paquete que es capaz de guardar todo tipo de variables de una forma jerarquizada, al estilo del registro de configuración de Windows, mediante el cuál se guardan automáticamente las posiciones y tamaños de ventanas y de otros elementos signifi- cativos de las ventanas, al mismo tiempo sirve de contenedor para las variables que el usuario desee guardar dentro de él. Tiene como ventajas respecto al Registro de Windows que es independiente del sistema operativo, se maneja en memoria, es transparente al desarro- llador y es fácilmente transportable. * Configuración regional imbuida dentro de la librería. La configuración regional es independiente del sistema operativo, por lo que la aplicación, si así se desea, no está sujeta a los parámetros de Windows para formatos de fechas, horas, tiempo, números, etc., todo ello, por supuesto es guardado en los paquetes de lenguaje y es dependiente del idioma que se seleccione. * Etiquetas asociadas a todos los controles. Todos los controles de la librería JfControls disponen de una etiqueta asociada completamente personalizable y que se puede situar en cualquier posición respecto al control. * Múltiples procedimientos y funciones que simplifican el desarrollo de aplicaciones. La librería incluye numerosos procedimientos y funciones que facilitan el manejo de ventanas y de los paquetes de configuración de la librería. La librería JfControls ha sido desarrollada pensando en voluminosas aplicaciones repletas de ventanas, por lo que debemos utilizar la característica de autocreación de ventanas de Delphi solamente en muy contadas ocasiones y si no se utiliza mucho mejor, ya que la aplicación se ejecutará mucho más rápidamente y utilizará mucho mejor los recursos de Windows. Los procedimientos y funciones que se incluyen en la librería mejoran la gestión de la creación y destruc- ción de ventanas y alivian los recursos del sistema, permitiendo además cambios dinámicos de estilo. * Múltiples problemas tradicionales solucionados. La librería JfControls no se limita a incorporar un conjunto de componentes y controles para el desarrollo de aplicaciones, sino que se han pulido todos los problemas tradicionales que se producen durante la elaboración de un proyecto, se han solventado problemas relativos a gráficos, como la gestión de diversos formatos gráficos desde las bases de datos y su visualización en el componente TJfDBImage, posicionamiento de ventanas hijas MDI, automatización de los botones de la ventana padre MDI, gestión de fondos en ventanas principales MDI, colores y fondos en gestión de mensajes y en gestión de errores, corrección de parpadeos en el repintado de formas y controles, etc. * Nueva gestión de máscaras con multilocalización. La librería JfControls incorpora un nuevo sistema de máscaras, mucho más fácil de utilizar, ya que los controles tienen la posibilidad de conocer el tipo de dato que van a manejar a través de su propiedad DataType, pero la gestión de máscaras no se limita a un control local en el componente, sino que se puede basar en la definiciones que se hayan efectuado a nivel de toda la aplicación, a nivel de la ventana en la que se encuentra el control o localmente a él. Estas capacidades hacen que una aplicación tenga la posibilidad de cambiar a nivel global la definición de máscaras para todas las ventanas o para algunas en concreto, haciendo más fácil trabajar, por ejemplo, con varios períodos diferentes de fechas, con diferentes tipos de decimales para las monedas, etc., dentro de una misma aplicación. * Gestión de tipos de datos en todos los controles. Todos los controles disponen de una propiedad DataType que les indica el tipo de datos que van a manejar, lo que unido a su propiedad Value de tipo Variant, les da posibilidades de formateo y uso mucho más fáciles. Los tipos de datos que actualmente se soportan son: Integer, Currency, Double, DateTime, String, Memo y Binary. * Sistema especial de optimización de los recursos del sistema operativo. La librería JfControls ejecuta una serie de procedimientos especiales en la gestión de gráficos, con los que se consigue una liberación automática de los recursos del sistema, dando la posibilidad a las aplicaciones elaboradas con dicha librería de utilizar mucha mayor cantidad de gráficos sin agotar los recursos de la máquina y dando la posibilidad de realizar aplicaciones mucho más grandes. * 30 componentes completamente personalizables. Los componentes de la librería JfControls comparten las propiedades que les permiten utilizar las configuraciones definidas en los paquetes, por lo que pueden ser personalizados completamente, de esta manera pueden ser utilizados todos los fondos, colores, fuentes e imágenes del proyecto. Todos los componentes tienen propiedades comunes para definir el texto, la etiqueta asociada, las máscaras, la transparencia, la redimensión, el posicionamiento, los privilegios y la visibilidad. * 11 componentes vinculados a datos. Los componentes de manejo de datos de la librería tienen su contra- partida en controles vinculados a fuentes de datos que han sido implementados utilizando los enlaces de datos estándar de la VCL, por lo que son compatibles con cualquier dataset desarrollado a partir de la versión 3 de Delphi, de esta manera se pueden utilizar los componentes de datos orientados al BDE, los IBX para comunicación con INTERBASE o cualquier otro conjunto de componentes que deriven del dataset estándar de Delphi. * Barras de menú completamente personalizables. Las barras de menú han sido completamente reelaboradas para lograr un toque de distinción y funcionalidad que no podrá encontrar en las aplicaciones tradicionales, su capacidad y facilidad de configuración le darán la posibilidad de crear aplicaciones que se distingan muy fácilmente de las demás. Pueden haber varias barras de menú simultá- neamente en la misma ventana, con diferentes alineamientos y diferentes posibilidades de Scroll. Los menús de la librería JfControls permiten Banners a cualquier nivel, incluso en la propia barra principal de menú, pueden tener fondos degradados, imágenes y colores sólidos, los textos permiten ángulos, diferentes estilos y colores, los bordes de los submenús pueden ser definidos, imágenes en las diferentes opciones con capacidad de alineación. Las barras de menú controlan automáticamente los botones de las ventanas hijas MDI, los cuales pueden ser cambiados de forma, fondo, etc., también pueden ser definidos completamente los botones que sirven para efectuar el Scroll. La misma capacidad de configuración se ha implementado en los menús emergentes. * Más de 40 tipos de formas para etiquetas y botones. La mayoría de controles de la librería permiten tomar diferentes formas o incorporar en su interior distintas formas. De esta manera, por ejemplo, los componentes TJfSpeedButton, TJfLabel o TJfCheckBox pueden tomar más de 40 tipos de formas y el componente TJfListBox cuando trabaja en modo CheckBox permite que cada marca tenga una forma determinada. Las formas pueden tener fondos de imágenes, degradados o colores sólidos, y pueden incorporar líneas de diferentes tamaños, estilos y colores. * Formas embebidas. El componente TJfScrollBox tiene procedimientos especiales para manejar otras ventanas en su interior. Estos procedimientos automatizan la creación y destrucción de las formas y gestionan la localización de las ventanas dentro del componente. * Control para visualización de imágenes que resiste cualquier tipo de formato gráfico. El componente TJfImage ha sido transformado y mejorado, de tal manera que es capaz de manejar cualquier tipo de formato gráfico que pueda ser instalado en Delphi, incluso puede trabajar en modo de autode- tección de formatos. La versión de bases de datos de este componente tiene la misma funcionalidad y es capaz de salvar en el mismo campo de la tabla cualquier formato gráfico simultáneamente. * Componente TJfPageControl completamente reescrito con posibilidades de personalización exclusivas. El componente TJfPageControl permite una completa personalización de sus solapas, pueden tomar estilo de botones con diferentes formas y fondos, o un estilo más tradicional. Las solapas pueden estar situadas en cualquier posición, ser multilínea o en modo Scroll. * StatusBar completamente personalizable y con tipos de información predefinida. El componente TJfStatusBar es completamente personalizable, permite fondos y estilos para cada panel interno, tipos de letra, texto multilínea, información predefinida (Hora, Bloq.num, Num.Caps, etc.), barra de progreso embebida, imágenes y mucho más. * Componente TJfLabel con texto angulable, formas, imagenes y multilínea. El componente TJfLabel es un control de etiquetas altamente sofis- ticado, simultáneamente es capaz de manejar formas embebidas, etiquetas accesorias, texto con ángulo, texto multilínea, sombra, fondos con imágenes, degradados, marcas de agua, colores sólidos, brillos, imágenes anexas, privilegios, máscaras, tipos de datos e informaciones predefinidas. La mayoría de controles tienen asociada una etiqueta que puede ser situada en cualquier posición y que tiene una funcionalidad, casi tan completa, como el componente TJfLabel. * Campo de edición con botones incorporados. El componente TJfEdit es un campo de edición con transparencia, gestión de máscaras, tipo de dato a manejar, fondos con imágenes, degradados, colores sólidos, sombra, imagen adjunta, etiqueta asociada, 6 botones adjuntos (Calendario, calculadora, mantenimiento, relación, personalizable 1, personalizable 2) completamente configu- rables, privilegio de acceso centralizado, diferentes estilos para los bordes, propiedad especial para la ejecución del dato, cuando se pulsa la tecla ENTER, sin necesidad de paso de foco al siguiente control, capacidad de especificar un lapso de tiempo para que se produzca el evento OnChange (sincronizaciones con campos de la base de datos). * Barras de botones de gestión y navegación para los componentes de imágenes, textos y bases de datos. En la librería JfControls existen varias barras de botones que sirven para manejar las características de carga, grabación y borrado de los controles TJfMemo, TJfRichEdit y TJfImage, también existe la tradi- cional barra de navegación para la gestión de los registros de una fuente de datos. En todas ellas se pueden configurar la forma de los botones, el fondo y su visibilidad. Estas barras se enlazan con los controles a los que pueden hacer referencia. * Gestión ampliada de textos enriquecidos. El componente TJfRichEdit es un componente de edición multilínea de texto enriquecido que permite color sólido para el fondo, sombra, etiqueta asociada totalmente configurable, privilegio de acceso centralizado, inclusión de imágenes junto al texto, inserción de objetos OLE, cuadro de dialogo con un completo entorno de edición de texto enriquecido y modo de manejo tradicional. * Barras de progreso completamente personalizables. El componente TJfProgressBar es una barra de progreso que ha sido completamente reescrita y permite fondos con imágenes, degradados, colores sólidos, transparencias, sombras, etiqueta asociada, privilegio de acceso centralizado, orientación de la barra y del movimiento de los bloques, diferentes tamaños de bloque, fondos completamente configurables para los bloques y diferentes tipos de bordes. * Gestión de privilegios. El componente TJfCApplication permite una gestión centralizada de los privilegios de acceso y visualización de todos los controles de la librería. Se pueden crear definiciones de permisos y enganchar varios controles a cualquiera de ellos, de tal manera que el manejo de ese permiso produce automáticamente un cambio de estado en todos los controles que están vinculados a el. Uno de los usos más generalizados de la gestión de privilegios es la capacidad de dar unos permisos u otros al usuario que usa la aplicación, facilitando la labor de otorgar y quitar la visibilidad o el manejo de los controles. * Calculadora y calendario configurables incorporados. Se han incorporado una calculadora y un calendario personalizables que pueden ser ejecutados desde los botones de los campos de edición o ejecutables desde el componente TJfCApplication. El calendario permite redimensionarlo, de tal manera que pueden verse más o menos cantidad de meses. Ambos componentes se acoplan automáticamente a las caracte- rísticas de la configuración regional indicada en el componente TJfCApplication. * Gestión de formas irregulares. Se han añadido componentes para crear ventanas con formas basadas en imágenes. Las ventanas generadas de esta forma pueden tomar cualquier forma que pueda imaginar e impresionarán gratamente a sus usuarios. Las formas de las ventanas pueden ser cambiadas en tiempo de ejecución. Los botones pueden manejar diferentes imágenes y fondos para cada uno de sus estados. La detección del ratón puede ser realizada con regiones o en la totalidad del rectángulo del botón. Incluso el componente TJfBitBtn puede tomar formas irregulares. * Manual visual. La librería JfControls viene con un completo tutorial muy visual que le facilitará la adaptación al manejo de los componentes, al mismo que le introducirá paso a paso en el desarrollo de sus aplicaciones * Completa ayuda contextual desde Delphi. También viene con un completo fichero de ayuda contextual para que pueda ver la explicación de todas las clases, propiedades, métodos, eventos, procedimientos y funciones de la librería JfControls. * Compatibilidad. La librería es compatible con Delphi 3 hasta Delphi 5, y con Windows 95, 98, Me, NT y 2000. COSTO Y FORMAS DE PAGO ====================== La Librería JfControls con 3 meses de actualizaciones gratuitas cuesta 195,33 Euros / 32.500 Pesetas / $ 180.23 USD, y una suscripción anual de actualizaciones cuesta 126,21 Euros / 21.000 Pesetas / $ 116,45 USD. La Librería+fuentes están disponibles para la venta a un precio de 390,66 Euros / 65.000 Pesetas / $ 362,34 USD. Puede pagar con tarjeta de crédito (VISA, American Express, EuroCard, 4B, Virtual Cash y Banesto), ya sea en línea por el servidor seguro Banesto On-Line http://www.jfactivesoft.com/spsales.htm o llenando un formulario imprimible que debe luego enviar por FAX al +34 96 6655275. También puede pagar a través de una transferencia bancaria: Banesto Plaza de Crevillente, 3 - Elche (Alicante) - España Entidad 0030 Sucursal 3125 Cuenta: 33-0387300273. Incluya 16,83 Euros / 2.800 Pts. / $ 15,53 USD por gastos bancarios. Debe enviar el comprobante de la transferencia por FAX al +34 96 6655275. ________________________________________________________________________ 3. GESTION DE ERRORES EN DELPHI 5 (y II) La primera parte de este artículo fue publicada en el último número: http://www.latiumsoftware.com/es/pascal/0014.php GESTOR DE EXCEPCIONES PREDETERMINADO ==================================== Aparte de atrapar excepciones para mostrar el correspondiente mensaje de error, un bloque try..except..end permite a las aplicaciones realizar algún código de limpieza o finalización habitualmente usado para liberar recursos asignados (cerrar archivos, liberar memoria, etc.). Por ejemplo, en el siguiente procedimiento liberamos la lista de cadenas (StringList) ya sea que haya ocurrido una excepción o no: procedure TForm1.Button1Click(Sender: TObject); var StringList: TStringList; begin StringList := nil; try StringList := TStringList.Create; StringList.LoadFromFile('project1.dpe'); ShowMessage(StringList.Text); except on e: exception do HandleError(Sender, e); end; StringList.Free; // Código de finalización. end; Para procedimientos que no necesitan liberar recursos, por ejemplo como este: procedure TForm1.Button2Click(Sender: TObject); var i: integer; begin i := 0; ShowMessage(IntToStr(10 div i)); end; ...no necesita usar un bloque try, y aún así puede capturar excepciones. El truco se logra asignando un manejador de evento a la propiedad OnException del objeto Application. Para simplificar esta tarea para el programador, escribí un procedimiento HandleApplicationExceptions que asigna a OnException un procedimiento que muestra el correspondiente mensaje de error y lo guarda en el registro de errores. Para hacer uso de él, simplemente tiene que llamar a este procedimiento por ejemplo cuando su programa comienza: program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}, ErrHndlr in 'ErrHndlr.pas'; {$R *.RES} begin Application.Initialize; HandleApplicationExceptions; // Activar el manejador de errores Application.CreateForm(TForm1, Form1); Application.Run; end. Para aquellos que estén interesados en conocer los detalles de implemen- tación, en la unidad ErrHndlr he definido HandleApplicationExceptions como sigue: procedure HandleApplicationExceptions; begin Application.OnException := AppExceptionHandler.Handler; end; Simplemente asigna a la propiedad OnException del objeto Application la dirección del método AppExceptionHandler.Handler. Tuve que declarar una pequeña clase para proveer este procedimiento de tipo TExceptionEvent (el tipo de la propiedad OnException), declarado en la unidad Forms: type TExceptionEvent = procedure(Sender: TObject; E: Exception) of object; Declaré esta clase en la sección implementación de ErrHndlr como sigue: type TAppExceptionHandler = class(TObject) public procedure Handler(Sender: TObject; E: Exception); end; var AppExceptionHandler: TAppExceptionHandler; procedure TAppExceptionHandler.Handler(Sender: TObject; E: Exception); begin HandleError(Sender, E, nil); end; El método simplemente llama al procedimiento HandleError(Sender,E, Address) que presenté el número pasado. NOTA: No he creado una instancia de AppExceptionHandler porque no se necesita dado que nunca accedemos propiedades de este objeto, así que no es necesario que exista. Usando este manejador de evento, la línea agregada al registro de errores para el error producido en TForm1.Button2Click se vería como la siguiente Lun 08-Ene-2001 15:39:28 - EDivByZero: División entre cero [Form1] @ 44748F ERRORES API =========== Las funciones de la API de Windows no usan las excepciones de Delphi. En su lugar reportan condiciones de error en su valor de retorno. La mayoría de ellas devuelven 0 (o False) si no tuvieron éxito y puede obtener el código de error mediante una subsiguiente llamada a GetLastError (antes de llamar cualquier otra función API). Para obtener el mensaje correspondiente al error puede pasar este código a la función SysErrorMessage. Puede usar los errores para crear excepciones, tal como mostraré más abajo, pero también decidí escribir un par de versiones sobrecargadas de HandleError para manejar los errores API directamente: procedure HandleError(ApiError: integer); overload; var Address: Pointer; begin asm // Address := %DirecciónDeRetorno%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(nil, ApiError, Address); end; procedure HandleError(Sender: TObject; ApiError: integer); overload; var Address: Pointer; begin asm // Address := %DirecciónDeRetorno%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(Sender, ApiError, Address); end; Ambos son envoltorios que proveen el parámetro Address para el siguiente procedimiento: procedure HandleError(Sender: TObject; ApiError: Integer; Address: Pointer); overload; var ErrMessage: string; begin ErrMessage := SysErrorMessage(ApiError); ShowErrorBox(ErrMessage); SaveError(Sender, 'Error WinAPI #' + IntToStr(ApiError) + ': ' + ErrMessage, Address); end; Este procedimiento es similar al procedimiento HandleError(Sender,E, Address) que presentara en el boletín pasado. El siguiente es un ejemplo de uso de HandleError con errores API: procedure TForm1.Button3Click(Sender: TObject); var Handle: THandle; Buffer: array [0..511] of char; BytesRead: longword; begin Handle := CreateFile('project1.dpe', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if Handle = INVALID_HANDLE_VALUE then HandleError(Sender, GetLastError) else begin if not ReadFile(Handle, Buffer, 511, BytesRead, nil) then HandleError(Sender, GetLastError) else begin Buffer[BytesRead] := #0; ShowMessage(Buffer); end; CloseHandle(Handle); end; end; La línea agregada al registro de errores se vería así: Lun 08-Ene-2001 15:41:06 - Error WinAPI #2: El sistema no puede hallar el archivo especificado [Form1.Button3] @ 447442 ERRORES API COMO EXCEPCIONES ============================ Otra forma de gestionar los errores API es lanzando una excepción cuando ocurran de modo que podamos usar el mecanismo de manejo de excepciones que vimos antes. Por ejemplo: procedure TForm1.Button4Click(Sender: TObject); var Handle: THandle; Buffer: array [0..511] of char; BytesRead: longword; begin Handle := INVALID_HANDLE_VALUE; try Handle := CreateFile('project1.dpe', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if Handle = INVALID_HANDLE_VALUE then raise Exception.Create(SysErrorMessage(GetLastError)); if not ReadFile(Handle, Buffer, 511, BytesRead, nil) then raise Exception.Create(SysErrorMessage(GetLastError)); Buffer[BytesRead] := #0; ShowMessage(Buffer); except on e: Exception do HandleError(Sender, e); end; CloseHandle(Handle); end; La línea agregada al registro de errores se vería como sigue: Lun 08-Ene-2001 15:42:17 - Exception: El sistema no puede hallar el archivo especificado [Form1.Button4] @ 447442 EXCEPCIONES A MEDIDA ==================== Lanzar (o "levantar") excepciones de esta manera es un poquito caro en cuanto a tamaño de código. Podemos definir un procedimiento para simplificar las cosas un poco: procedure RaiseApiError(ApiError: integer); begin raise Exception.Create(SysErrorMessage(ApiError)) end; Y luego podríamos usarlo de esta forma: procedure TForm1.Button4Click(Sender: TObject); ... if Handle = INVALID_HANDLE_VALUE then RaiseApiError(GetLastError); if not ReadFile(Handle, Buffer, 511, BytesRead, nil) then RaiseApiError(GetLastError); ... end; El problema de convertir errores API en excepciones es que perdemos el dato del código de error porque la clase Exception no define un campo para el número de error. Por esta razón he definido una clase base para excepciones propias que provea dos campos más: uno para el número de error, y otro para la dirección del llamador: type ECustomError = class(Exception) public Number: Integer; Address: Pointer; constructor Create(const ErrMessage: string; Address: Pointer); overload; constructor Create(const ErrMessage: string; Number: Integer = 0); overload; constructor Create(const ErrMessage: string; Number: Integer; Address: Pointer); overload; end; procedure RaiseCustomError(const ErrMessage: string; Number: Integer = 0); implementation constructor ECustomError.Create(const ErrMessage: string; Address: Pointer); begin Create(ErrMessage, 0, Address); end; constructor ECustomError.Create(const ErrMessage: string; Number: Integer; Address: Pointer); begin inherited Create(ErrMessage); Self.Number := Number; Self.Address := Address; end; {W+} constructor ECustomError.Create(const ErrMessage: string; Number: Integer); var Address: Pointer; begin asm // Address := %DirecciónDeRetorno%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; Create(ErrMessage, Number, Address); end; procedure RaiseCustomError(const ErrMessage: string; Number: Integer); var Address: Pointer; begin asm // Address := %DirecciónDeRetorno%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; raise ECustomError.Create(ErrMessage, Number, Address); end; Podemos derivar una clase EApiError de ECustomError: type EApiError = class(ECustomError) public constructor Create(ApiError: Integer); overload; constructor Create(ApiError: Integer; Address: Pointer); overload; end; procedure RaiseApiError(ApiError: integer); implementation constructor EApiError.Create(ApiError: Integer; Address: Pointer); begin inherited Create(SysErrorMessage(ApiError), ApiError, Address); end; constructor EApiError.Create(ApiError: Integer); var Address: Pointer; begin asm // Address := %ReturnAddress%; mov edx, [ebp+4]; // EDX := (EBP+4)^; mov Address, edx; // Address := EDX; end; Create(ApiError, Address); end; {W+} procedure RaiseApiError(ApiError: integer); var Address: Pointer; begin asm // Address := %ReturnAddress%; mov edx, [ebp+4]; // EDX := (EBP+4)^; mov Address, edx; // Address := EDX; end; raise EApiError.Create(ApiError, Address); end; Ahora RaiseApiError lanza una excepción EApiError que incluye el código de error y la dirección donde la excepción fue lanzada, de modo que tendremos que hacer un pequeño ajuste a nuestro viejo procedimiento HandleError(Sender,E,Address): procedure HandleError(Sender: TObject; E: Exception; Address: Pointer); overload; begin ShowErrorBox(E.Message); if E is ECustomError then with ECustomError(E) do SaveError(Sender, E.ClassName + ' #' + IntToStr(Number) + ': ' + Message, Address) else SaveError(Sender, E.ClassName + ': ' + E.Message, Address); end; Ahora la línea agregada al registro de errores cuando ocurra un error API se varía así: Lun 08-Ene-2001 15:42:17 - EApiError #2: El sistema no puede hallar el archivo especificado [Form1.Button4] @ 445C31 OTROS ERRORES ============= Hay otros errores que pueden ocurrir en una aplicación y que uno querría guardar en un registro de errores. Principalmente éstos son errores internos, errores de programación, errores lógicos, o como quiera llamarlos. Puede usar RaiseCustomError para lanzarlos como una excepción, como por ejemplo: procedure TForm1.Button5Click(Sender: TObject); begin if (GetTickCount And 1) <> 0 then RaiseCustomError('Ocurrió una condición de error'); end; Puesto que no he usado un bloque try, la excepción será capturada por el evento OnException del objeto Application, y la línea agregada al registro de errores podría verse como esta: Lun 08-Ene-2001 15:42:17 - ECustomError #0: Ocurrió una condición de error [Form1] @ 445EE1 Como en el caso de los errores API, también escribí un par de procedi- mientos que manejar esos errores sin hacer uso de excepciones: procedure HandleError(const ErrMessage: string); overload; var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(nil, ErrMessage, Address); end; procedure HandleError(Sender: TObject; const ErrMessage: string); overload; var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(Sender, ErrMessage, Address); end; Ambos obtienen el parámetro Address para llamar a HandleError: procedure HandleError(Sender: TObject; const ErrMessage: string; Address: Pointer); overload; begin ShowErrorBox(ErrMessage); SaveError(Sender, ErrMessage, Address); end; Puede usar esos procedimientos como se muestra aquí: procedure TForm1.Button5Click(Sender: TObject); begin ShowMessage('Initialization code'); if (GetTickCount And 1) <> 0 then HandleError(Sender, Ocurrió una condición de error'); ShowMessage('Finalization code'); end; La línea agregada al registro de errores se vería así: Mon 08-Jan-2001 15:42:17 - Ocurrió una condición de error [Form1.Button5] @ 445EE1 - - - - - - - - - - - - - - - - - - - - - Espero que hayan encontrado este artículo útil. Si tienen dudas o preguntas pueden suscribirse a nuestra lista de correo de bajo tráfico gratuita. Para ello, simplemente envíen un email en blanco a: delphi-intermedio-subscribe@gruposyahoo.com Pueden obtener más información aquí: http://www.latiumsoftware.com/es/forums.html El código fuente completo del ejemplo final puede descargarse de esta dirección: http://www.latiumsoftware.com/descarga/p0015.zip ________________________________________________________________________ 4. WINDOWSE - ANALIZADOR AVANZADO DE VENTANAS! GREATIS SOFTWARE ================ Greatis Software es una compañía en rápido crecimiento ubicada en Yaroslavl, Rusia, que se especializa en desarrollo de software de sistema y en software especial para desarrolladores. Su página web es http://www.greatis.com y allí podrá encontrar muchas aplicaciones y componentes, algunos de ellos con código fuente. WINDOWSE ======== Una de las aplicaciones que podrá encontrar allí es WinDowse, una herramienta extremadamente conveniente para obtener información técnica necesaria acerca de cualquier ventana (recuérdese que un control es en sí mismo una ventana). Cuando coloque el puntero del ratón sobre una ventana, WinDowse le mostrará todos los parámetros de la ventana, la clase de la ventana, ventanas madres e hijas, incluyendo: * Ficha "Window" (Ventana) - Texto o etiqueta - ID de proceso - Nombre del archivo Exe - Instancia de la aplicación - Manejador (handle) de la ventana - Manejador (handle) de la ventana madre - Dirección de la función de la ventana - Manejador (handle) del menú de la ventana - Coordenadas respecto de la ventana madre - Coordenadas respecto de la pantalla - Tamaño de la ventana - Tamaño del área cliente de la ventana - Estilo y estilo extendido de la ventana * Ficha "Class" (Clase) - Nombre de la clase - Dirección de la función de la clase - Manejadores (handles) de los iconos grande y pequeño - Manejador (handle) del puntero del ratón - Manejador (handle) de la brocha del fondo - Manejador (handle) del módulo - Estilo de la clase. * Ficha "Parents" (Madres) - Lista de la jerarquía de ventanas madres * Ficha "Children" (Hijas) - Lista de las ventanas hijas de la ventana actual * Ficha "Graphics" (Gráficos) - Posición absoluta (relativa a la pantalla) del cursor - Posición del cursor relativa a las coordenadas de la ventana - Posición del cursor relativa a las coordenadas del área cliente de la ventana - Color del píxel bajo el puntero del ratón - Herramientas captura-pantalla: mostrar, zoom, copiar, guardar Todos los parámetros pueden verse en formato hexadecimal, decimal o binario. Al activarse, WinDowse muestra una lectura continua a medida que el cursor se mueve por la pantalla - alternando entre ventanas separadas o anidadas. En cualquier momento la lectura continua se puede congelar con un clic del ratón, y los datos de esa ventana se pueden estudiar en detalle. WinDowse también permite que los resultados sean copiados directamente en el portapapeles. Cada campo del análisis se provee con ayuda contextual explicando que es. LICENCIA ======== WinDowse 4.1 es GRATIS. El código fuente completo para Delphi cuesta $19.95(US). Para ordenar, visite http://www.greatis.com/windowsebuy.htm Por más información, contacte Greatis Software: b-team@greatis.com (enviar los mensajes en inglés). ________________________________________________________________________ 5. UN DBGRID CON CASILLAS DE VERIFICACION En la última edición prometí mostrar como colocar casillas de verifica- ción para los campos lógicos de un DBGrid. Aunque el código de este artículo es utilizable, por supuesto que sería mejor usar algún compo- nente profesional como uno de los que seguramente podrá encontrar en la red. El propósito de este artículo es simplemente presentar un ejemplo de uso de algunas propiedades de los objetos TDBGrid, TColumn y TField. De lo primero que debemos ocuparnos es de mostrar la casilla de verifi- cación. Para ese propósito debemos atrapar el evento OnDrawColumnCell del DBGrid: procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin ChkDBGridDrawColumnCell(Sender as TDBGrid, Rect, DataCol, Column, State); end; ChkDBGridDrawColumnCell es un procedimiento que he declarado en una unidad separada: procedure ChkDBGridDrawColumnCell(DBGrid: TDBGrid; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var X, Y, Index: integer; Field: TField; begin Field := Column.Field; if (Field <> nil) and (Field.DataType = ftBoolean) then begin // Llenar la celda con el color de fondo DBGrid.Canvas.FillRect(Rect); // Determina la posición del gráfico dentro de la celda case Column.Alignment of taRightJustify: X := Rect.Right - 2 - 16; taCenter: X := (Rect.Right - Rect.Left - 16) div 2 + Rect.Left; else // taLeftJustify: X := Rect.Left + 2; end; Y := (Rect.Bottom - Rect.Top - 16) div 2 + Rect.Top; // Determina el gráfico a dibujar if Field.AsBoolean then Index := 1 else Index := 0; // Lo dibuja Form1.ImageList1.Draw(DBGrid.Canvas, X, Y, Index); end else // Dibujo predeterminado DBGrid.DefaultDrawColumnCell(Rect, DataCol, Column, State); end; Lo que este procedimiento hace es primero comprobar si la celda corres- ponde a un campo lógico y en tal caso dibuja una silla "chequeada" o "no chequeada" usando la imagen correspondiente tomada de una lista de imágenes. El código es similar al que presentara en el último numero en el artículo DIBUJANDO CELDAS EN UN DBGRID. Para otras columnas simplemente llamamos a DefaultDrawColumnCell. Ahora debemos enfrentar un problema: cada vez que el usuario presione una tecla ASCII o haga clic en una celda seleccionada, o presione F2, la grilla entrará en modo de edición. Esto es así porque normalmente la grilla tiene la opción dgEditing activada (vea la propiedad Options). Para anular este comportamiento debemos apagar la opción dgEditing cuando el usuario selecciones una celda que corresponda a un campo lógico, y luego volver a encenderla cuando el usuario abandone esa celda. A tal fin tenemos que capturar los eventos OnColEnter y OnColExit: procedure TForm1.DBGrid1ColEnter(Sender: TObject); begin ChkDBGridColEnter(Sender as TDBGrid); end; procedure TForm1.DBGrid1ColExit(Sender: TObject); begin ChkDBGridColExit(Sender as TDBGrid); end; Nuevamente llamé a procedimientos definidos en una unidad separada: procedure ChkDBGridColEnter(DBGrid: TDBGrid); var Field: TField; begin Field := DBGrid.SelectedField; if (Field <> nil) and (Field.DataType = ftBoolean) then DBGrid.Options := DBGrid.Options - [dgEditing]; end; procedure ChkDBGridColExit(DBGrid: TDBGrid); var Field: TField; begin Field := DBGrid.SelectedField; if (Field <> nil) and (Field.DataType = ftBoolean) then DBGrid.Options := DBGrid.Options + [dgEditing]; end; Sólo tiene que usar esos procedimientos en su DBGrid si usa la opción dgEditing. Finalmente, se debe proveer una forma para que el usuario cambie el valor del campo, por ejemplo haciendo clic en la celda con el ratón o presionando la barra espaciadora. Podemos hacer eso atrapando los eventos OnCellClick y OnKeyPress: procedure TForm1.DBGrid1CellClick(Column: TColumn); begin ChkDBGridCellClick(Column); end; procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); begin ChkDBGridKeyPress(Sender as TDBGrid, Key); end; Los procedimientos se definen como sigue: procedure ChkDBGridCellClick(Column: TColumn); var Field: TField; begin Field := Column.Field; if (Field <> nil) and (Field.DataType = ftBoolean) and Field.CanModify and not Column.ReadOnly then with Field.Dataset do begin if State = dsBrowse then Edit; Field.AsBoolean := not Field.AsBoolean; end; end; procedure ChkDBGridKeyPress(DBGrid: TDBGrid; var Key: Char); var Field: TField; begin Field := DBGrid.SelectedField; if (Field <> nil) and (Field.DataType = ftBoolean) then if (Key = ' ') and Field.CanModify and not DBGrid.Columns[DBGrid.SelectedIndex].ReadOnly then with Field.Dataset do begin if State = dsBrowse then Edit; Field.AsBoolean := not Field.AsBoolean; end; end; ________________________________________________________________________ 6. ¡BORLAND ACABA DE ANUNCIAR LA DISPONIBILIDAD DE KYLIX! Kylix vendrá en tres versiones: * Kylix Desktop Developer (para desarrollos GUI en Linux) U$S 999. * Kylix Server Developer (para desarrollos web) U$S 1,999. * Open Edition (para código abierto y software libre (GPL)). LAs primeras dos estarán generalmente disponibles antes de finales del primer trimestre de 2001 (Borland ha anunciado envíos para el 22 de Febrero) y están disponible para comprar ahora mismo en el sitio de Borland: http://shop.borland.com/Category/0,1257,3-15-1080,00.html La versión Open Edition estará disponible a mediados de este año y será de descarga gratuita (o a la venta por $99 con documentación impresa y CD). ¿Cuál es la diferencia entre Kylix Desktop Developer y Kylix Server Developer? Básicamente, - La edición Server vendrá con NetCLX (componentes para Internet, incluyendo soporte para Apache) que no estarán disponibles en la edición Desktop. - La edición Server soportará bases de datos IBM DB2, Oracle 8i, InterBase y MySQL, mientras que la edición Desktop sólo soportará las dos últimas. ¿Qué tienen en común? - Ambas ediciones incluirán el código fuente de CLX - Requerimientos del sistema: * Intel Pentium 200 MHz (P2 400 MHz recomendado) * 64 MB RAM (128 MB recomendado) * Unidad CD-ROM * 175 MB de espacio en disco, o 155 MB para instalación completa * Monitor VGA o de mayor resolución * Ratón u otro dispositivo puntero - Distribuciones Linux soportadas: * Red Hat 6.2 o superior * Mandrake 7.2 o superior * SuSE 7.0 o superior Más información: http://www.borland.com/kylix/ ________________________________________________________________________ 7. ENLACES * Grupo Albor http://www.grupoalbor.com/ * La Torre de Babel http://www.fcharte.com ________________________________________________________________________ ¡TÚ PUEDES AYUDARNOS! Necesitamos tu ayuda para que este boletín pueda continuar y crecer. Una forma en que puedes ayudarnos es enviando este enlace a tus amigos: http://www.latiumsoftware.com/es/pascal/index.php Otra forma es votándonos en alguno de estos rankings para darle más visibilidad a nuestro sitio web y aumentar así el número de suscrip- ciones al boletín, que esperamos en el futuro se traduzca también en un mayor número de colaboraciones de artículos, trucos, etc.: http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium http://news.optimax.com/topdelphi/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://www.top219.org/cgi-bin/vote.cgi?delphi&83 http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80 http://www.programacion.net/votar-enlace.php?id=474 http://www.lawebdelprogramador.com/buscar/enlace.php?id=615 Por favor vota. Son sólo unos segundos para ti que REALMENTE pueden hacer la diferencia. Necesitamos tu ayuda para poder continuar. ________________________________________________________________________ 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/p0015.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) 2001 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!






