Boletín Pascal #51
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
![]() |
Boletín Pascal #51 - 02-FEB-2005 Índice 1. Unas palabras del editor 2. Cómo correr una sola instancia de una aplicación (y II) 3. Dentro de las clases e interfaces de Delphi (y II) 4. Enumerando todos los recursos de una red LAN 5. Foros / listas de correo 6. Delphi en la Red - Componentes, librerías y aplicaciones · Shareware / Comercial · Freeware · Actualizaciones de productos Borland - Artículos, trucos y consejos - Tutoriales y capacitación - Noticias ________________________________________________________________________ Tecno Soft Solutions. Reseller Autorizado de Symbol Technologies, líder mundial en captura de códigos de barras. >>> http://www.tecno-symbol.com ________________________________________________________________________ 1. Unas palabras del editor Ha pasado un largo tiempo sin que se publique el boletín, no por falta de voluntad, sino por falta de tiempo. Aunque muchos no lo crean, lleva varias horas armar un número y publicarlo, y en el pasado 2004 la verdad que me ha sido difícil encontrar esas horas, que siempre son robadas al necesario descanso y a la familia... Bien, pues ahora que dispongo de un poco más de tiempo (al menos temporalmente), quiero decirles que no me he olvidado de ustedes y que en estos días estaré traduciendo las dos ediciones que me quedan pendientes de publicación, y ojalá que durante el 2005 podamos publicar una edición cada dos meses como nos hemos propuesto con Dave, pero no prometo nada, ¿OK? Si no fuera por los autores que colaboran sus artículos, este boletín no existiría, así que vaya nuestro agradecimiento en esta ocasión a Furious Logic, Peter Johnson y Ezra Hoch, este último acreedor del premio disponible para este número: * Ezra Hoch - "Dentro de las clases e interfaces de Delphi" Delphi SWF SDK (Standard) v1.4 - por FeatherySoft ($170) Librería Object Pascal para crear ficheros SWF sin usar librerías dinámicas externas. Características: objetos visuales (forma, botón, texto, forma cambiante, sprite); todos los tipos de relleno (sólido, gradiente, imagen); fuentes incrustadas; comandos de acciones; sonidos (eventos, streaming); video; cualquier transición y transformación. http://www.delphiflash.com/ Los premios para la próxima edición, que dicho sea de paso ya está casi lista y se publicará próximamente, son: * KylixDriver v1.1 - por ET Kimberliteware Ltd ($39 / $69 con fuentes) Conjunto de herramientas para el desarrollo de drivers Linux que accedan al hardware de la PC, permitiendo desarrollar con Kylix controladores de dispositivo ISA/PCI para hardware específico. http://etsoftware.tripod.com/products.html * InstallAWARE 3.0 Express Edition - por MimarSinan Int. ($69.95) Desarrolle instaladores para Windows Installer sin necesidad de conocimiento previo de MSI. InstallAWARE automáticamente convierte en tiempo de construcción un script de flujo condicional en una base de datos MSI que cumple con ICE, certificable mediante logo. El IDE ofrece una interfaz visual que automáticamente genera un script de instalación por usted, el cual puede ser completamente adaptado. http://www.installaware.com/landingea.html * KnowedgeBASE Vortex 2.9 por Delphinium Software ($49.35) El corazón de KnowledgeBASE es un árbol de información altamente expandible y con gran capacidad de búsqueda, que puede verse como esquema o auditado dentro de un procesador de textos incorporado. Incluye una base de datos para referencias guardadas. http://www.download.com/KnowledgeBase-Vortex/3000-2064-10342084.html Estamos orgullosos de ofrecer a los lectores del boletín un descuento especial para InstallAWARE 3 (30% en cualquier edición), gracias a la colaboración de MimarSinan International. InstallAWARE 3 ha sido desarrollado enteramente en Delphi y sirve para crear instaladores MSI sin necesidad de conocimiento previo sobre Windows Installer. En vez de limitarse a proveer una interfaz visual (como Wise o InstallShield), InstallAWARE tiene su propio RAD y lenguaje de programación/scripting, el que en tiempo de construcción es automáticamente convertido a base de datos MSI, certificable mediante con logo. Las características únicas incluyen distribución Web parcial, programación genuina para Windows Installer, un poderoso editor de diálogos con controles únicos (como contenedores Flash y HTML) y compresión 7ZIP avanzada (¡el runtime para .NET es de 11MB en vez de 23MB!). Por un tiempo limitado, los lectores pueden obtener un descuento de 30% en cualquier edición de InstallAWARE 3 siguiendo los enlaces que se publican en este número del boletín. ¿Alguien ha tenido oportunidad de usar el nuevo Delphi 2005? Recibimos sus comentarios en <eds2004 @ latiumsoftware.com>. Para más información sobre Delphi 2005: http://www.borland.com/delphi Saludos, Ernesto De Spirito y Dave Murray boletin-pascal-owner@gruposyahoo.com __________________ Colaboraron en esta edición: Furious Logic, Peter Johnson y Ezra Hoch ________________________________________________________________________ Help & Manual 3.50 por EC Software · Shareware ($ 279) - Una herramienta visual de autoría de ayuda para generar archivos WinHelp (.HLP), Adobe PDF, páginas HTML y los nuevos archivos HTML HELP (.CHM) introducidos en Windows 98, así como otros formatos de archivo y documentación impresa, todo desde una misma fuente. Una herramienta imprescindible para cualquier desarrollador de software. http://www.helpandmanual.com/ ________________________________________________________________________ 2. Cómo correr una sola instancia de una aplicación (y II) Parte 2: Una solución reutilizable Por Peter Johnson, Copyright (c) 2003 <delphidabbler at tiscali dot co dot uk> http://www.delphidabbler.com/ Visión general de una solución orientada a objetos -------------------------------------------------- Esta sección presenta una solución al problema de correr una sola instancia de una aplicación, solución que es orientada a objetos y extensible. El código principal está centralizado en una sola unidad (USingleInst.pas) que implementa y crea un objeto singleton (TSingleInst) que maneja la instancia única de la aplicación y se encarga de enviar y recibir datos de parámetros. Mientras que TSingleInst provee un nombre para la clase de ventana del formulario principal, el método de nombre de clase de ventana puede (y debe) ser redefinido (overridden) para proveer un nombre de clase único. También puede proveerse una marca de agua al redefinir el método. Se requiere alguna alteración al archivo de proyecto y al formulario principal, pero los cambios no son tan significativos como en el ejemplo anterior (presentado en la primera parte del artículo, publicada en el Boletín Pascal #50) y la funcionalidad principal está aislada en USingleInst.pas. El procedimiento para habilitar el soporte de una sola instancia de la aplicación en un proyecto es como sigue: * Añadir USingleInst.pas al proyecto. * Crear una nueva clase derivada de TSingleInst y redefinir los métodos que devuelven el nombre de clase de ventana y (opcionalmente) la marca de agua. * Registrar la nueva clase como la clase a usar al crear un objeto singleton SingleInst. * Modificar los fuentes del formulario principal y del proyecto para llamar al punto de entrada requerido del objeto SingleInst. En el resto de esta sección repasaremos la unidad USingleInst, luego daremos un vistazo a cómo derivar y registrar una nueva clase a partir de ella y finalmente describiremos los cambios a realizar a los archivos de código fuente del proyecto y del formulario principal. La unidad USingleInst --------------------- Repasaremos las partes de la unidad en orden. El código fuente de la unidad acompaña este artículo. Esta es la sección de Interfaz de la unidad que declara la clase y las funciones de apoyo: unit USingleInst; interface uses Windows, Controls, Messages; type TSingleInstParamHandler = procedure(const Param: string) of object; TSingleInstClass = class of TSingleInst; TSingleInst = class(TObject) private fOnProcessParam: TSingleInstParamHandler; // manejador de evento fEnsureRestoreMsg: UINT; // mensaje único para pedir restauración protected function WdwClassName: string; virtual: function WaterMark: DWORD; virtual; function FindDuplicateMainWdw: HWND; virtual; function SendParamsToPrevInst(Wdw: HWND): Boolean; virtual; function SwitchToPrevInst(Wdw: HWND): Boolean; procedure EnsureRestore(var Msg: TMessage); dynamic; procedure WMCopyData(var Msg: TMessage); dynamic; public constructor Create; procedure CreateParams(var Params: TCreateParams); function HandleMessages(var Msg: TMessage): Boolean; function CanStartApp: Boolean; property OnProcessParam: TSingleInstParamHandler read fOnProcessParam write fOnProcessParam; end; function SingleInst: TSingleInst; procedure RegisterSingleInstClass(Cls: TSingleInstClass); Repasemos primero las declaraciones de tipo. TSingleInstParamHandler es el tipo del manejador de evento OnProcessParam que es invocado por cada parámetro pasado a la aplicación en un mensaje WM_COPYDATA, mientras que TSingleInstClass es el tipo de clase ("class of") de la clase TSingleInst y clases derivadas. Cubriremos los métodos de la clase en la sección de Implementación. El otro único elemento que merece ser mencionado ahora es el campo fEnsureRestoreMsg. El mismo se usa para almacenar el Id del mensaje usado para solicitar a una aplicación que restaure y muestre su ventana. El Id de mensaje es provisto por Windows y está garantizando que es único. Desafortunadamente, el uso de ese mensaje significa que no podemos usar el manejador de mensajes de Delphi para manejar el mensaje y debemos recurrir a interceptar mensajes en el bucle de mensajes (ver más abajo). Finalmente la función SingleInst se usa para devolver una instancia del objeto singleton TSingleInst (o descendiente) mientras que el procedimiento RegisterSingleInstClass se usa para registrar el tipo de la clase a ser usada para crear el objeto singleton. Examinaremos la sección de Implementación en segmentos. He aquí el primero: implementation uses SysUtils, Forms; var // Globales almacenando singleton SingleInst y la clase de SingleInst gSingleInst: TSingleInst = nil; gSingleInstClass: TSingleInstClass = nil; function SingleInst: TSingleInst; begin if not Assigned(gSingleInst) then begin if Assigned(gSingleInstClass) then gSingleInst := gSingleInstClass.Create else gSingleInst := TSingleInst.Create; end; Result := gSingleInst; end; procedure RegisterSingleInstClass(Cls: TSingleInstClass); begin gSingleInstClass := Cls; end; Las variables globales privadas gSingleInst y gSingleInstClass almacenan una referencia al objeto singleton y al tipo del objeto singleton (tal como se registra en RegisterSingleInstClass) respectivamente. De la implementación de SingleInst se puede ver que el objeto singleton es creado la primera vez que el objeto se referencia. Si se registró una clase derivada, ésta se usa para crear el singleton, de lo contrario se usa la clase base. Esto significa que cualquier clase derivada de TSingleInst debe ser registrada antes que el objeto sea accedido por primera vez - se recomienda que RegisterSingleInstClass sea llamada en la sección de Initialización de la unidad donde se define la clase derivada. Esto también explica por qué el objeto singleton no se crea en la sección de Inicialización de esta unidad, puesto que esta sección sería llamada antes que las de las unidades de las clases derivadas. Yendo a la definición de la clase, primero observemos el constructor de la clase que simplemente registra a Windows el mensaje definido por el usuario: constructor TSingleInst.Create; begin inherited; fEnsureRestoreMsg := RegisterWindowMessage('USINGLEINST_ENSURERESTORE'); end; Hay varios "puntos de entrada" en la clase que deben ser llamados desde la unidad principal o desde el archivo de proyecto. Los veremos a continuación: function TSingleInst.CanStartApp: Boolean; var Wdw: HWND; begin Wdw := FindDuplicateMainWdw; if Wdw = 0 then Result := True else Result := not SwitchToPrevInst(Wdw); end; procedure TSingleInst.CreateParams(var Params: TCreateParams); begin inherited; StrPLCopy(Params.WinClassName, WdwClassName, SizeOf(Params.WinClassName) - 1); end; function TSingleInst.HandleMessages(var Msg: TMessage): Boolean; begin if Msg.Msg = WM_COPYDATA then begin WMCopyData(Msg); Result := True; end else if Msg.Msg = fEnsureRestoreMsg then begin EnsureRestore(Msg); Result := True; end else Result := False; end; El primero de estos "puntos de entrada" es CanStartApp, el que se llama desde el archivo de proyecto e indica si la aplicación debe ser iniciada o no. Funciona de modo similar a la función CanStart de la primera parte de este artículo (publicada en el Boletín Pascal #50). El método CreateParams establece el nombre de clase de ventana del formulario principal actualizando los parámetros de creación del formulario. El formulario principal debe redefinir su TForm.CreateParamsSingleInst.CreateParams desde dentro de ese método. El "puntos de entrada" más complejo es el método HandleMessages, que intercepta el mensaje WM_COPYDATA y el campo fEnsureRestoreMsg del formulario principal y maneja el procesamiento delegándolo a WMCopyData y EnsureRestore respectivamente (ver más abajo). Esto método devuelve verdadero si maneja el mensaje y falso en caso contrario. El formulario principal debe redefinir el método TForm.WndProc y desde allí llamar a SingleInst.HandleMessages, llamando su método TForm.WndProc heredado si el método HandleMessages devuelve falso. Examinemos ahora los métodos de soporte protegidos. Primero tenemos dos métodos que deben ser redefinidos en clases descendientes: function TSingleInst.WdwClassName: string; begin Result := 'SingleInst.MainWdw'; end; function TSingleInst.WaterMark: DWORD; begin Result := 0; end; WdwClassName simplemente devuelve el nombre a usar como el nombre de de ventana del formulario principal. Este debería ser redefinido para devolver algo único a la aplicación. Similarmente, Watermark devuelve 0 como el valor de marca de agua (las marcas de agua se trataron en la primera parte de este artículo -publicada en el Boletín Pascal #50-). Este método debería ser redefinido para devolver algún valor inusual si se emplearán marcas de agua. Ahora siguen los dos métodos de soporte que manejan mensajes: procedure TSingleInst.EnsureRestore(var Msg: TMessage); begin if IsIconic(Application.Handle) then Application.Restore; if Assigned(Application.MainForm) and not Application.MainForm.Visible then Application.MainForm.Visible := True; Application.BringToFront; end; procedure TSingleInst.WMCopyData(var Msg: TMessage); var PData: PChar; Param: string; begin if TWMCopyData(Msg).CopyDataStruct.dwData = WaterMark then begin PData := TWMCopyData(Msg).CopyDataStruct.lpData; while PData^ <> #0 do begin Param := StrPas(PData); if Assigned(fOnProcessParam) then fOnProcessParam(Param); Inc(PData, Length(Param) + 1); end; Msg.Result := 1; end else Msg.Result := 0; end; Nótese que estos métodos se basan bastante en los métodos de manejo de mensajes tratados en la primera parte de este artículo, excepto que ya no se implementan directamente en la unidad del formulario. El único cambio significativo es que WMCopyData ahora invoca al evento OnProcessParam en vez de llamar a un método fijo para procesar los parámetros directamente. Finalmente examinaremos los tres métodos protegidos restantes: function TSingleInst.FindDuplicateMainWdw: HWND; begin Result := FindWindow(PChar(WdwClassName), nil); end; function TSingleInst.SendParamsToPrevInst(Wdw: HWND): Boolean; var CopyData: TCopyDataStruct; I: Integer; DataSize: Integer; Data: PChar; PData: PChar; begin DataSize := 0; for I := 1 to ParamCount do Inc(DataSize, Length(ParamStr(I)) + 1); Inc(DataSize); Data := StrAlloc(DataSize); try PData := Data; for I := 1 to ParamCount do begin StrPCopy(PData, ParamStr(I)); Inc(PData, Length(ParamStr(I)) + 1); end; PData^ := #0; CopyData.lpData := Data; CopyData.cbData := DataSize; CopyData.dwData := WaterMark; Result := SendMessage(Wdw, WM_COPYDATA, 0, LPARAM(@CopyData)) = 1; finally StrDispose(Data); end; end; function TSingleInst.SwitchToPrevInst(Wdw: HWND): Boolean; begin Assert(Wdw <> 0); if ParamCount > 0 then Result := SendParamsToPrevInst(Wdw) else Result := True; if Result then SendMessage(Wdw, fEnsureRestoreMsg, 0, 0); end; Estos métodos son análogos a las rutinas equivalentes en la primera parte de este artículo y no serán descritos nuevamente aquí. Modificaciones al archivo de proyecto ------------------------------------- El archivo de proyecto debe pasar por un simple cambio para usar el objeto SingleInst a fin de determinar si iniciar o no la aplicación. Primero debe añadirse la unidad USingleInst al proyecto y luego se debe modificar el archivo de proyecto para envolver la creación del formulario y el código de ejecución de la aplicación en una sentencia condicional, como sigue: ... begin if SingleInst.CanStartApp then begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end; end. Modificación al formulario principal ------------------------------------ Se necesita más trabajo en la unidad del formulario principal. Primero, agregue las siguientes declaraciones en la sección protegida de la clase del formulario: protected procedure WndProc(var Msg: TMessage); override; procedure HandleParam(const Param: string); procedure CreateParams(var Params: TCreateParams); override; Ahora implemente estos métodos para llamar a SingleInst como sigue: procedure TForm1.CreateParams(var Params: TCreateParams); begin inherited; SingleInst.CreateParams(Params); end; procedure TForm1.WndProc(var Msg: TMessage); begin if not SingleInst.HandleMessages(Msg) then inherited; end; El método CreateParams llama al método heredado (inherited) y luego llama a SingleInst.CreateParams para establecer el nombre de clase de ventana. El método redefinido WndProc llama a SingleInst.HandleMessages para determinar si el mensaje es uno de los manejados por SingleInst (la función devuelve verdadero si maneja el mensaje). Si el mensaje no es manejado por SingleInst.CreateParams se llama al método WndProc heredado para manejarlo. También queremos manejar el evento TSingleInst.OnProcessParam para que la aplicación sea notificada cuando se pasen parámetros con un mensaje WM_COPYDATA. En este ejemplo agregaremos los parámetros a un cuadro de lista. Asignamos el manejador de evento en FormCreate, donde también procesaremos los parámetros pasados al programa la primera vez que se inicia: procedure TForm1.FormCreate(Sender: TObject); var I: Integer; begin SingleInst.OnProcessParam := HandleParam; for I := 1 to ParamCount do HandleParam(ParamStr(I)); end; procedure TForm1.HandleParam(const Param: string); begin ListBox1.Items.Add(Param); end; Redefiniendo TSingleInst ------------------------ Ahora veremos un ejemplo de como redefinir TSingleInst. Simplemente proveemos una nueva marca de agua y un nuevo nombre de clase de ventana. La unidad entera (UMySingleInst.pas) se lista abajo. El único punto a notar es cómo se registra la nueva clase como la clase a usar para el objeto singleton SingleInst. unit UMySingleInst; interface uses Windows, USingleInst; type TMySingleInst = class(TSingleInst) protected function WdwClassName: string; override; function WaterMark: DWORD; override; end; implementation { TMySingleInst } function TMySingleInst.WaterMark: DWORD; begin Result := $DE1F1DAB; end; function TMySingleInst.WdwClassName: string; begin Result := 'DelphiDabbler.SingleInst.1'; end; initialization RegisterSingleInstClass(TMySingleInst); end. Conclusión ---------- Este artículo ha demostrado como asegurar que sólo una instancia de una aplicación se ejecute. También demuestra cómo pasar los parámetros de línea de órdenes a la instancia que ya está en ejecución, asegurando que su ventana se muestre prominentemente. El método usado es detectar la presencia de la clase de ventana de la ventana principal de la aplicación entre las ventanas del nivel más alto. La comunicación con la instancia previa se hace por medio de mensajes enviados a la ventana principal de la instancia. En particular, se usa una un mensaje definido por el usuario para asegurar que la ventana se restaure y se haga visible, y se usa el mensaje WM_COPYDATA para enviar los parámetros de línea de órdenes cruzando las fronteras de los procesos. Se provee el esqueleto de algo de código fuente para una aplicación que corre como una única instancia. Finalmente se presentó una clase reutilizable que puede ser usada en el desarrollo de un programa que corra una única instancia de la aplicación. Se incluye también una aplicación demostrativa junto con el código fuente que acompaña esta edición. __________________ Peter Johnson es un programador hobbyista que vive en West Wales (Reino Unido) y que "chapotea" en Delphi. Mantiene el sitio web DelphiDabbler (http://www.delphidabbler.com/) donde publica sus artículos y sus aplicaciones y componentes Delphi gratuitos. Una versión completa de este artículo se encuentra disponible en: http://www.delphidabbler.com/articles.php?article=13 ________________________________________________________________________ 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 ________________________________________________________________________ 3. Dentro de las clases e interfaces de Delphi (y II) Por Ezra Hoch La primera parte de "Dentro de las clases e interfaces de Delphi" se publicó en el Boletín Pascal #49. Debería leerse antes de este artículo: http://www.latiumsoftware.com/es/pascal/0049.php#4 En esta parte finalizaremos cubriendo la implementación de interfaces en Delphi, y repasaremos unas conclusiones útiles. Comencemos con un ejemplo en profundidad: type IInterface1 = interface procedure ActA; procedure ActB; end; IInterface2 = interface(IInterface1) procedure ActC; procedure ActD; stdcall; end; TSampleClass = Class(TInterfacedObject, IInterface1, IInterface2) procedure ActA; procedure ActB; procedure ActC; procedure ActD; stdcall; end; var Interface1 : IInterface1; Interface2 : IInterface2; Sample : TSampleClass; begin Sample := TSampleClass.Create; Interface1 := Sample; Interface2 := Sample; Interface1.ActA; Interface1.ActB; Interface2.ActA; Interface2.ActB; Interface2.ActC; Interface2.ActD; end; En vez de ver el código compilado de este ejemplo, simplemente notaré sus aspectos interesantes. Primero, al asignar un valor a Interface1, esperaríamos que Delphi lo tome como la dirección apuntada por 'Sample', más una cantidad específica ($10) y que eso sea todo. Al asignar un valor a Interface2, esperaríamos que Delphi haga lo mismo, sólo que sumaría ($0C) porque las interfaces se almacenan en memoria de la última a la primera. Pero Delphi no hace esto. Asigna tanto a Interface1 como a Interface2 la dirección apuntada por 'Sample' más $0C. Esto es porque IInterface2 hereda de IInterface1. Por lo tanto IInterface2 incluye Interface1, de modo que cualquier llamada a Interface1 en realidad será ejecutada a través de la lista de métodos de IInterface2. Segundo, cuando llamamos a Interface1.ActA, se llama al cuarto método de la lista de métodos de IInterface2 (porque IInterface2 hereda de IInterface1 y a su vez hereda de IUnknown). Llamando a Interface1.ActB se llama al quinto método de la lista de métodos de IInterface2. Cuando llamamos a Interface2.ActA se llama al cuarto método de la lista de métodos de IInterface2, al igual que Interface1.ActA. Esto es porque IInterface2 hereda de IInterface1. Tercero, cuando llamamos a Interface2.ActD, Delphi añade una instrucción adicional antes de llamar al séptimo método de IInterface2. Esto es porque hemos declarado el método con una convención diferente de llamada (stdcall). Nótese que todos los métodos de IUnknown se definen con la directiva stdcall. La estructura del código de los métodos de la lista de métodos de una interfaz siempre sigue la siguiente regla: Primer método . . Último método Lista de métodos de la interfaz madre En nuestro caso, el código de los métodos de la lista de métodos de IInterface2 es como sigue: ActC ActD // Lista de métodos de IInterface1 ActA ActB // Lista de métodos de IUnknown QueryInterface _AddRef _Release NOTA: La estructura de arriba es cómo se organiza el código de los métodos en memoria, pero la primera entrada en una lista de métodos pertenece a QueryInterface (el primer método de IUnknown), aunque apuntará a un lugar en memoria (la implementación de ese método QueryInterface específico de la interfaz) que está más arriba que la implementación de los métodos propios de la interfaz. En nuestro caso, la implementación del método QueryInterface de IInterface2 está más arriba en memoria que la implementación del método ActB de IInterface2, que a su vez está más arriba en memoria que la implementación del método ActD, aunque ActD sea la séptima entrada en la lista de métodos de IInterface2, ActB la quinta y QueryInterface la primera. Para entender acabadamente lo que sucede cuando Delphi llama a un método de una interfaz, démosle un vistazo al código ensamblador de las llamadas a los métodos de la lista de métodos de IInterface2 en el ejemplo de arriba. El siguiente código es una copia exacta del código compilado (excepto por los comentarios): // ActC add eax, -$0C call TSampleClass.ActC // ActD add dword ptr [esp + $04], -$0C call TSampleClass.ActD // ActA add eax, -$0C call TSampleClass.ActA // ActB add eax, -$0C call TSampleClass.ActB // QueryInterface add dword ptr [esp + $04], -$0C call TInterfacedObject.QueryInterface // _AddRef add dword ptr [esp + $04], -$0C call TInterfacedObject._AddRef // _Release add dword ptr [esp + $04], -$0C call TInterfacedObject._Release Como se recordará, un método de un objeto es una función o procedimiento ordinario que acepta como primer parámetro una instancia de la clase del método. Como puede notar, antes de cada llamada al método real (por ej. "TSampleClass.ActD") hay una línea de código que cambia el valor de "eax" o de "dword ptr [esp + $04]", dependiendo de la convención de llamada. Como puede notarse, en todos los casos se resta $0C de una variable. Pero, ¿por qué 12 ($0C = 12)? Porque resulta que esta interfaz (IInterface2) está en el tercer lugar después del puntero a la VMT de la clase TSampleClass (FRefcount e IUnknown están antes). Por consiguiente, el valor de cualquier instancia de IInterface2 de TSampleClass (Interface2 por ejemplo) es en realidad el valor del puntero a la instancia de la clase más 12. Aquí hay otro ejemplo que ayudará a entender la sección de arriba. El siguiente código continúa las definiciones del código de arriba: type IAnotherInterface = interface procedure ActE; end; TAnotherSample = class(TInterfacedObject, IInterface2, IAnotherInterface) procedure ActA; procedure ActB; procedure ActC; procedure ActD; stdcall; procedure ActE; end; var Interface2: IInterface2; begin Interface2:= TAnotherSample.Create; Interface2.ActC; end; Ahora comparemos la entrada para este ejemplo de IInterface2 y el anterior: // IInterface2 de TAnotherSample: add eax, -$10 call TAnotherSample.ActC // IInterface2 de TSampleClass: add eax, -$0C call TSampleClass.ActC Hay dos cambios obvios: a) La función real que es llamada (TAnotherSample.ActC o TSampleClass.ActC) b) La cantidad por la que se modifica EAX. Nótese que al cambiar IInterface2 de TAnotherSample, EAX se desplaza en 16 ($10 = 16), en vez de ser desplazado en 12. Esto es porque en TAnotherSample, IInterface2 es la segunda interfaz en la estructura de la instancia en memoria, y por consiguiente está "más lejos" que la instancia misma y debe desplazarse 4 bytes adicionales. Ahora veamos algunas cosas útiles. Primero, si quiere chequear si dos (o más) variables de interfaz son de la misma instancia, no puede simplemente compararlas, incluso si son del mismo tipo. Debe "QueryInterfacearlas" (convertirlas) a un mismo tipo de interfaz, y luego comparar. Como regla general, si desea comparar interfaces, conviértalas a IUnknown y luego compare. Ejemplo: type IBooA = interface end; IBooB = interface end; TBoo = class(TInterfacedObject, IBooA, IBooB) end; var Boo : TBoo; BooA : IBooA; BooB : IBooB; begin Boo := TBoo.create; BooA := Boo; BooB := Boo; // Esto no compilará if BooA = BooB then begin Beep; end; if Integer(BooA) = Integer(BooB) then begin // Nunca llegará aquí Beep; end; if IUnknown(BooA) = IUnknown(BooB) then begin // Nunca llegará aquí Beep; end; // La palabra "as" es la mismo que QueryInterface al actuar con // interfaces if (BooA as IUnknown) = (BooB as IUnknown) then begin // Siempre llegará aquí Beep; end; end; Explicación: La primera comparación no compilará porque BooA y BooB son de dos tipos diferentes. La segunda y la tercera comparación compilarán, pero nunca devolverán verdadero. Esto es porque la conversión de tipos (typecasting) no cambiará el valor de la variable que está siendo convertida. Sólo permite al compilador compilar el código aunque haya dos tipos diferentes involucrados. He aquí que, si BooA es diferente de BooB, la comparación nunca devolverá verdadero, sin importar qué conversión de tipos se les hagan. Pero, ¿por qué BooA y BooB tienen diferentes valores si ambas fueron asignadas con la sentencia ":= Boo;". La respuesta es simple. Recuerde cuando dije que el valor de una variable de interfaz es en realidad el valor de la instancia misma (en realidad es el valor del puntero a la instancia) más un número por cada interfaz- En nuestro caso, BooA es lo mismo a lo que apunta Boo, más 16. Y BooB es lo mismo a lo que Boo apunta, más 12. Por eso es que BooA y BooB no son lo mismo. La cuarta comparación sí funciona. Esto es porque si una interfaz es del mismo tipo, entonces compararla con otra interfaz del mismo tipo devolverá el resultado esperado (si ambas interfaces fueron adquiridas a través de QueryInterface, no por conversión de tipos). Es así porque si son del mismo tipo, entonces la diferencia entre ambas y la instancia es la misma. Y si son de la misma instancia, entonces deberían ser iguales. Esto es, cada interfaz es igual a su instancia más un desplazamiento Delta específico (el Delta depende de la interfaz). En otras palabras, Interfaz = Instancia + Delta. Si Instancia es la misma para ambas interfaces, y el Delta es el mismo (porque son del mismo tipo de interfaz), entonces ambas interfaces serán iguales. Nota: Esta es la forma en que Delphi trabaja, para bien o para mal. Debería tomar esto en cuenta al escribir código para propiedades de tipo interfaz. El siguiente código no funcionará apropiadamente: TSample = class private FData : IUnknown; procedure SetData(Value : IUnknown); protected procedure Changed; virtual; abstract; public property Data : IUnknown read FData write SetData; end; procedure TSample.SetData(Value : IUnknown); begin // Esto es incorrecto. if Value <> FData then begin FData := Value; Changed; end; end; Puede parecer que ese código debería funcionar, pero no funcionará cuando alguien asigna la propiedad Data con un IUnknown obtenido por simple conversión de tipos. El código correcto debería ser: procedure TSample.SetData(Value : IUnknown); begin if (Value as IUnknown) <> (FData as IUnknown) then begin FData := Value; Changed; end; end; Segundo, una interfaz declarada como implementada por una clase (con excepción de las interfaces que herede de otras interfaces) significa que cada instancia de aquella clase tomará 4 bytes más de memoria. Puede parecer poco (y probablemente lo es) excepto en un caso. Considere el siguiente código: IInterfaceA = interface end; IInterfaceB = interface end; TSampleClass1 = class(TInterfacedObject, IInterfaceA) end; TSampleClass2 = class(TSampleClass1, IInterfaceA, IInterfaceB) end; Podría parecer que cada instancia de TSampleClass1 debería ocupar 16 bytes, y que cada instancia de TSampleClass2 debería ocupar 20 bytes (4 bytes más, porque soporta una interfaz más). Este no es el caso. Cada instancia de TSampleClass1 sí ocupa 16 bytes, pero cada instancia de TSampleClass2 ocupa 24 bytes. Esto es porque Delphi crea una entrada de interfaz incluso para interfaces que ya están implementadas en la clase madre. La solución a esto es simple. Sólo remueva la declaración de IInterfaceA en TSampleClass2. Esto no cambiará el hecho de que TSampleClass2 implementa IInterfaceA, porque TSampleClass2 hereda de TSampleClass1, la que implementa IInterface1. Esto no habría sucedido si IInterfaceB fuera un descendiente de IInterfaceA. Esto podría sumar mucho si realiza su herencia inapropiadamente. Por ejemplo: TSampleClass1 = class(TInterfacedObject, IUnknown) end; TSampleClass2 = class(TSampleClass1, IUnknown) end; TSampleClass3 = class(TSampleClass2, IUnknown) end; TSampleClass4 = class(TSampleClass3, IUnknown) end; TSampleClass5 = class(TSampleClass4, IUnknown) end; Cada instancia de TSampleClass5 ocupará 32 bytes de memoria, a pesar que no tiene datos reales (excepto por FRefCount de TInterfacedObject). ________________________________________________________________________ Ayúdanos a conseguir más suscriptores votando por el Boletín Pascal (Pascal Newsletter) en estos rankings: http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium ________________________________________________________________________ 4. Enumerando todos los recursos de una red LAN Desde Lima, Perú, Furious Logic nos envía un programa que enumera todos los recursos de una LAN. Como puede observarse en el código fuente del programa, que se adjunta a esta edición, el núcleo del programa es un procedimiento (MostrarRed) que hace uso de la API WNetEnumResource para enumerar los recursos de la red. Antes de llamar a WNetEnumResource es necesario obtener un handle llamando a la API WNetOpenEnum, y al final debemos liberarlo con una llamada a la API WNetCloseEnum. El proceso obtiene la dirección IP de un recurso con la función GetHostByName, la que se pasa como parámetro a la función inet_ntoa para convertirla en una cadena en formato www.xxx.yyy.zzz, que es lo que finalmente se muestra. En caso que un recurso sea a su vez un contenedor de recursos, el procedimiento se llama recursivamente a sí mismo para enumerar los recursos del contenedor, y así hasta el máximo nivel de anidamiento. Otro aspecto a destacar es que el procedimiento corre en un hilo de ejecución (thread) diferente del programa principal, una sana práctica para no congelar el programa mientras dura el proceso de detección. En el Boletín Pascal #53 presentaremos otro aporte de Furious Logic. ________________________________________________________________________ 5. Foros / listas de correo Recordamos a los suscriptores las direcciones de nuestros foros. Para unirse a algún foro, lo más recomendable es hacerlo desde la web para así tener acceso a todas las áreas del foro y la configuración de las opciones de suscripción, pero también es posible suscribirse por email. Para suscribirse desde la web es necesario poseer un ID de Yahoo! (si no tienes el tuyo, puedes conseguirlo gratis registrándote como usuario de Yahoo!). * Delphi-abierto. Programación en Delphi (todos los niveles). Si estás en la etapa de aprendizaje o si no te agradan los foros discriminados por niveles, este foro es para ti. http://espanol.groups.yahoo.com/group/delphi-abierto Suscripción: http://espanol.groups.yahoo.com/group/delphi-abierto/join delphi-abierto-subscribe@gruposyahoo.com * Delphi-intermedio. Programación en Delphi (nivel intermedio). Si ya sabes mucho de Delphi, pero todavía te falta un largo trecho para ser un gurú (o no tanto), este foro es para ti. http://espanol.groups.yahoo.com/group/delphi-intermedio Suscripción: http://espanol.groups.yahoo.com/group/delphi-intermedio/join delphi-intermedio-subscribe@gruposyahoo.com * Delphi-avanzado. Programación en Delphi. Sólo para gurús. http://espanol.groups.yahoo.com/group/delphi-avanzado Suscripción: http://espanol.groups.yahoo.com/group/delphi-avanzado/join delphi-avanzado-subscribe@yahoogroups.com * GrupoKylix. Programación en Kylix. http://espanol.groups.yahoo.com/group/GrupoKylix Suscripción: http://espanol.groups.yahoo.com/group/GrupoKylix/join GrupoKylix-subscribe@yahoogroups.com * FreePascal-es. Programación en Free Pascal (freepascal.org). http://espanol.groups.yahoo.com/group/freepascal-es Suscripción: http://espanol.groups.yahoo.com/group/freepascal-es/join freepascal-es-subscribe@yahoogroups.com * Desarrolladores-Software. Un lugar para tratar todos aquellos temas relacionados con el desarrollo de software y su comercialización, y para compartir experiencias en el ámbito laboral, profesional o comercial con otros. http://es.groups.yahoo.com/group/desarrolladores-software Suscripción: http://es.groups.yahoo.com/group/desarrolladores-software/join desarrolladores-software-subscribe@yahoogroups.com * Componentes. Un foro para buscar/recomendar componentes de software (componentes VCL y CLX, objetos ActiveX, librerías DLL, etc.), así como utilidades, tutoriales, información, etc. http://espanol.groups.yahoo.com/group/componentes Suscripción: http://espanol.groups.yahoo.com/group/componentes/join componentes-subscribe@yahoogroups.com ________________________________________________________________________ Delphi BUGS? Catch & Log every BUG showing Unit, Class, Method, Line #. Now with support for IntraWeb applications and Anti-Freeze feature. http://www.eurekalog.com/bannerclick.php?id=15 ________________________________________________________________________ 6. Delphi en la Red Por Dave Murray <irongut at vodafone dot net> Componentes, librerías y aplicaciones ===================================== Shareware / Comercial --------------------- * KylixDriver v1.1 - by ET Kimberliteware Ltd ($39 / $69 with source) KylixDriver is a RAD Kylix-oriented and integrated toolkit for PC hardware access. This toolkit can be efficiently used for writing Linux device drivers for ISA and PCI hardware. http://www.geocities.com/etkimberliteware/kylixdriver/index.html * Delphi SWF SDK (Standard) v1.4 - by FeatherySoft ($170) Object Pascal library for creating SWF files, without using of any external dynamic libraries. Released features: visual objects (shape, button, text, morphing shape, sprite); all types of filling (solid, gradient, image); device and embedding fonts; action commands; sound (events, streaming); video; any transition and transformation. http://www.delphiflash.com/ Freeware -------- * PGP Components for Delphi v4.0 - Michael in der Wiesche (with source) These sources provide a Delphi (2-7) direct interface to PGP requiring a preceding full installation of PGP 6.5.x, 7.x or 8.x. Features include: Encoding and decoding (encrypt/decrypt/sign/verify); Creating and verifying file detached signatures; Key management; Key generation (DH/DSS, RSA); X509 Certificate support; Keyserver functions. http://home.t-online.de/home/idw.doc/PGPcomp.htm * Synapse TCP/IP Library v.34 - by Lukas Gebauer (with source) TCP/IP library for Delphi, Kylix and FreePascal using blocking sockets. Contains simple non-visual objects for easy programming of command line utilities, visual projects, NT services, etc. New: FreePascal 1.9.4 support; Experimental Delphi 8 support; Optional support for ICONV library; New flexible FTP directory listing parser. http://synapse.ararat.cz/ * TPJDropFiles + TPJFormDropFiles v4.1 - by Peter Johnson (with source) Two components that catch files dragged and dropped from Explorer. TPJFormDropFiles provides a window that can contain other controls and catch any files dropped anywhere on it or child controls. TPJDropFiles is a non-visual component that subclasses a form to catch files that are dropped anywhere on the form. http://www.delphidabbler.com/software.php?id=dropfiles * Merkes' Tiny Hex Editor v1.5.0.10 - by Merkes Pages A free hex editor for binary files. Allows you to edit multiple files (mdi), disk sectors and virtual memory of other processes. Tiny Hex Editor is scriptable, lets you view data structures and has a plugin interface for third party extensions. http://www.mirkes.de/en/freeware/tinyhex.php Actualizaciones de productos Borland ------------------------------------ * Public Beta: Delphi 7.1 Update - Database Supplemental This is a public beta of database runtime files that address defects introduced in the Delphi 7.1 update. http://community.borland.com/article/0,1410,32492,00.html Artículos, trucos y consejos ============================ * Interview with Danny Thorpe about Diamondback Danny Thorpe talks about the next Delphi release. http://community.borland.com/article/0,1410,32708,00.html * Interview with Corbin Dunn about the Diamondback IDE Corbin Dunn, Software Engineer, Research and Development at Borland, talks about the Delphi IDE. http://community.borland.com/article/0,1410,32719,00.html * Borland "Diamondback" Data Services over .NET Remoting Details on data remoting with the BorCon preview edition of "Diamondback", the next version of Delphi. http://community.borland.com/article/0,1410,32718,00.html * Diamondback Preview License Update A license update that does not require an NDA or Beta Agreement. http://community.borland.com/article/0,1410,32717,00.html * BDNtv: Diamondback Preview of Data Remoting Demonstrates the ease of remoting data in a heterogeneous database environment with Diamondback. Includes setting up a local DataHub for SQL Server and InterBase then making it a remote server. And, creating a client that updates tables in both databases simply by calling one method of the DataHub component. Flash http://dotnet.borland.com/bdntv/delphi/dataremoting.html * BDNtv: Diamondback Sneak Peek A sneak peek at Diamondback's new IDE features and the new Delphi language feature for enumeration. The multiple personality IDE, refactoring for both .NET and Win32, Error Insight, Help Insight, the History View and more are shown. The for .. in .. do (foreach) enumerator syntax is also revealed. Flash http://dotnet.borland.com/bdntv/delphi/diamondbacksneakpeek.html * Delphi 8 and Microsoft .NET version 1.1 Service Pack 1 Delphi 8 has trouble compiling projects after .NET 1.1 Service Pack 1 has been installed. This article explains why and offers a workaround. Also affects the BorCon Diamondback preview release. http://community.borland.com/article/0,1410,32713,00.html * Borland CEO Touts Software Process Management InfoWorld Editor-at-Large Paul Krill spoke with Dale Fuller, president and CEO of Borland, at the BorCon conference last week about Borland's tools and ALM strategies and issues such as Java vs. .Net, Web services, and outsourcing. http://www.computerworld.com.au/index.php/id;954592276;fp;16;fpid;0 * Interview: Borland Aims for Better Code Better collaboration tools are the only route to superior software, argues Borland's David Intersimone. http://www.computing.co.uk/analysis/1157073 * DB2 Web Service Engines on Linux with Kylix 3: Part 1 - by Bob Swart Demonstrates how to build a SOAP Web service engine with Kylix 3, exposing the data from DB2 UDB database tables to the outside world. www-106.ibm.com/developerworks/db2/library/techarticle/dm-0406swart/ * DB2 Web Service Engines on Linux with Kylix 3: Part 2 - by Bob Swart Demonstrates how to build the user interface for a SOAP Web service engine with Kylix 3 on Linux, exposing the data from DB2 UDB database tables to the outside world. www-106.ibm.com/developerworks/db2/library/techarticle/dm-0407swart/ * Producing Dynamic Data-Entry Forms from DB2 Tables on Linux - B. Swart Uses Kylix 3 and dbExpress to analyze DB2 UDB database tables, fields (names and types) to allow the user to select a specific table, toggle which fields are shown, and dynamically view the output in both a datagrid and individual data-aware controls. www-106.ibm.com/developerworks/db2/library/techarticle/dm-0407swart2/ * Use Delphi Code to Create/Drop DB2 UDB Database Tables - by Bob Swart Examines the design and implementation of a Delphi application you can use to create or delete / drop tables inside IBM DB2 databases. It provides several examples of sending SQL DDL to the DB2 DBMS using Delphi and the cross-platform dbExpress data access technologies. www-106.ibm.com/developerworks/db2/library/techarticle/dm-0409swart/ * Work with IBM DB2 databases and SQL in Delphi for .NET - by Bob Swart Using Delphi code to create/drop DB2 database tables, focuses on the use of SQL queries to build simple and more complex SQL SELECT queries to end up with master-detail relations. Also examines SQL joins in detail, covering examples of inner joins, left/right outer joins, and the differences between them. www-106.ibm.com/developerworks/db2/library/techarticle/dm-0409swart1/ Tutoriales y capacitación ========================= * Free Web Seminars More than 10 on-demand Web seminars from Borland to help you optimize your software delivery. http://community.borland.com/article/0,1410,32698,00.html Noticias ======== * Borland Delphi 2005 Boosts Microsoft Windows Productivity Borland today announced Delphi 2005, previously codenamed Diamondback. Delphi 2005 combines Win32, .NET, Delphi and C# support all within one environment, significantly advances developer and team productivity and integrates with Borland's leading ALM solutions. http://www.tmcnet.com/usubmit/2004/Oct/1081925.htm * Software Delivery Strategy Gets Under Way at Borland Kicking off its Software Delivery Optimization campaign, Borland announced upgrades to CaliberRM requirements management and StarTeam configuration management software at BorCon. Also at the conference, the company demonstrated the next version of its Delphi IDE for Windows application development. http://sdtimes.com/news/111/story7.htm * Borland Scheme Aims to Maximise Business Value of Software Work Borland aims to turn software development into a simple business process with its Software Delivery Optimisation programme, announced at the BorCon 2004 earlier this month. http://www.computerweekly.com/articles/article.asp?liArticleID=133397 * Borland Moving Forward with SDO Borland took a new direction this week, with the announcement of Software Delivery Optimization. SDO will see three technology concepts delivered around Borland's ALM tools, as the company tries to lure C-level executives and vice presidents of application development with a message that software and the business should - and can - function as one. http://www.cbronline.com/article_feature.asp? guid=E077757F-B5CD-415C-8CCC-F10E19F6CC11 * (Turn and Face the Strain) Software Changes Borland this week stood atop its soapbox preaching the evils of software projects gone awry, with projects beset by issues such as last-minute changes and differing perspectives on the potential success of development efforts. To stem the tide of botched projects, Borland pitched its Software Delivery Optimization strategy. http://weblog.infoworld.com/techwatch/archives/000712.html * Borland Adds Project-Management Features To CaliberRM Borland announced CaliberRM 2005 this week, a product designed to help software project managers take some of the guesswork out of software development. New features include tools for estimating the cost, scheduling and staffing of a project before any code gets written. http://informationweek.com/story/showArticle.jhtml?articleID=47208433 * Business Management for Software Projects End users will be able to manage software projects like a business with Borland's Software Delivery Optimisation. It aims to enable users to manage software projects like a business, according to Boz Elloy, senior vice-president of software products. http://www.computerweekly.com/articles/article.asp?liArticleID=133333 * Borland challenges Visual Studio with Diamondback With the Diamondback release of its Delphi tool for Windows applications, Borland will challenge Microsoft while accommodating .Net, Win32 and Delphi development. Michael Swindell, Borland director of product management for developer tools, said with Diamondback Borland will compete with Microsoft's Visual Studio. http://www.computerweekly.com/articles/article.asp?liArticleID=133332 * Borland Touts Software Delivery Optimization At BorCon, Borland will reveal its software delivery optimization strategy, which leverages the company's ALM and developer products to ease software development and maintenance. The strategy ultimately will feature a bundle of the company's products code-named Themis, due out in the first half of 2005, which purports to provide a platform for integrated, repeatable development processes. http://www.javaworld.com/javaworld/jw-09-2004/jw-0913-iw-borland.html * Borland Outlines Vision for Transforming Software Development Today at BorCon 2004, Dale Fuller, President and CEO of Borland, presented Borland's vision and product strategy for transforming software development from an unpredictable art form into a more manageable and repeatable business process. Borland's vision for Software Delivery Optimization builds on the technical efficiencies of ALM, and incorporates the business processes and management capabilities companies seek to ensure business-to-IT alignment, and the delivery of quality software, on time, within budget, for maximum business value. http://home.businesswire.com/portal/site/google/index.jsp? ndmViewId=news_view&newsId=20040913005322&newsLang=en * Borland and NEC Team on Online Communications Borland and NEC will partner to develop adaptor software in order to rapidly integrate web-based applications with voice and video. This will target communications over IP telephony, IP video and conferencing over online applications. http://www.cbronline.com/article_news.asp? guid=2FE9728E-9350-49C4-BC59-2C25C62987BE * Borland Acquires Software Project Planning Tool Borland has acquired of Estimate Professional, a software project planning and estimation tool from Software Productivity Center. This newly acquired tool will be embedded within the next version of the Borland CaliberRM requirements management solution. http://www.infoworld.com/article/04/08/25/HNborlestimate_1.html * Borland Joins ECMA to Help Shape Standards for the .NET Framework Borland has been elected as a member of ECMA International. As a member of ECMA, Borland will help shape standards for the .NET Framework and the C# and C++ programming languages. http://home.businesswire.com/portal/site/google/index.jsp? ndmViewId=news_view&newsId=20040812005195&newsLang=en * Borland Developer News - .NET Edition, August 2004 Your source for Delphi and C# related news. Covering mostly .NET with some Win32 development. http://community.borland.com/article/0,1410,32491,00.html ________________________________________________________________________ Irongut's Delphi Pages Dedicada a la programación con Borland Delphi y Kylix. Tenemos artículos sobre programación, noticias Borland y Delphi, código fuente y componen- tes para usar en sus aplicaciones y más. http://www.paranoia.clara.net/ ________________________________________________________________________ ¡Tú puedes ayudarnos! Por favor danos una mano y ayúdanos a llegar a los 20.000 suscriptores en los próximos meses. Una forma en que puedes ayudarnos es enviando este enlace a tus amigos: http://www.latiumsoftware.com/es/pascal/index.php boletin-pascal-subscribe@gruposyahoo.com 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: http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium 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/p0051.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 Irongut's Delphi Pages http://www.paranoia.clara.net/ Copyright 2004 by Ernesto De Spirito + Dave Murray. All rights reserved. ________________________________________________________________________ |
Los ejemplos completos de código fuente de este número están disponibles para descargar.
![]() |
¿Errores? ¿Omisiones? ¿Comentarios? Por favor contáctanos!






