Boletín Pascal #26
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
![]() |
Boletín Pascal #26 - 05-SEP-2001 INDICE 1. UNAS PALABRAS DEL EDITOR - Se necesita ayuda 2. DELPHI ADENTRO (y II) - La historia de una instancia que aún no era instancia - Mecanismo de llamada a los métodos estáticos - Mecanismo de llamada a los métodos virtuales - Unas palabras finales - Referencias bibliográficas - Glosario 3. CONSTRUYENDO UN OBJETO DE NEGOCIO 4. PREGUNTAS FRECUENTES SOBRE DELPHI 6 PERSONAL EDITION - ¿Por qué la llamaron "Personal"? - ¿A quiénes está dirigida? - ¿Es gratis? - ¿Si es gratis, por qué Borland la vende? - ¿Es código abierto ("open source")? - ¿Tengo que distribuir mis aplicaciones bajo la GNU GPL? - ¿Qué componentes vienen con la Edición Personal? - ¿Hay una Edición Estándar de Delphi 6? - ¿Cómo instalo Delphi 6 Personal? 5. COMPONENTES VCL - Componente SpeedParser - Analizador Rápido de Expresiones - Imagen PCX con soporte de paleta 6. TRUCOS Y CONSEJOS - Haciendo una aplicación cliente TCP/IP - Compartiendo mi experiencia... · Conectándose a un servidor TCP/IP desde un cliente Delphi · La solución que encontré es... - Alineando texto en un StringGrid - Evitando que el usuario ejecute otras aplicaciones 7. DELPHI EN LA RED ________________________________________________________________________ 1. UNAS PALABRAS DEL EDITOR En esta edición me complace presentar la segunda (y última) parte del artículo Delphi Adentro de Víctor Lorenzo Prado. También me alegra darle la bienvenida a nuevos autores en este boletín, como Max Kleiner, Mattias Andersson, Tommy Andersen y S.S.B. Magesh Puvananthiran. También me complace anunciar que el foro delphi-intermedio (nuestro foro para programadores en Delphi) ha superado los 300 miembros y ha alcanzado un promedio de catorce mensajes diarios. Lo invito a que vea los últimos mensajes y compruebe que sus miembros hacen un gran esfuerzo por no dejar preguntas sin contestar: http://espanol.groups.yahoo.com/group/delphi-intermedio/messages Para unirse al foro, puede hacerlo desde la web: http://espanol.groups.yahoo.com/group/delphi-intermedio/join O por email: delphi-intermedio-subscribe@gruposyahoo.com Puede configurar su suscripción al foro para convertir o no los mensajes a formato HTML, o para no recibir los mensajes en su email (podrá ver los mensajes en la web). Se necesita ayuda ================= Por más de un año, cada edición del boletín ha sido el trabajo de un solo individuo. Desde la edición pasada, estoy agradecido de contar con la colaboración de Matthew J. Brock, quien desinteresadamente ha hecho una revisión del texto de la edición en inglés. Esta ha sido una buena experiencia, y ahora quisiera hacer un llamado para otras posiciones: Compatibilidad de versiones de Delphi: Este sería un equipo de programa- dores Delphi que usen diferentes versiones del compilador. Su trabajo sería probar los ejemplos de código de los artículos para determinar si funcionan en su versión, y si no, portarlos si es posible. Traductores inglés-español: Su trabajo sería traducir el texto de los artículos originalmente escritos en inglés. Se trataría que la traduc- ción se realice a un español latinoamericano neutro. Traductores español-inglés: idem ant., pero del español al inglés para la edición en inglés. Editor: Sí, ése es mi puesto, no está vacante todavía, pero quizás lo esté en un futuro cercano. El trabajo del editor es manejar parte de las relaciones con los autores, juntar las piezas y escribir la nota del editor. Se requiere gran dominio del inglés (lecto-escritura). Aunque estos trabajos quizás no signifiquen mucho más que un par de horas a la semana, el requisito para todas las posiciones es un nivel más o menos alto de disponibilidad (como media hora por día o más) porque, como usted podrá imaginarse, el boletín se hace en "cadenas" de pequeñas partes... Esto es todo trabajo voluntario, no existe ninguna remuneración por él excepto por la satisfacción de ayudar divulgar el conocimiento sobre Delphi y figurar en los créditos como parte del staff del boletín. Si está interesado, por favor no dude en ponerse en contacto conmigo y no se olvide de mencionar la/s posiciones a las que aspira y por qué desea ocuparlas. 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. DELPHI ADENTRO (y II) Por Víctor Lorenzo Prado <vlorz@yahoo.es> La historia de una instancia que aún no era instancia ===================================================== En la sección anterior se introducían algunos conceptos básicos sobre teoría de la programación, de gran utilidad para el desarrollador de software profesional. Se planteaba como una necesidad el abstraerse de la estructura interna de las clases, de su implementación. En esta sección, en cambio, se propone el "Abstraernos un poco de las Abstrac- ciones" para comprender un poco más sobre la operación de las clases en Delphi. Comencemos por analizar las causas que originan un error muy común cuando se programa en Delphi (la realización de llamadas a métodos de una instancia, o el acceso a sus propiedades, cuando esta aún no ha sido inicializada) y que nos ayudará a comprender un poco sobre las interio- ridades de su compilador. Tomemos como caso de estudio la declaración e implementación de la clase "TSampleClass" que se brinda en el Listado 1: Listado 1. Clase TSampleClass. ------------------------------ Interface Type TSampleClass = Class Protected { Declaraciones Protegidas } { Campos Privados (Datos) } FProperty1 : integer; FProperty2 : integer; { Métodos de acceso a propiedades } procedure SetProperty2(const Value: integer); function GetProperty2 : integer; Public { Declaraciones Públicas } { Métodos Públicos } function VirtualFunction1 : integer; virtual; function StaticFunction1 : integer; { Propiedades Públicas } Property Prop1 : integer read FProperty1 write FProperty1; Property Prop2 : integer read GetProp2 write SetProp2; End; { TSampleClass } Implementation { TSampleClass } procedure TSampleClass.SetProperty2(const Value: integer); begin { Asigna el valor a la propiedad } FProperty2 := Value; end; function TSampleClass.GetProperty2: integer; begin { Devuelve el valor de la propiedad } Result := FProperty2; end; function TSampleClass.StaticFunction1: integer; begin { Solo devuelve un 0 } Result := 0; end; function TSampleClass.VirtualFunction1: integer; begin { Solo devuelve un 0 } Result := 0; end; (Nota: Por simplicidad ha sido eliminado el resto del código que conforma la Unit) (-------- Fin del Listado 1 --------) Analizando el listado vemos que se está declarando una clase que posee dos propiedades, "Prop1" y "Prop2", ambas de tipo "Entero Con Signo de 32 bits" (integer): { Propiedades Públicas } Property Prop1 : integer read FProperty1 write FProperty1; Property Prop2 : integer read GetProp2 write SetProp2; Puede observarse que el acceso al contenido de la propiedad "Prop1" se realiza directamente mediante lecturas y escrituras en el campo "FProperty1"; y a la propiedad "Prop2", mediante los Métodos de Acceso a Propiedad "GetProp2" (lectura) y "SetProp2" (escritura). Además de estas dos propiedades, la clase también exporta dos métodos: { Métodos Públicos } function VirtualFunction1 : integer; virtual; function StaticFunction1 : integer; En su implementación ambos métodos realizan la misma función, devolver como resultado un valor nulo (cero). Sin embargo algo curioso ocurre con ellos, no se comportan de igual manera cuando son llamados para una instancia que no ha sido aún "creada" (instanciada, se suele decir). Veamos el experimento que se muestra en el código de programa del Listado 2: Listado 2. Llamadas a los Métodos sin Instanciar la Clase. ---------------------------------------------------------- function TDelphiAdentro_II.TestMethodsCalls_1 : integer; var SampleClass : TSampleClass; i : integer; begin { Note que se realizan la llamadas sin inicializar la instancia } i := SampleClass.StaticFunction1 + 5; Result := i + SampleClass.VirtualFunction1; end; (-------- Fin del Listado 2 --------) Dando una mirada al código debe esperarse que esta función devuelva como resultado el valor entero 5 ("SampleClass.StaticFunction1" debe retornar 0, que sumado con 5 da como resultado 5, que sumado a su vez al resultado de "SampleClass.VirtualFunction1", también cero, debe entonces resultar en un valor 5). ¡¡Pero no ocurre así!! Si esta función es ejecutada paso a paso podrá apreciarse que, en efecto, "SampleClass.StaticFunction1" devuelve 0, ¡¡Pero la función "SampleClass.VirtualFunction1" No!! En lugar de devolver un valor 0 esta función provoca un Error Fatal de Violación de Acceso: Access violation at address 00402DD4 in module 'T4C.exe'. Read of address C08BC300. ¿Por qué ocurre esto si las dos funciones son "exactamente" "iguales"? Ah..., porque simplemente no lo son, el método "VirtualFunction1" es declarado virtual, mientras que el método "StaticFunction1" es estático. Y esto hace que los mecanismos implementados por Delphi para tener acceso a ellos (mecanismos de llamada, uso de la instrucción CALL del microprocesador) sean totalmente distintos. Mecanismo de llamada a los métodos estáticos ============================================ Comencemos por ver el funcionamiento de las llamadas a métodos estáticos a partir de observar qué sucede al compilar el fragmento de código dado en el Listado 3. Listado 3. Llamadas al Mismo Método en Distintas Instancias. ------------------------------------------------------------ procedure TDelphiAdentro_II.TestMethodsCalls_2; var { Declaración de Variables Locales } SampleClass1 : TSampleClass; SampleClass2 : TSampleClass; i : integer; begin try { Creación de las Instancias de la Clase } SampleClass1 := TSampleClass.Create; SampleClass2 := TSampleClass.Create; { Llamadas a los Métodos } i := SampleClass1.GetProperty2; i := SampleClass2.GetProperty2; Result := i; finally { Destrucción de las Instancias } SampleClass1.Free; SampleClass2.Free; end; end; (-------- Fin del Listado 3 --------) Obsérvense las secciones que posee este fragmento de código: una primera de declaración de las Variables Locales (dos para las instancias de la clase TSampleClass y un entero); una segunda donde son creadas las instancias de la clase (sobre este particular se tratará un poco más adelante); una tercera donde es invocado, para dos instancias distintas de la misma clase, el método "GetProperty2"; y una cuarta de liberación de la memoria ocupada por las instancias. Lo interesante de este código está en observar la implementación del método "GetProperty2" de la clase "TSampleClass", dado en el Listado 1, y la forma utilizada en el Listado 3 para invocarlo. Si aparentemente no se le está pasando ningún parámetro al método ¿Cómo puede entonces "saber" el código de programa dónde están los datos con que debe operar? Partiendo de lo planteado en la primera parte sobre la encapsulación en un mismo ente de los datos y los métodos de procesarlos, pudiera alguien pensar en que para cada instancia que se crea de la clase, junto con el espacio de los datos, se incluye otro para copiar el código que los debe procesar. Pero de hacerlo así sería un craso error, un total desperdicio de recursos. Momentáneamente pudiera parecer un "enigma", pero la solución utilizada para resolver este problema técnico es en extremo sencilla: Enviar como primer parámetro un apuntador a la zona que contiene los datos de la instancia. ¿Que este parámetro no aparece declarado en la interfaz del método? Es cierto, no aparece, precisamente ahí se está aplicando el paradigma de la abstracción sobre la implementación de los mecanismos que hacen operar a las clases. Pero ese mecanismo no queda del todo oculto para el programador, no aparece en la declaración de la interfaz (¡Genial, menos código que escribir!) pero sí se puede tener acceso a él mediante el parámetro "Self". El parámetro "Self" no es otra cosa que un apuntador a la instancia de la clase. Veamos cómo funciona esto a nivel del código ensamblador que genera el compilador para la sección donde son invocados los métodos: { Sentencia } i := SampleClass1.GetProperty2; { Código Generado } MOV EAX,[EBP-$04] CALL TSampleClass.GetProperty2 MOV [EBP-$0C],EAX { Sentencia } i := SampleClass2.GetProperty2; { Código Generado } MOV EAX,[EBP-$08] CALL TSampleClass.GetProperty2 MOV [EBP-$0C],EAX El código es autoexplicativo, aún para los no habituados al lenguaje ensamblador de 32 bits. Hagámosle una disección: * Sentencia que escribimos en Delphi: i := SampleClass1.GetProperty2; * Preparación del primer parámetro con la dirección donde se encuentran los datos de la instancia: MOV EAX,[EBP-$04] * Llamada al código de programa correspondiente al método: CALL TSampleClass.GetProperty2 * Almacenamiento del resultado devuelto: MOV [EBP-$0C],EAX Dado que las variables que se han declarado son locales a un procedimiento, el espacio para ellas es reservado dentro del STACK, de aquí que EBP-$04 contenga el apuntador (dirección) a los datos de la instancia "SampleClass1"; EBP-$04, a los de "SampleClass2"; y EBP-$0C, el valor de "i". Comenzamos primeramente por un método estático que no toma parámetros de entrada por su simplicidad. Pero aún si el método tomara parámetros el mecanismo sería igual, el primer parámetro sería un apuntador a los datos de la instancia y luego el resto de los parámetros. Visto esto, ya se nos presenta a la vista la respuesta del porqué cuando se llamaba al método "StaticFunction1" en el experimento del Listado 2 no ocurría ningún error. ¿Cierto? No había lugar a errores porque no se hacía acceso a ningún campo de la instancia, pero si se hubiera tenido acceso a alguno de sus campos sí hubiera habido errores. Pruebe, por ejemplo, a ejecutar paso a paso la siguiente línea de código observando las instrucciones en lenguaje ensamblador generadas por el compilador: i := TSampleClass(nil).GetProperty2; En este caso, el método "GetProperty2" provocará un error de violación de acceso al tratar de tener acceso al campo "FProperty2". Nótese ahora un detalle importante en el código generado por el compilador para la llamada a un método estático, la instrucción de llamada utilizada es de tipo directo: CALL TSampleClass.GetProperty2 "TSampleClass.GetProperty2" representa la dirección donde se encuentra el código que implementa el método "GetProperty2". Esto es posible porque, para los métodos estáticos, conociéndose la clase a la que pertenece la instancia ya se conoce exactamente la dirección donde radica el código que los implementa. *** PARA LOS MÉTODOS ESTÁTICOS, EL TIPO (CLASE) DECLARADO PARA LA VARIABLE QUE CONTENDRÁ LA INSTANCIA ES QUIEN DETERMINA CUALES SERÁN LOS MÉTODOS LLAMADOS Y NO EL TIPO DE LA CLASE QUE HA SIDO INSTANCIADA *** Mecanismo de llamada a los métodos virtuales ============================================ Antes de explicar el mecanismo de llamada a los métodos virtuales de una instancia es válido recordar un concepto, la POO no es únicamente ENCAPSULACIÓN de datos y métodos de procesamiento en un mismo ente, también es POLIMORFISMO, y los métodos virtuales son precisamente los que permiten su existencia. Mediante el empleo de métodos virtuales podemos crear clases con iguales interfaces y comportamientos distintos, donde el comportamiento de la instancia de la clase sea determinado por la clase que ha sido instanciada y no por el tipo declarado para la variable a la que se asigna la instancia [1]. El mecanismo utilizado por Delphi para implementar las llamadas a los métodos virtuales utiliza el mismo modo de pasar la información sobre la instancia a la cual se invoca el método. Pero difiere en la forma de realizar la llamada. Dado que el método a llamar depende de la clase que ha sido instanciada (posiblemente desconocida en tiempo de compilación) y no del tipo declarado para la variable que almacena la instancia, el método de llamada no puede ser de forma directa. La solución encontrada para este problema es también relativamente simple: El uso de una Tabla de Métodos Virtuales (VMT), una tabla donde son almacenadas las direcciones de los métodos virtuales asociados a la clase de la cual se ha creado la instancia. ¿Es necesario que cada instancia posea su propia copia de la tabla de métodos virtuales? No, basta con que exista solo una tabla de métodos virtuales asociada a la clase y que la instancia "conozca" dónde localizarla. Y lo más simple es, al crear la instancia, colocar la dirección de la tabla de métodos virtuales en forma de puntero en uno de sus campos. Tomemos nuevamente como material de estudio un fragmento de código, ahora el dado en el Listado 4: Listado 4. Llamadas al Mismo Método en Distintas Instancias. ------------------------------------------------------------ function TDelphiAdentro_II.TestMethodsCalls_3 : integer; var { Declaración de una Variable Local } SampleClass1 : TSampleClass; begin try { Creación de una Instancia de la Clase } SampleClass1 := TSampleClass.Create; { Llamadas al Método de una instancia inicializada } Result := SampleClass1.VirtualFunction1; finally { Destrucción de la Instancia creada } SampleClass1.Free; end; end; (-------- Fin del Listado 4 --------) Veamos entonces el código generado por Delphi para tener acceso a un método virtual: { Sentencia } Result := SampleClass1.VirtualFunction1; { Código Generado } MOV EAX,[EBP-$08] MOV EDX,[EAX] CALL DWORD PTR [EDX] MOV [EBP-$04],EAX Nuevamente el código es autexplicativo, pero hagámosle también una disección: * Sentencia que escribimos en Delphi : Result := SampleClass1.VirtualFunction1; * Preparación del primer parámetro con la dirección donde se encuentran los datos de la instancia: MOV EAX,[EBP-$08] * Obtener la dirección de la tabla de métodos virtuales: (puntero almacenado en el primer campo de datos de la instancia) MOV EDX,[EAX] * Llamada al código de programa correspondiente al método: (Primera entrada dentro de la tabla de métodos virtuales) CALL DWORD PTR [EDX] * Guardar el resultado en una variable temporal en el STACK: MOV [EBP-$04],EAX Y ahora, una vez visto este código se puede llegar a otra conclusión importante: *** PARA INVOCAR UN MÉTODO VIRTUAL DE UNA INSTANCIA ES NECESARIO TENER ACCESO A UNO DE SUS CAMPOS, LA VMT, Y POR LO TANTO NO SE PUEDEN INVOCAR MÉTODOS VIRTUALES DE UNA INSTANCIA SI ESTA AÚN NO HA SIDO CREADA *** Esta es la causa del error que se nos presenta cuando ejecutamos el código del experimento del Listado 2. Para conocer más detalles sobre el funcionamiento de la tabla de métodos virtuales y su estructura puede consultarse el material que Inprise brinda en [2]. Unas palabras finales ===================== ¿Se les ocurre ya cómo crear métodos que "generalmente" no "exploten"? Imagino que sí, pero les daré una pista... ¿Han visto que el método "Free" de los descendientes de "TObject" casi "nunca" explota? Échenle un vistazo a su código, que es tan interesante como sencillo. Moraleja de la Historia: ¿Cuál es la diferencia entre una Instancia de una Clase y un Balón de Football? Que podemos imaginarnos que tenemos un balón rodando frente a nosotros y pretender que le pateamos y damos un gol, y hasta escuchar la algarabía de los hinchas. No importa lo fuerte que le pateemos nadie saldrá herido ni ninguna ventana rota. Podremos simular que jugamos con él si queremos. Pero en el caso de la instancia no podemos jugar demasiado con la imaginación, porque si no existe sí podremos "romper alguna ventana de cristal" y en lugar de aplausos recibir como pago un bien desagradable cartel de Windows diciendo "Access Violation...bla bla bla". Referencias bibliográficas ========================== [1] - Inprise Corporation, "Virtual and Dynamic Methods", Object Pascal Reference (incluido dentro de las ayudas en línea), 1999. [2] - Inprise Corporation, "Virtual Method Table", Object Pascal Reference (incluido dentro de las ayudas en línea), 1999. Glosario ======== CALL - Instrucción del microprocesador. Llamada a Subrutina. Permite ejecutar bloques de código que realizan funciones que son comunes a varias partes del programa sin tener que repetirlos. ERROR FATAL DE VIOLACIÓN DE ACCESO - Error que ocurre cuando una aplicación trata de tener acceso a un recurso reservado para otra aplicación o para el propio sistema operativo. INSTANCIAR - Crear una instancia de una clase. Reservar dinámicamente el espacio de memoria necesario para almacenar sus campos y el resto de los datos que permitirán su operación mediante una llamada al constructor de la clase. STACK - Estructura de datos utilizada por los microprocesadores para salvar momentáneamente el contenido de los registros y luego poder restaurarlos al terminar de trabajar con ellos. Funciona según una lógica "First-In Last-Out" (el primero en entrar es el último en salir). El STACK también es utilizado por los programas para el pase de parámetros a subrutinas y para la creación de variables locales. VMT - Del inglés Virtual Method Table. Tabla de punteros donde se almacenan las direcciones asociadas a los métodos virtuales de una instancia de una clase. ________________________________________________________________________ 3. CONSTRUYENDO UN OBJETO DE NEGOCIO - Por Max Kleiner http://max.kleiner.com "UML mit Delphi" ca.370 S., Precio: 40.85 EUR, ISBN-3935042000 ¿Qué es un objeto de negocio? ----------------------------- Para proporcionar servicios de negocio, un objeto de negocio trabaja en colaboración con los objetos de almacenamiento de datos y los objetos de interfaz. Un objeto de negocio transforma los datos en información con consultas o cálculos. Los datos se pueden obtener de los servicios de datos o de los servicios de archivo. Los objetos de negocio a veces son referidos como objetos conceptuales, porque proveen servicios que cumplen los requerimientos de negocio, sin importar la tecnología. La idea de este artículo está de hecho dedicada al desarrollo de tal objeto de negocio, siguiendo un ejemplo con InterBase. En un archivo de unidad de módulo de datos uno puede escribir métodos, incluyendo los gestores de eventos de los componentes del módulo, así como las rutinas globales que encapsulan las reglas de negocio. Por ejemplo, uno puede escribir un procedimiento para realizar un cálculo de comisión bancaria y podría llamar a tal procedimiento desde un gestor de evento de un componente en el módulo o desde cualquier formulario que utilice el módulo. En un objeto de negocio simple (sin campos en la clase), uno tiene por lo menos cuatro tareas que completar: 1. La clase Business-Class hereda de un Data-Provider 2. La consulta es parte de la clase 3. Hay que hacer un cálculo o regla de negocio 4. El objeto es independiente de la GUI, la GUI llama al objeto 1. Construimos la clase; la superclase puede ser TQuery o TDataModule: type TBusinessObj = class(TQuery) private function open_QueryFee(qryID: integer):boolean; function calcFee(fee: double):double; public procedure changeFee(amount: double); procedure changeLimit(amount: double); end; 2. Definimos una consulta parametrizada: function TBusinessObj.open_QueryFee(qryID: integer):boolean; begin Result := False; try SQL.Clear; SQL.Add('SELECT * from ACCOUNT'); SQL.Add('WHERE acc_no = :pClient_acc'); Params[0].Name := 'pClient_acc'; Params[0].DataType := ftInteger; Params[0].AsInteger := qryID; Open; Result := True; finally // basura o algo end; end; 3. Antes de guardar el monto, calculamos algo procedure TBusinessObj.changeFee(amount: double); begin Edit; FieldByName('FEE').AsFloat := calcFee(amount); Post; end; // como una regla de negocio function TBusinessObj.calcFee(fee: double): double; begin result:= (2 * fee) / BANK_FACTOR // sólo un cálculo end; 4. Vayamos al cliente, que tiene un objeto de negocio como su miembro: private ... tblAccount: TBusinessObj; end; procedure TForm1.btnFeeClick(Sender: TObject); begin with tblAccount do begin if open_QueryFee(StrToInt(edtAccount.Text)) then changeFee(strToFloat(edtFee.text)); end; end; Mientras no tengamos campos en el objeto de negocio para mapear una base de datos relacional de manera orientada a objetos, podemos hacer una simple conversión de tipos de la instancia, sin un constructor. procedure TForm1.FormCreate(Sender: TObject); begin tblAccount := TBusinessObj(Query1); // conversión de tipos end; Panorama: --------- Cuando llegue la hora, puede que usted quiera mapear los atributos de una base de datos a los campos de la clase correspondiente. Esto significa que todas las sentencias SQL sean encapsuladas, para que así tenga un verdadero acceso orientado a objetos, el que describiré en una segunda parte. Veamos un ejemplo de algo destacado en programación de bases de datos de objetos, que es similar en Boldsoft con BOLD o en gs-soft con MetaBASE: Person := TPerson.Create; oAddressList := TAddressList.Create; try oPerson.Person_ID := 30; oAddressList := TAddressList.DBReadAllRelatedToObject(oPerson); for i := 0 to oAddressList.Count - 1 do S_MBox(oPerson.LastName + ' ' + oAddressList.Adresses[i].Town); finally oPerson.Free; oAddressList.Free; end; ________________________________________________________________________ 4. PREGUNTAS FRECUENTES SOBRE DELPHI 6 PERSONAL EDITION ¿Por qué la llamaron "Personal"? ================================ Porque su licencia lo limita a usarla sólo para usos no comerciales. Cualquier propósito comercial, de negocios, gubernamental o institu- cional de cualquier clase está expresamente prohibido. La llamaron "personal" precisamente porque realmente es "personal". Usted puede distribuir sus trabajos creados con la Edición Personal de Delphi 6, pero no puede recibir ninguna compensación directa o indi- recta. Esto significa que por ejemplo usted no puede vender licencias de su software, cobrar por el desarrollo de una aplicación a medida, cobrar regalías, distribuir aplicaciones "adware", etc., etc., etc. ¡"Compensación directa o indirecta" es sumamente abarcativo!!! ¿A quiénes está dirigida? ========================= Puede sacar sus propias conclusiones. Supongo que es para hobbyistas o para aquellos que quieren aprender algo de programación... Ciertamente que no es para programadores que esperan obtener una remuneración por su trabajo, ni para negocios o instituciones de ningún tipo (incluyendo el gobierno). ¿Es gratis? =========== Sí, es gratis, pero debe registrarse. Puede bajarla del sitio de Borland: http://www.borland.com/delphi/personal/index.html ¿Si es gratis, por qué Borland la vende? ======================================== La descarga es de 140 MB, demasiado para la conexión a Internet que tienen muchas personas. Además, la versión en CD-ROM que Borland vende por $99.95 incluye un manual impreso y soporte de instalación. ¿Es código abierto ("open source")? =================================== No, no es open source, aunque están disponibles los fuentes de algunas unidades (como windows.pas). ¿Tengo que distribuir mis aplicaciones bajo la GNU GPL? ======================================================= No, eso se aplica a la Open Edition de Kylix. Con Delphi 6 Personal, si distribuye sus aplicaciones, debe hacerlo gratuitamente, pero no está obligado a distribuir el código fuente si no lo desea, aunque no puede cobrar por ello si decide hacerlo. ¿Qué componentes vienen con la Edición Personal? ================================================ Delphi 6 Personal Edition viene con 85 componentes. Nada de componentes de BD, FastNet, QReport, etc. Es muy parecida a la edición estándar de Delphi 5 (con algunas cosas nuevas, por supuesto). ¿Hay una Edición Estándar de Delphi 6? ====================================== A partir de la versión 6, no hay una Edición Estándar de Delphi. Si está interesado en desarrollar aplicaciones comerciales, considere adquirir la Edición Estándar de Delphi 5. ¿Cómo instalo Delphi 6 Personal? ================================ 1) Descargue Delphi 6 Personal del sitio de Borland: http://www.borland.com/delphi/personal/index.html En el tercer paso del proceso de descarga no se olvide de hacer clic en el enlace "Send me a serial number and authorization key via email" (enviarme el número de serie y la clave de autorización por email) para recibir por email el número de serie y la clave de autorización que se requiere poder instalar el producto. Si usted es miembro de la Comunidad no se olvide primero de comprobar que su nombre, dirección de email y otros datos de su cuenta de la Comunidad sean correctos. Si usted no es miembro de la Comunidad, compruebe que está escribiendo correctamente su nombre, dirección de email y otros datos. 2) Corra el archivo descargado (BorlandDelphiPersonalEdition.exe). Eso descomprimirá el instalador en una carpeta de su elección. De manera predeterminada será en C:\Archivos de programa\Borland Delphi Personal Installer 3) Corra el instalador (INSTALL.EXE) y haga clic en el botón "Delphi 6" del Setup Launcher para comenzar la instalación. 4) Se le solicitará el número de serie y la clave de autorización que le debería haber sido proporcionada por email. 5) Lea la licencia muy cuidadosamente. Si está de acuerdo con los términos de la licencia, haga clic en el botón de radio correspon- diente y luego haga clic en el botón "Next" (siguiente) para continuar. 6) Proceda con el resto de la instalación de manera usual... 7) Al final puede que necesita reiniciar su sistema. 8) Después de la instalación encontrará el grupo "Borland Delphi 6" en su menú de Inicio de Windows. Diríjase allí y corra Delphi 6. 9) Se le solicitará que se registre. Necesitará una conexión a Internet para registrarse en línea (el proceso de registración es bastante fácil para los miembros de la Comunidad Borland). También se puede registrar por teléfono o con su navegador web si tiene una clave de activación. No recibí una clave de activación así que supongo que está disponible para quienes compren la versión en CD-ROM. 10) ¡Disfrute! :) ________________________________________________________________________ 5. COMPONENTES VCL Componente SpeedParser - Analizador Rápido de Expresiones ========================================================= - Por Mattias Andersson <mattias@centaurix.com> Este componente de análisis de expresiones evaluará rápidamente fórmulas matemáticas y le permitirá definir sus propias variables. Al evaluar expresiones matemáticas, la velocidad es toda una cuestión, por lo que hice un intento de analizador de expresiones (expression parser) que recompila la expresión en listas/arreglos de instrucciones y variables. Use la propiedad ParseString para establecer su expresión y después simplemente llame al método Parse para realizar la evaluación. Si desea definir su propias variables utilice los métodos AddVar y SetVar. Las variables predefinidas son A..Z y Pi. La aplicación demo incluida utiliza los componentes SpeedParser para establecer los valores RGB de cada píxel en una imagen dependiendo de expresiones que evalúan las coordinadas X e Y. El componente TImage no es muy rápido al establecer los píxeles; si usted está buscando una biblioteca rápida de gráficos debe darle una ojeada a Graphics32 de Alex Denisov: http://www.g32.org Mattias Andersson Imagen PCX con soporte de paleta ================================ - Por Tommy Andersen <tommy.andersen@easyware.org> Leí el artículo http://213.208.2.22/articles/article_2565.asp de Maarten de Haan, y pensé en publicar mi propio componente de gráficos PCX con soporte de manejo de paleta de colores, etc. Este componente no soporta guardar imágenes PCX como el arriba mencionado, pero una pequeña combi- nación de ambos y tendrá un gran componente! :) Tommy Andersen ________________________________________________________________________ 6. TRUCOS Y CONSEJOS Haciendo una aplicación cliente TCP/IP - Compartiendo mi experiencia... ======================================================================= - Por S.S.B. Magesh Puvananthiran <sesbaNOSPAM@hotmail.com> Conectándose a un servidor TCP/IP desde un cliente Delphi --------------------------------------------------------- Como todo desarrollador Delphi sabe, para hacer una aplicación cliente TCP/IP en Delphi podemos utilizar el componente de TClientSocket. Me enfrenté a un problema al tratar de conectar a un servidor TCP/IP (otra computadora) y enviar datos a esa máquina y obtener datos de vuelta. En el evento OnShow del formulario, establecí las propiedades Address y Port del componente TClientSocket con la dirección IP del servidor TCP/IP y el número de puerto, y establecí Active en True. Después de eso intenté enviar los datos en el mismo evento. No pude enviar los datos. Comprobé que la propiedad Active fuera True (significando conectado). Así que pensé que el problema podría estar en el evento OnShow del formulario y entonces puse el mismo código (establecer la dirección IP y el número de puerto y Active en True) en los eventos FormCreate y OnClick, pero no pude salir de ese problema. La cosa es que intenté conectarme con el servidor TCP/IP e intenté enviar los datos al mismo tiempo. Esto no parece trabajar correctamente. Después leí la ayuda de Delphi cuidadosamente y logré la solución. La solución que encontré es: ---------------------------- Primero necesitamos establecer la dirección IP y el número de puerto del servidor TCP/IP en el componente TClientSocket en el evento OnCreate del formulario principal del proyecto, y establecer Active en True. Después de ello podemos utilizar los métodos Open y Close del componente TClientSocket para conectarnos y desconectarnos del servidor TCP/IP respectivamente. Si deseamos enviar datos a un servidor TCP/IP desde diferentes formu- larios en un proyecto, podemos utilizar un módulo de datos (DataModule) y poner el componente TClientSocket allí para utilizarlo en por todas partes a través del proyecto, incluyendo ese módulo de datos en todos los archivos de unidad donde deseemos utilizarlo. Y una cosa más, en el evento ClientSocketRead necesitamos poner una demora de tiempo mientras se leen los datos desde el servidor TCP/IP. Este retraso podría ser de algunos milisegundos y depende del tráfico de la red puesto que podemos no leer todos los datos enviados del servidor TCP/IP en un momento dato, incluso si uno tiene un buffer grande. Así que puede que necesite esperar algunos milisegundos entre lecturas. Utilicé el ClientType del TClientSocket como ctNonBlocking. Podemos también utilizar ctBlocking como ClientType, pero en ese caso el servidor TCP/IP debe ser uno multi-hilos. Aunque puede parecer un tema sencillo, simplemente quería compartirlo con todos nuestros amigos en Delphi. Gracias. Magesh. Alineando texto en un StringGrid ================================ Para alinear texto en una cuadrícula de cadenas (StringGrid) tiene que asignar el evento OnDrawCell donde usted tiene que calcular la posición del texto y después dibujarlo en el lienzo. El ejemplo siguiente centra el texto de la primera fila de un TStringGrid y alínea a la derecha el texto de la primera columna: procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var CellText: string; begin with StringGrid1 do if ARow = 0 then begin CellText := Cells[ACol, ARow]; Canvas.TextRect(Rect, Rect.Left + (Rect.Right - Rect.Left - Canvas.TextWidth(CellText) + 1) div 2, Rect.Top + 2, CellText); end else if ACol = 0 then begin CellText := Cells[ACol, ARow]; Canvas.TextRect(Rect, Rect.Right - Canvas.TextWidth(CellText) - 2, Rect.Top + 2, CellText); end; end; Evitando que el usuario ejecute otras aplicaciones ================================================== Si no quiere que los usuarios de su aplicación puedan correr otras aplicaciones mientras la suya está corriendo, puede usar la siguiente función para "bloquear"/"desbloquear" su sistema: procedure ModalApp(Activate: LongBool); var h: cardinal; begin SystemParametersInfo(SPI_SCREENSAVERRUNNING, Cardinal(Activate), @h, 0); h := FindWindow('Shell_TrayWnd', nil); if h <> 0 then if Activate then ShowWindow(h, SW_HIDE) else ShowWindow(h, SW_SHOWNOACTIVATE); h := FindWindow('Progman', 'Program Manager'); if h <> 0 then EnableWindow(h, not Activate); end; Asumiendo que Activate es True, la llamada a SystemParametersInfo con SPI_SCREENSAVERRUNNING es para "engañar" al sistema operativo haciéndole creer que el salvapantallas está corriendo. Esto tendrá el deseado efecto colateral de inhabilitar los atajos CTRL+ALT+DEL, CTRL+ESC y ALT+TAB. El propósito de esto es evitar que el usuario pueda forzar la terminación de la aplicación con el Administrador de Tareas (el cierre de la aplicación supuestamente está protegido con contraseña). Luego encontramos el manejador (handle) de la barra de tareas para ocultarla (o podríamos inhabilitarla) y finalmente encontramos el manejador del escritorio para inhabilitarlo. Llame a ModalApp pasando False como parámetro para desactivar el efecto y regresar a la normalidad. NOTA: Este código probablemente no funcionará en Windows NT/2000. ________________________________________________________________________ 7. DELPHI EN LA RED * Delphi Database Programming Course - By Zarko Gajic Free online database programming course for beginner Delphi developers focused on ADO techniques. A new chapter has been added in the last weeks (Chapter 14 "Charting with Databases"). http://delphi.about.com/library/weekly/aa010101a.htm * Searching for Delphi - By Zarko Gajic Where and how to search for Delphi and Object Pascal programming related materials on the Net. http://delphi.about.com/library/weekly/aa121900a.htm * Delphi Coding Standards and Conventions - By Zarko Gajic Recommendations of standards and conventions for designing, coding, and commenting software projects written in Delphi and Kylix. http://delphi.about.com/cs/standards/index.htm * Component writing, part 2 - By Peter Morris This second part cover how to write advanced properties, how to write custom streaming for those properties, and sub-properties http://www.howtodothings.com/showarticle.asp?article=320 * Screen Shuffling with Delphi - By Zarko Gajic Delphi code that divides the current desktop screen into blocks and then swaps the blocks. It includes an option that lets you adjust the shuffling speed, and the size of the blocks. Great intro to sliding puzzle game or to screen saver development. http://delphi.about.com/library/weekly/aa082801a.htm * Listening to the Clipboard - By Zarko Gajic Extending the clipboard's flexibility and functionality from Delphi. Taking control over the Clipboard with custom formats. Coding Delphi to receive clipboard change notifications. http://delphi.about.com/library/weekly/aa110700a.htm * Delphi Object Hierarchy - By Borland Colorful hierarchy of all objects in all versions of Delphi 5. http://delphi.about.com/library/doh.pdf * Building a stand-alone Web service with Indy - By Dave Nottage This article shows how to build a Web service using Indy and Delphi 6. http://community.borland.com/article/0,1410,27513,00.html * Pente: An introduction to programming strategy games - By Armando de la Torre This article describes the creation of a game known as Pente or Go, a simple strategy game which has similar rules to tic-tac-toe. http://community.borland.com/article/0,1410,27512,00.html * Other people's windows This article demonstrates how to subclass non-Delphi windows from within a Delphi application. http://community.borland.com/article/0,1410,27295,00.html * WebSnap and Web Services hand-in-hand - By Daniel Polistchuck. Here's a guide to combining WebSnap and Web Services to create SOAP client web pages. http://community.borland.com/article/0,1410,27691,00.html * Programmers' pets - By Pintér Gábor Every programmer has a favorite application. http://community.borland.com/article/0,1410,26428,00.html * Managing a birthday calendar with InterBase - By Marco Hemmes User-defined functions are the key to remembering everyone's natal anniversary. http://community.borland.com/interbase/0,1419,7,00.html * Untapped resources in Windows - By Lubomir Rosenstein Scanning and creating shortcuts using COM and Delphi. http://community.borland.com/article/0,1410,26174,00.html * Generic handling for any Edit menu - By Steven C. Gudmundson Get out of the repetitive-coding rate rat race with this flexible, reusable code. http://community.borland.com/article/0,1410,26842,00.html * A Simple example of Artificial Intelligence using Delphi Array - By Raghunath Dhungel A Simple example of Artificial Intelligence using Delphi Array. Computer simulates learning process of human, learning by correcting mistakes! http://www.delphi3000.com/articles/article_2551.asp * How to deal with OLE DB directly, using its interfaces - By Alex Wijoyo An example for how to deal with OLE DB directly, using its interfaces. http://www.delphi3000.com/articles/article_2604.asp * Writing a simple ISAPI Filter for IIS - By Daniel Wischnewski This article shows you the basics of creating a simple ISAPI filter and how to use one to map sub-domains into specific folders. http://www.delphi3000.com/articles/article_2646.asp * How to change properties of objects just by name? - By Jürgen Sommer How can I access properties of classes that are not implemented via the uses-clause, just knowing their names (by string)? http://www.delphi3000.com/articles/article_2664.asp * How to create a DataBaseName Property? - By Geers Christophe The following example shows how to create a component with a DataBaseName property like the DataBaseName property of TQuery. The purpose of this property is to list all the available databases so that the user of the component can just select the required database. http://www.delphi3000.com/articles/article_2692.asp ________________________________________________________________________ ¡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/p0026.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!






