Boletín Pascal #40 - 30-OCT-2002
Índice
1. Unas palabras del editor
2. Ganchos (hooks) de Windows (o "¿Cómo trabajan esos programas espía?")
3. Codificar y decodificar Base64 (MIME)
4. Ejemplo de un servicio Windows, con un hilo
5. Capturando la salida de una aplicación de consola
6. Creación de Objetos - Introducción (0 de 3)
7. Inline Assembler en Delphi (IV) - Registros
8. Foros / listas de correo
9. Delphi en la Red
- Componentes, librerías y aplicaciones
. Freeware
- Artículos, trucos y consejos
- Tutoriales
- Otros enlaces
________________________________________________________________________
¿Necesitas alojamiento para tu sitio web? http://www.tecnosoftonline.com
________________________________________________________________________
1. Unas palabras del editor
Tenía la intención de publicar esta edición hace un par de semanas como
había prometido, pero en circunstancias muy particulares un nuevo virus
no identificado entró en mi PC y sobrescribió el primer cilindro de mi
disco duro (el sector de arranque maestro, y el sector de arranque y
parte de la tabla de asignación de archivos -FAT- de la primera
partición), así que imagínense... (Todavía estoy instalando todos los
componentes Delphi que solía tener). Lamento mucho la demora.
Para disipar dudas, los nombres de dos de nuestros foros/listas de
correo (grupos en Yahoo! Grupos) han cambiado:
delphi-es --> delphi-intermedio
Delphi --> delphi-avanzado
Por lo tanto, han cambiado todas sus direcciones, por lo que se deberán
realizar las sustituciones correspondientes. Por ejemplo, para publicar
mensajes en el foro Delphi de nivel intermedio:
Antes: <delphi-intermedio@...>
Ahora: <delphi-intermedio@...>
Quisiera agradecer a los autores que contribuyeron artículos para este
número, y me complace entregarles los siguientes premios:
* Florin Sabau ("Ganchos (hooks) de Windows")
· llPDFLib v1.1 - por llionsoft, Shareware ($70, $280 con fuentes)
llPDFLib en una biblioteca en puro Object Pascal para crear documentos
PDF. No usa ninguna DLL ni software externo de terceras partes para
generar ficheros PDF. La librería consiste del componente TPDFDocument
con propiedades y métodos como los del TPrinter de Delphi, pero
diseñado para generar un fichero PDF.
http://www.llion.net/
* Jochen Fromm ("Capturando la salida de una aplicación de consola")
· Greatis Form Designer v3.4 - por Greatis Software, Shareware ($49.95)
Es un diseñador de formularios en tiempo de ejecución que le permite
mover y cambiar el tamaño de cualquier control de su formulario. No
necesita preparar su formulario para usar Form Designer. Simplemente
suelte el componente TFormDesigner en cualquier formulario, establezca
la propiedad Active en True y ¡disfrute! Para Delphi 4-7 y BCB 3-6.
http://www.greatis.com/formdes.htm
* Kim Sandell ("Ejemplo de un servicio Windows, con un hilo")
· Developer Information Library (DIL) CD - por UK Borland User Group
Más de 17.000 trucos, consejos, FAQs y artículos técnicos · Parches y
actualizaciones de las herramientas Borland · Más de 4.000 componentes
y herramientas · Más de 4.000 bitmaps listos para usar con otros
20.000 comprimidos · Más de 350 JavaScripts listos para usar · Juego
completo de HOWTOs de Linux · y mucho más...
http://www.richplum.co.uk/html/dil.asp
Para la próxima edición, tenemos disponible el siguiente premio para el
autor de uno de los artículos contribuidos al boletín:
* TSDBGridFooter v2.0 por Jovan Sedlan, Shareware ($74.50)
Este componente es una poderosa herramienta que provee cálculos auto-
máticos para su DBGrid y muestra información en un pie configurable
debajo de la grilla. Ha sido diseñado para trabajar con TSDBGrid
(también incluido), aunque puede usarse con cualquier descendiente de
TCustomDBGrid.
http://www.sedlan.com/dbgrid_footer.php
Cambiando de tema, he sido lanzado el primer update pack para Delphi 7:
* Delphi 7 Update Pack 1
http://community.borland.com/article/0,1410,29209,00.html
Espero que disfruten esta edición.
Saludos,
Ernesto D'Spirito
boletin-pascal-owner@...
__________________
Colaboraron en esta edición: Dave Murray y Charl Linssen
________________________________________________________________________
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. Ganchos (hooks) de Windows (o "¿Cómo trabajan esos programas espía?")
Por Florin Sabau <aaa111@...>
¿Alguna vez se preguntó cómo obtienen esos programas espía todo lo que
se escribe en la computadora en la que están instalados? Bueno, no sé
exactamente como ELLOS lo hacen :), pero les voy a mostrar una forma
posible usando ganchos (hooks) de Windows. Este artículo le mostrará
cómo Ud. puede crear un programa que "escuche" cierta combinación de
teclas y cuando sea activado que haga algo significativo (abrir la
bandeja de CDROM).
Técnicamente, un gancho es simplemente otra subrutina ("procedimiento
gancho" o "procedimiento de gancho") que "se me mete en el camino" del
mecanismo normal de administración de mensajes de Windows. El proce-
dimiento gancho puede ser instalado en el sistema de modo que reciba
ciertos mensajes de Windows ANTES que estos sean despachados a su
procedimiento de ventana asignado. Windows contiene muchos tipos
diferentes de ganchos; cada tipo provee acceso a aspectos diferentes del
mecanismo de administración de mensajes de manejo de Windows. He aquí
algunos de ellos (en realidad las constantes que los identifican han
sido tomadas de windows.pas) con una breve descripción:
WH_KEYBOARD: Instala un procedimiento gancho que monitorea los
mensajes de teclado. Usaremos este en nuestro programa.
WH_MOUSE: Instala un procedimiento gancho que monitorea los
mensajes del ratón.
WH_CBT: Instala un procedimiento gancho que recibe notificaciones
útiles para una aplicación de entrenamiento basado en computadora
(CBT, computer-based training)
WH_JOURNALRECORD: Instala un procedimiento gancho que registra
mensajes de entrada en la cola de mensajes del sistema. Este
gancho es útil para registrar macros.
WH_JOURNALPLAYBACK: Instala un procedimiento gancho que emite
mensajes previamente registrados por un procedimiento gancho
WH_JOURNALRECORD.
Puesto que más de un programa puede instalar un gancho en el sistema
al mismo tiempo, Windows mantiene internamente una "cadena de ganchos"
(hook chain), que es simplemente una lista de punteros a los procedi-
mientos ganchos que tiene instalados. Cuando ocurre un mensaje en el
sistema, Windows primero se lo pasa a cada procedimiento en la cadena
de ganchos, uno tras otro. Luego, si el mensaje no fue "bloqueado" por
ninguno de los procedimientos gancho, Windows lo despacha al proce-
dimiento de ventana designado.
Una cosa más antes de entrar en la siguiente sección: los ganchos
pueden ser clasificados de otra forma. Hay ganchos de sistema (globales)
que reciben mensajes para todos los hilos en el sistema, y ganchos
específicos de un hilo (locales), que reciben mensajes sólo designados
para un hilo individual. Puesto que un procedimiento de gancho global
puede ser llamado en el contexto de cualquier aplicación (para capturar
mensajes de todas las aplicaciones), debe estar localizado en una DLL
(Dynamic Link Library). Esta restricción no se aplica a los ganchos
específicos de un hilo, así que el procedimiento gancho puede estar
ubicado en cualquier parte de la aplicación que es dueña del hilo a
ser enganchado
En este artículo trataremos solamente con ganchos globales.
Instalando un procedimiento gancho WH_KEYBOARD en la cadena de ganchos
----------------------------------------------------------------------
La API de ganchos contiene 3 funciones muy importantes: SetWindowsHookEx
(que instala un procedimiento gancho), UnhookWindowsHookEx (que desins-
tala un procedimiento gancho) y CallNextHookEx (que llama al siguiente
procedimiento gancho en la cadena de ganchos). Los parámetros que toman
estas funciones se muestran abajo (de windows.pas):
function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod:
HINST; dwThreadId: DWORD): HHOOK; stdcall;
"idHook": tipo de gancho a instalar (por ejemplo WH_KEYBOARD);
"lpfn": puntero la procedimiento gancho al que deben enviarse los
mensajes;
"hmod": manejador (handle) de la DLL que instala el gancho, general-
mente hInstance para ganchos globales o 0 para ganchos locales;
"HINST": identificador del hilo al que se asociará el gancho. Si es
0, el procedimiento de gancho se asociará con todos los hilos.
Devuelve un valor usado para identificar el gancho.
function UnhookWindowsHookEx(hhk: HHOOK): BOOL; stdcall;
"hhk": identificador del gancho a desinstalar.
Devuelve True si tiene éxito, False si fracasa.
function CallNextHookEx(hhk: HHOOK; nCode: Integer; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
"hhk": identificador del gancho actual;
"nCode","wParam","lParam": parámetros que deberían ser enviados al
siguiente procedimiento en la cadena de ganchos.
Devuelve el valor retornado por el siguiente procedimiento en la
cadena. Más adelante veremos en el ejemplo lo que significa.
El procedimiento gancho
-----------------------
El procedimiento gancho para un gancho de teclado tiene un formato
estándar que y para que el gancho funcione, el programador debe proveer
este formato exacto:
function HookProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM):
LRESULT; stdcall;
"nCode":
= HC_ACTION - los parámetros wParam y lParam contiene información
acerca del mensaje de teclado.
= HC_NOREMOVE - los parámetros wParam y lParam contiene información
acerca del mensaje de teclado, y el mensaje de teclado no ha sido
removido de la cola de mensajes (una aplicación llamó la función
PeekMessage especificando la bandera PM_NOREMOVE).
"wParam": especifica el código de tecla virtual de la tecla que
generó el mensaje (por ejemplo VK_F9 para la tecla de función F9).
"lParam": especifica información adicional (como la cantidad de
repeticiones, el código de exploración, ...); no usado en
nuestro programa (ver Win32SDK para detalles).
HookProc debería devolver un valor no nulo para impedir que Windows pase
el mensaje al resto de la cadena de ganchos o al procedimiento de
ventana destino, o cero para permitir que Windows pase el mensaje al
procedimiento de ventana destino.
Ejemplo
-------
Este ejemplo crea un gancho global sobre el teclado y cuando ocurre una
cierta combinación de teclas realiza algo significativo:
WinKey + F9: Muestra la ventana principal si está oculta;
WinKey + F10: Eyecta la bandeja de CDROM;
WinKey + F12: Termina la aplicación.
La comunicación entre la DLL que implementa el gancho y la aplicación se
realiza con la función API SendMessage, la que envía un mensaje HOOK_MSG
(definido en constants.inc) a la aplicación principal, con el comando
(SHOW, EJECT, QUIT) en wParam (ver más abajo).
Dado que no queremos ser molestados por el formulario siendo mostrado
todo el tiempo, al hacer clic en el botón Minimizar se oculta (incluso
de la lista de tareas), pero puede ser mostrado más tarde con la
combinación Winkey+F9.
Para el código fuente completo ver el archivo adjunto. Aquí sólo
mostraré las partes más importantes de la aplicación:
1. HookDll.dpr
function KeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM):
LRESULT; stdcall;
var
Handled: Boolean;
KeyState: TKeyboardState;
Han: HWND;
function WinKeyPressed: boolean;
begin
Result := (KeyState[VK_LWIN] and $80 <> 0)
or (KeyState[VK_RWIN] and $80 <> 0);
end;
begin
Handled := False;
Result := 1;
if nCode = HC_ACTION then
begin
GetKeyboardState(KeyState);
Han:=FindWindow('TForm1', APP_CAPTION);
if (IsWindow(Han)) and (KeyState[wParam] and $80 <> 0)
and WinKeyPressed then
begin
Handled := True;
case wParam of
VK_F9: SendMessage(Han,HOOK_MSG, APP_SHOW, 0);
VK_F10: SendMessage(Han,HOOK_MSG, EJECT_CDROM, 0);
VK_F12: SendMessage(Han,HOOK_MSG, APP_QUIT, 0);
else
Handled := False;
end;
end;
end;
if not Handled then
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;
Al entrar el procedimiento gancho comprobamos la bandera HC_ACTION en
nCode (así sabemos si tenemos una acción de teclado), y luego guardamos
en KeyState el estado (si está presionada o levantada, invertida con
CapsLock, etc.) para todas las teclas virtuales usando GetKeyboardState.
También encontramos el manejador de ventana (window handle) de la apli-
cación principal a la que enviaremos los comandos (APP_SHOW, EJECTCDROM
y APP_QUIT). Si la encontramos (IsWindow(han)) y tenemos un mensaje de
teclado PRESSED (KeyState[wParam] and $80 <> 0) y la tecla Winkey
también está presionada, entonces enviamos un comando al formulario
principal de acuerdo a la tecla que ha sido presionada. Si no podemos
manejar el mensaje de teclado (Handled=False), entonces se lo pasamos
al siguiente gancho en la cadena.
2. Hooks.Dpr y Unit1.pas
La función más importante aquí es HOOK_MSG_PROC, que es la que recibe
los comandos enviados desde la DLL:
type TForm1=class(TForm)
...
procedure HOOK_MSG_PROC(var Msg: TMessage); message HOOK_MSG;
...
end;
...
procedure TForm1.HOOK_MSG_PROC(var Msg: TMessage);
begin
case Msg.WParam of
APP_SHOW:
begin
Application.ShowMainForm := True;
Visible:=True;
end;
EJECT_CDROM: mciSendString('set CDAudio door open',nil,0,0);
APP_QUIT: Close;
end;
end;
La característica "ocultar al minimizar" se logra capturando el mensaje
WM_SYSCOMMAND que envía Windows cuando ocurre un mensaje de sistema
(como cerrar, minimizar, maximizar). Entonces ocultamos el formulario si
tenemos un comando SC_MINIMIZE:
procedure TForm1.OnMinimize(var Msg: TMessage);
begin
if Msg.WParam = SC_MINIMIZE then
begin
if not IsHookInstalled then
begin
ShowMessage('Primero instale el gancho o no'#13#10 +
'podrá acceder al programa');
Exit;
end;
Application.ShowMainForm := False;
Visible := False;
end else
Inherited;
end;
To hide the program from the tasks list we use:
function RegisterServiceProcess(dwProcessID, dwType: integer):
integer; stdcall; external 'KERNEL32.DLL';
NOTE: The above hiding technique works only on Windows 9x.
Of course this is only a very simple application of hooks, but the
possibilities are numerous. A small spy program, perhaps! :) Anyway, if
you have any questions about this article feel free to send me a note
to <aaa111@...>.
__________________
NOTA: Se adjunta el código fuente completo.
________________________________________________________________________
¿Cuándo fue la última vez que votó por el Boletín Pascal? Por favor
apoye esta iniciativa votándonos en "The Programming Top 100!"
http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium
________________________________________________________________________
3. Codificar y decodificar Base64 (MIME)
Por Daniel Wischnewski
delphi3000@...
Escribí la siguiente unidad para reemplazar los componentes INDY
TIdEncoderMIME y TIdDecoderMIME. Este "Codec" se usa principalmente en
software de email.
La razón principal fue la carencia de velocidad de estos componentes.
La segunda razón fue que son componentes y consiguientemente requieran
la VCL - una pesada carga adicional para sistemas no-VCL.
Ambas rutinas están escritas en ensamblador y sobrecargadas en
distintas versiones para fácil acceso.
Estoy seguro que muchos de ustedes pueden incrementar la velocidad aún
más. Por favor háganmelo saber (en inglés). Gracias.
__________________
NOTA: Se adjunta el código fuente completo.
________________________________________________________________________
AnyShape Transpack v2.0 - por MindBlast Software (DELPHI + KYLIX)
¿Cansado de las aburridas ventanas rectangulares? AnyShape Transpack es
un componente multiplataforma (Delphi/Kylix) que facilita la creación de
ventanas transparentes y de formas extrañas con edición WYSIWYG, vista
previa en tiempo de diseño, arrastre automático, verdaderos formularios
en primer plano, combinación de regiones, y más. Shareware ($30).
http://www.mindblastsoftware.com/?page=transpack&ref=PascalNL
________________________________________________________________________
4. Ejemplo de un servicio Windows, con un hilo
Por Kim Sandell - kim.sandell@...
www.nsftele.com
Delphi 5 y 6 tienen una plantilla de proyectos para servicios, pero está
incompleta. Este ejemplo se construye sobre esa plantilla y completa el
servicio. También muestra cómo comenzar un hilo que emite un pitido cada
dos segundos. Puede usarlo como base al desarrollar servidores y
servicios.
El ejemplo ha sido diseñada para Delphi 5-7 y funciona en Windows
NT/2000/XP. La opción de inicio del servicio ha sido establecida a
MANUAL. Si quiere hacer un servicio que se inicie automáticamente con
Windows, entonces necesita cambias esto. ¡SEA CUIDADOSO! Si su aplica-
ción se cuelga cuando está corriendo como un servicio, NO HAY FORMA de
terminarla.
__________________
NOTA: Se adjunta el código fuente completo.
__________________
Author's Profile Summary:
Name : Kim Sandell Born : 1973 Helsinki, Finland
Skills : VB, Delphi (1-6), C/C++, SQL, Interbase, IP Networks.
Special areas: System Service, Protocols, Security (VPN),
Authentication, Servers (Deamons) + More
Company: NSF Telecom Ab Title: CTO
WWW : http://www.nsftele.com EMail: kim.sandell@...
________________________________________________________________________
5. Capturando la salida de una aplicación de consola
Por Jochen Fromm <Jochen.Fromm@...>
¿Cómo ejecuta una aplicación de consola o DOS y captura su salida
mientras está corriendo? ¿Por ejemplo, cómo captura la salida del
comando FileCompare (FC)?
Ya hay dos artículos acerca de este problema,
http://www.delphi3000.com/articles/article_2112.asp
http://www.delphi3000.com/articles/article_2298.asp
pero obtienen la salida cuando el proceso ha finalizado. También hay dos
artículos de Microsoft acerca de redireccionamiento de aplicaciones DOS:
* Microsoft Knowledge Base Article - Q190351
HOWTO: Spawn Console Processes with Redirected Standard Handles
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q190351
* Microsoft Knowledge Base Article - Q150956
INFO: Redirection Issues on Windows 95 MS-DOS Applications
http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q150956
La idea básica es ejecutar la aplicación de consola con CreateProcess y
redirigir la salida con tuberías (pipes), permitiendo al proceso
llamador acceder a la salida de la aplicación de consola.
Es importante capturar la salida mientras el proceso se encuentra aún en
ejecución. Si la tubería de salida se bloquea por un sobreflujo, la
nueva información no podrá ser escrita desde la aplicación de consola a
la tubería de salida, y el programa se detendrá. En este caso tenemos
situación de "abrazo mortal" (deadlock): el usuario (proceso padre) está
esperando que el proceso hijo termine, y el proceso hijo está esperando
que el padre limpie el buffer.
__________________
NOTA: Se adjunta el código fuente completo.
________________________________________________________________________
6. Creación de Objetos - Introducción (0 de 3)
Por Oscar A. Esqueda Cortes
Antecedentes OOP
================
Antes de entrar a la programación de componentes, debemos recordar
conceptos básicos de la programación orientada a objetos.
Nota: En el boletín 36 se encuentra un articulo mío sobre POO (OOP por
sus siglas en inglés), más extenso que esta parte. Considere leerlo.
* ¿Que es POO?
Básicamente no es un lenguaje, y tampoco es una técnica para hacer
ventanitas y usar el Mouse, sino más bien es un conjunto de técnicas y
metodologías (filosofías si quieren decirlo) para incrementar nuestra
productividad al desarrollar software.
El aumento de productividad significa tanto crear nuestros programas
en poco tiempo, como también poder darles mantenimiento de una manera
más rápida y eficaz.
Las partes vitales de la POO son las clases y objetos, pero la piedra
filosofal son: la Herencia, el Polimorfismo y la Encapsulación.
* Definición de Clase
Una clase, es simplemente una abstracción que hacemos de nuestra
experiencia sensible.
* Definición de Objeto
Un objeto es un conjunto de datos y métodos que se comporta de acuerdo
a las reglas de su clase.
* Herencia
Crear una clase a partir de otra, pero tomando todas sus caracte-
rísticas y funcionamiento es heredar una clase.
* Encapsulación
El concepto de encapsulación consiste en implementar el comportamiento
de una clase dentro de ella, sin que terceros ojos sepan como
funciona.
* Polimorfismo
La idea del polimorfismo consiste en crear una clase base de la cual
dependerán varias clases, por herencia claro, pero el comportamiento
de una clase hija no tiene porque ser igual a otra, aunque tengan los
mismos métodos.
* Sobrecarga
La sobrecarga no es estrictamente una característica OOP, pero la
mencionamos aquí porque muchas veces se la asocia erróneamente con
ella (posiblemente debido a su casi simultaneidad de aparición y
porque es en la OOP donde más se observa se uso). La sobrecarga nos
brinda versatilidad al permitirnos declarar más de una vez un mismo
procedimiento o función con distintos parámetros (y opcionalmente
distinto tipo de valor de retorno, en el caso de las funciones).
Arquitectura VCL
================
La herencia es la piedra angular de la VCL de Delphi. Todo está basado
en Clases y Objetos, desde la primera clase TObject que está declarada
en la unidad System.pas. De esta clase base derivan todas las demás
clases y componentes/controles con los que jugamos en tiempo de diseño.
Diagrama de la VCL
------------------
Este es un diagrama muy simple y resumido, pero cumple los requisitos
que necesitamos.
(Por lo regular al comprar el Delphi, se entrega un mapa de las clases,
péguenlo en la pared junto al poster de Cameron Díaz o quien tengan en
la pared, JE).
---------
| TOBJECT |
---------
|
|------------------|
----------- -------------
| EXCEPTION | | TPERSISTENT |
----------- -------------
|
------------
| TCOMPONENT |
------------
|
----------
| TCONTROL |
----------
|
|----------------------|
----------------- -------------
| TGRAPHICCONTROL | | TWINCONTROL |
----------------- -------------
Veamos un poco de las características de las clases principales de
Delphi.
TObject
-------
Esta clase es la clase principal en la VCL de Delphi. Todas las clases
(u objetos) derivan de esta clase principal. Esta clase implementa
información RTTI o de clases para identificación de las mismas. Es
decir, mantiene métodos que identifican a cada clase. Por ejemplo vea
algunos de ellos:
- ClassInfo : Puntero a información del objeto.
- ClassName : Nombre de la clase devuelta como string (útil para
comparaciones).
- ClassParent: Clase de la que heredamos el objeto, nil si es TObject.
- ClassType : Clase actual, devuelve un tipo TClass.
Esta clase base solamente da opciones de identificación de clases,
construcción y destrucción de objetos básica. Para mas información
busque TObject en la ayuda de Delphi.
Los objetos que derivan de TObject son Exception, TIniFile y TRegistry
entre otros, además de la siguiente clase en la escala jerárquica:
TPersistent.
TPersistent
-----------
Como su nombre lo indica es útil para mantenerse, es decir, todas las
clases que deseemos que se guarden en el archivo DFM de una forma deben
heredar de TPersistent. Básicamente, contiene métodos para guardar
información de la clase en Streams o conjunto de datos permanentes.
Útil para objetos dentro de objetos, por ejemplo las propiedades
HorzScrollBar y VertScrollBar de un TForm o las columnas de un DBGrid.
Los objetos que derivan de esta clase son por ejemplo las colecciones
como las columnas de un DBGrid o de un ListView.
TComponent
----------
La clase TObject es la principal de toda la VCL, pero la TComponent es
la principal para el diseño visual en Delphi. Todos los componentes no
visuales (TTable, TDatasource, TTimer, TActionList) heredan de esta
clase. Si vamos a crear un componente no visual, lo tendremos que
derivar de TComponent.
PROPIEDAD
La clase TComponent introduce un concepto de propiedad, es decir, cuando
uno crea componentes, digamos TLabel u otro, la llamada a su constructor
es así:
:= TLabel.Create(Form1);
donde decimos que el propietario del objeto es Form1. O sea que al
destruirse Form1, se encarga de destruir sus objetos designados. Esto
es útil para mantener una forma limpia de destrucción de objetos y
liberación de memoria.
NOTA: Aun así, si construye objetos en tiempo de ejecución, use un
Try/Finally para destruirlos.
Hay propiedades para el control de sus objetos designados, como:
- ComponentCount: Total de componentes dependientes.
- Componentes : Arreglo iniciando de 0 de los componentes designados
al TComponent.
EJEMPLO
Viene un ejemplo llamado lstComp, para Delphi 5, donde muestra cómo
funciona esto. Es una lista que se llena con todos los componentes de
la forma. Además introduce el concepto de nombrado de objetos con su
propiedad Name, concepto que no tienen las clases anteriores, para
identificar a los componentes y sus descendientes.
Existen dos propiedades que merecen un poco de tiempo:
- ComponentState, que indica el estado del componente. Veamos algunas de
sus propiedades.
· csDesigning : El componente esta en tiempo de diseño.
· csDestroying: El componente se esta destruyendo.
- ComponentStyle, para asignar el comportamiento del componente, estos
son algunos de sus valores:
· csInheritable: puede ser heredado.
Para la creación de componentes es útil el saber si estamos en tiempo de
diseño o alguna otra situación.
Los componentes derivados de TComponent son: TDatasource, TTable,
TQuery, TTimer, y todos los no visuales que sólo se ven en diseño.
TControl
--------
Los componentes visuales derivan de TControl. Es raro que creen un
componente de éste; lo mejor es de sus descendientes. La función de éste
es definir la finalidad del componente Padre. A excepción del TForm,
todo componente tiene un padre, que es su componente contenedor, los
componentes como TPanel y TGroupBox entre otros, permiten agrupar
componentes dentro de ellos.
Además, el TControl introduce propiedades que controlan su visibilidad,
como Top, Left, Width, Height y Visible, así como Fonts, Color, Anchors,
entre otras, pero hay dos que merece la pena verlas con calma (parecidas
al TComponent):
- ControlState: indica el estado del control, veamos algunos de sus
valores:
· csCreating : El control se esta creando.
· csLButtonDown : se capturo un evento de botón de Mouse.
· csFocusing : el control trata de obtener el foco.
- ControlStyle: la cual determina las características del control, el
cual puede tener los siguientes valores:
· csAcceptsControls : es un control contenedor.
· csCaptureMouse : captura los eventos del Mouse.
· csSetCaption : tiene titulo.
· csMenuEvents : responde a eventos del menú del sistema.
· csFixedWidth y csFixedHeight : Tamaño fijo.
De nuevo pido al lector que se de una vuelta por la ayuda en el tema
"TControl, ControlStyle" para mas información.
Estas dos son útiles en el caso de querer comprobar el estado del
control o el estilo del control nuevo, si es contenedor, aceptar
controles de Mouse u otros casos.
No hay objetos derivados de TControl que se usen directamente en la VCL.
Más bien se heredan dos para los cuales se crean todos los objetos
visuales: TGraphicControl y TWinControl.
TGraphicControl
---------------
Esta clase es la base para todos los objetos visuales que no reciben el
foco. Implementa una clase llamada TCanvas en una propiedad llamada
Canvas, la cual permite procedimientos de dibujo en ella.
Los objetos que reciben un foco, deben llevar un manejador o Handle de
ventana y requiere más recursos. Los GraphicControls usan los recursos
de su Parent para dibujarse y no gastar más recursos de los que
requeriría, por lo que un descendiente de TGraphicControl no tiene
Handle o manejador y no puede ser manipulado con API's, pero sí puede
recibir mensajes Windows o notificaciones. No recibe mensajes del
teclado sino sólo del Mouse, por el motivo de no recibir el foco.
Los controles que derivan de esta clase son: TBevel, TCustomLabel
(TLabel y TDBText), TImage, TPaintBox, TShape, TSpeedButton, TSplitter
y TToolButton (colección de TToolBar).
TWinControl
-----------
Los objetos TWinControl (TWidgetControl en Linux) encapsulan los objetos
Windows. En realidad todos los controles visuales Windows son Ventanas,
desde un punto de vista técnico, ya que todos tienen un Handle o
Manejador, que es un identificador único en todo el sistema operativo,
útil para las funciones API de Windows.
Las características que implementa la clase son:
- Poder recibir el foco y por lo tanto manejar eventos relacionados
con el teclado.
- Ser contenedor de otros controles, vea las propiedades mas útiles.
Las propiedades más útiles son:
- ControlsCount : Total de componentes dentro del contenedor.
- Controls : Arreglo iniciando de 0 con los componentes
contenidos, devuelve un TControl.
- Handle : Manejador de ventana asignado por Windows.
- ParentWindow : Manejador de su Parent (padre).
- TabOrder y TabStop: Propiedades para el tab.
EJEMPLO:
El ejemplo que viene en el archivo adjunto muestra un TTreeView en el
cual se llenan, en el orden que están, los Parent (padre) de los
controles visuales.
En realidad, sólo pocos objetos son creados directamente de TWinControl
(TAnimate, THeaderControl, TProgressBar, TScrollBar), pero todos los
controles Windows, heredan de esta.
Las clases TCustomX
-------------------
Ahora, casi todos los controles derivan de alguna clase base que inicia
con TCustom, por ejemplo:
TLabel = Class(TCustomLabel)
TEdit = Class(TCustomEdit)
TForm = Class(TCustomForm)
Estas clases fueron creadas para poder crear componentes nuevos a partir
de esos, es decir. Las clases como TLabel no traen nada distinto a la
clase TCustomLabel, solo el hecho de publicar ciertas propiedades y
métodos, para eso están las clases base TCustom.
Veamos la clase TCustomLabel (tomada del código VCL de Delphi 6):
TCustomLabel = class(TGraphicControl)
private
FFocusControl: TWinControl;
FAlignment: TAlignment;
FAutoSize: Boolean;
FLayout: TTextLayout;
FWordWrap: Boolean;
FShowAccelChar: Boolean;
FOnMouseLeave: TNotifyEvent;
FOnMouseEnter: TNotifyEvent;
function GetTransparent: Boolean;
procedure SetAlignment(Value: TAlignment);
procedure SetFocusControl(Value: TWinControl);
procedure SetShowAccelChar(Value: Boolean);
procedure SetTransparent(Value: Boolean);
procedure SetLayout(Value: TTextLayout);
procedure SetWordWrap(Value: Boolean);
procedure CMTextChanged(var Message: TMessage);
message CM TEXTCHANGED;
procedure CMFontChanged(var Message: TMessage);
message CM FONTCHANGED;
procedure CMDialogChar(var Message: TCMDialogChar);
message CM DIALOGCHAR;
procedure CMMouseEnter(var Message: TMessage);
message CM MOUSEENTER;
procedure CMMouseLeave(var Message: TMessage);
message CM MOUSELEAVE;
protected
procedure AdjustBounds; dynamic;
procedure DoDrawText(var Rect: TRect; Flags: Longint); dynamic;
function GetLabelText: string; virtual;
procedure Loaded; override;
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
procedure Paint; override;
procedure SetAutoSize(Value: Boolean); override;
property Alignment: TAlignment read FAlignment write SetAlignment
default taLeftJustify;
property AutoSize: Boolean read FAutoSize write SetAutoSize
default True;
property FocusControl: TWinControl read FFocusControl
write SetFocusControl;
property ShowAccelChar: Boolean read FShowAccelChar
write SetShowAccelChar default True;
property Transparent: Boolean read GetTransparent
write SetTransparent default False;
property Layout: TTextLayout read FLayout write SetLayout
default tlTop;
property WordWrap: Boolean read FWordWrap write SetWordWrap
default False;
public
constructor Create(AOwner: TComponent); override;
property Caption;
property Canvas;
property OnMouseEnter: TNotifyEvent read FOnMouseEnter
write FOnMouseEnter;
property OnMouseLeave: TNotifyEvent read FOnMouseLeave
write FOnMouseLeave;
end;
Todas las propiedades están en Protected y la clase TLabel, lo que
hace es pasarlas a published, esto lo veremos en la siguiente parte
de este articulo.
TLabel = class(TCustomLabel)
published
property Align;
property Alignment;
property Anchors;
property AutoSize;
property BiDiMode;
property Caption;
property Color;
property Constraints;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property FocusControl;
property Font;
property ParentBiDiMode;
property ParentColor;
property ParentFont;
property ParentShowHint;
property PopupMenu;
property ShowAccelChar;
property ShowHint;
property Transparent;
property Layout;
property Visible;
property WordWrap;
property OnClick;
property OnContextPopup;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMouseEnter;
property OnMouseLeave;
property OnStartDock;
property OnStartDrag;
end;
Reglas para escribir componentes
--------------------------------
Para terminar, les paso unos cuantos tips para escribir componentes, los
tome "prestados" de Marco Cantu (http://www.marcocantu.com, ya le tome
esto de su libro, pues les paso la dirección de su pagina), espero sirva
de orientación.
* Estudiar el Lenguaje de Pascal Orientado a Objetos: Herencia,
Sobrescritura y sobrecarga de métodos, la diferencia entra las partes
publicas y privadas de una clase y la diferencia entra propiedades y
eventos. (NOTA: lean mi artículo de OOP y también varios de estos
conceptos los veremos en el próximo artículo).
* Estudiar la jerarquía de las estructuras VCL (Nota: aquí tienen una
pista en este artículo).
* Crear componentes sencillos, imitar a otros componentes y evitar
dependencias.
* Usar excepciones. (bloques, try except, finally y el uso del raise con
excepciones personalizadas).
* Identificar a cada componente con un mapa de bits para la paleta de
componentes, además de nombrar lo mas significativamente el
componente, ponerle una notación al principio para identificar de
quien son y diferenciarlos de otros ya existente (ToecLabel, por
ejemplo).
* Prepararnos para escribir código real, es decir, al escribir
componentes por lo regular no se usara el aspecto Visual.
Estas son reglas muy básicas, mas bien para orientarnos, en las
siguientes ediciones haremos componentes, este fue el único artículo
teórico, en el siguiente haremos 5 componentes y explicaremos un poco
de ellos, además de darles el código fuente de ellos claro.
Hasta la próxima
Ing. Oscar A. Esqueda Cortes
Information Technology Consulting
http://www14.brinkster.com/itcmx
oesqueda@...
________________________________________________________________________
7. Inline Assembler en Delphi (IV) - Registros
Por Ernesto D'Spirito <edspirito@...>
Pasando registros como parámetros
=================================
Al igual que los arreglos estáticos, los registros se pasan internamente
como punteros a los datos, independientemente de si el parámetro se pasa
por valor o por referencia (ya sea como "var" o como "const").
Dadas las siguientes declaraciones...
type
TRecord = record
Id: integer;
Name: string;
end;
var
a, b: TRecord;
procedure InitRecord(var r: TRecord; Id: integer; const Name: string);
begin
r.Id := Id;
r.Name := Name;
end;
...una llamada al procedimiento InitRecord en ensamblador sería así:
// En Object Pascal:
// InitRecord(a, n, s);
// En Inline Assembler:
asm
lea eax, a // EAX := @a; // 1er parámetro en EAX
mov edx, n // EDX := n; // 2do parámetro en EDX
mov ecx, s // ECX := s; // 3er parámetro en ECX
call InitRecord // InitRecord;
end;
Accediendo los campos de un registro
====================================
Los campos de un registro se encuentran a un cierto desplazamiento de la
dirección del registro (la dirección del primer campo). En el ejemplo,
asumiendo que tenemos la dirección de un registro de tipo TRecord en el
registro EAX, el campo Id está ubicado en [EAX+0] (o simplemente [EAX]),
y el campo Name está ubicado en [EAX+4], pero normalmente no escribimos
"números mágicos". En vez de ello, para producir un código autoexplica-
tivo y mantenible tenemos cinco formas alternativas:
mov edx, [eax + TRecord.Name]
mov edx, (TRecord PTR [eax]).Name
mov edx, (TRecord [eax]).Name
mov edx, TRecord[eax].Name
mov edx, [eax].TRecord.Name
Las cinco sentencias de arriba se ensamblarían como:
mov edx, [eax + 4]
En vez de un registro (como EAX), las sintaxis también se aplican a
nombres de variables locales.
Se puede inferir de la primer sintaxis que en inline assembler la
expresión TipoRegistro.Campo se evalúa en tiempo de compilación como una
constante representando el desplazamiento en el que se encuentra el
Campo en el TipoRegistro. Por ejemplo, la siguiente sentencia es válida:
mov ecx, TRecord.Name // mov ecx, 4
Volviendo al tema, el procedimiento InitRecord (introducido arriba) se
puede implementar en ensamblador de esta forma:
procedure InitRecord(var r: TRecord; Id: integer; const Name: string);
asm // EAX = @r; EDX = Id; ECX = @Name[1]
mov (TRecord PTR [eax]).Id, edx // EAX^.Id := EDX; // Id
// _LStrAsg(@EAX^.Name, @Name) --> EAX^.Name := Name
lea eax, (TRecord PTR [eax]).Name // EAX := @(EAX^.Name);
mov edx, ecx // EDX := @Name[1];
call System.@LStrAsg // _LStrAsg(EAX, EDX)
end;
Al entrar al procedimiento tenemos EAX apuntando al registro (primer
parámetro), EDX conteniendo el Id (segundo parámetro), y ECX apuntando
a los datos de la cadena Name (tercer parámetro). Asignar un entero es
bastante simple, pero asignar una cadena es un poco más complicado:
If la cadena destino no es la cadena nula then
begin
Decrementar la cuenta de referencias de la cadena destino;
If la la cuenta de referencias de la cadena destino es cero then
Liberar la cadena destino;
end;
If la cadena origen no es la cadena nula then
Incrementar la cuenta de referencias de la cadena origen;
Asignar origen a destino;
El procedimiento _LStrAsg (en la unidad System) implementa esta lógica
por nosotros. El procedimiento recibe dos parámetros: el primero (en
EAX) es la cadena destino pasada por referencia, y el segundo (en EDX)
es la cadena origen pasada por valor (lo que en realidad se pasa es el
puntero, pues las cadenas son punteros a los verdaderos caracteres).
Consiguientemente, en nuestro caso, EAX debe apuntar a la dirección de
la variable string que será asignada assigned (es decir, EAX debe
contener la dirección de r.Name), mientras que EDX debe ser el valor a
asignar:
EAX --> r.Name --> r.Name[1] ==> EAX = @r.Name
EDX --> Name[1] ==> EDX = @Name[1]
Ref.: "-->" significa "apunta a" (o "contiene la dirección de")
Así que establecemos EAX y EDX y luego llamamos a _LStrAsg:
lea eax, (TRecord PTR [eax]).Name // EAX := @(EAX^.Name);
mov edx, ecx // EDX := @Name[1];
call System.@LStrAsg // _LStrAsg(EAX, EDX)
Funciones de bajo nivel para trabajar con registros
===================================================
Al igual que con los arreglos estáticos, si un registro se pasa por
valor entonces es responsabilidad de la función llamada preservar el
registro. Cuando una función necesita cambiar los valor de uno o más
campos de un registro pasado por valor, normalmente crea una copia local
y trabaja sobre la copia. El compilador crea una copia por nosotros en
el "begin" de funciones Pascal, pero en funciones totalmente en
ensamblador tenemos que hacerlo por nosotros mismos. Una forma de
hacerlo es como mostramos en la parte III con los arreglos estáticos.
Aquí tenemos otra forma:
procedure OperateOnRecordPassedByValue(r: TRecord);
var
_r: TRecord;
asm
// Copiar los elementos de "r" (parámetro) en "_r" (copia local)
// Move(r, _r, sizeof(TRecord));
lea edx, _r // EDX := @_r;
mov ecx, type TRecord // ECX := sizeof(TRecord);
call Move // Move(EAX^, EDX^, ECX);
lea eax, _r // EAX := @_r;
mov edx, TRecord_TypeInfo // EDX := TRecord_TypeInfo;
call System.@AddRefRecord // System._AddRefRecord(EAX,EDX);
lea eax, _r // EAX := @_r; // opcional
// Aquí va el resto de la función. Trabajaremos sobre el registro
// "_r" (la copia local), ahora apuntado por EAX.
end;
En esta oportunidad hemos llamado al procedimiento Move en vez de copiar
los datos allí mismo con REP MOVSB. De esta manera escribimos menos
código.
IMPORTANTE: Copiar los valores de memoria sólo funciona con registros
que no contienen campos de tipos que cuentan referencia como las
cadenas, arreglos dinámicos o variantes de tipo cadena o arreglo
dinámico.
Si tenemos uno o más campos de tipo cadena, o campos de cualquier otro
tipo con conteo de referencias, después de copiar los valores de memoria
debemos incrementar sus respectivas cuentas de referencias. El procedi-
miento _AddRefRecord (en la unidad System) hace eso. Recibe dos pará-
metros: un puntero al registro (en EAX) y un puntero a los datos de
información de tipo para el registro generada por el compilador (en
EDX).
La información de tipo (type information) para un registro es básica-
mente una estructura de datos que contiene las posiciones y tipos de los
campos con conteo de referencias en el registro. Los procedimientos que
trabajan con registros declarados en la unidad System (específicamente
_InitializeRecord, _AddRefRecord, _CopyRecord y _FinalizeRecord)
requieren un puntero a los datos de información de tipo como su último
parámetro.
Pero, ¿dónde están esos datos? Bueno, desafortunadamente no hay un
símbolo para acceder su ubicación directamente. Tenemos que obtener su
dirección llamando a la función TypeInfo, pero ésta no es una función
que podemos llamar desde código en ensamblador porque no es una verda-
dera función, sino una función incorporada (built-in) que el compilador
resuelve en tiempo de compilación.
Una solución posible es inicializar una variable global llamando a la
función TypeInfo desde nuestro código Pascal:
var
TRecord_TypeInfo: pointer;
:
initialization
TRecord_TypeInfo := TypeInfo(TRecord);
Y luego podemos usar esta variable así:
procedure OperateOnRecordPassedByValue(r: TRecord);
var
_r: TRecord;
asm
// Copiar los elementos de "r" (parámetro) en "_r" (copia local)
// Move(r, _r, sizeof(TRecord));
lea edx, _r // EDX := @_r;
mov ecx, TYPE TRecord // ECX := sizeof(TRecord);
call Move // Move(EAX^, EDX^, ECX);
// System._AddRefRecord(@_r, TypeInfo(TRecord));
lea eax, _r // EAX := @_r;
mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord);
call System.@AddRefRecord // System._AddRefRecord(EAX, EDX);
lea eax, _r // EAX := @_r; // opcional
// Aquí va el resto de la función. Trabajaremos sobre el registro
// "_r" (la copia local), ahora apuntado por EAX.
// Tenemos que finalizar la copia local antes de regresar
// System._FinalizeRecord(@_r, TypeInfo(TRecord));
lea eax, _r // EAX := @_r;
mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord);
call System.@FinalizeRecord // System._FinalizeRecord(EAX, EDX);
end;
Notomos que antes de que la función retorne debemos hacer una llamada a
_FinalizeRecord para destruir la copia local (por ejemplo, esto decre-
mentará las cuentas de referencia de las cadenas apuntadas por campos de
cadena).
Llamar a Move y luego a _AddRefRecord es una forma válida de copiar
registros si y sólo si el registro destino no ha sido inicializados
(después de llamar a _AddRefRecord el registro queda inicializado). Si
el registro destino ya estuviera inicializado, entonces tenemos que
llamar a _CopyRecord en su lugar.
Por ejemplo:
procedure proc(const r: TRecord);
var
_r: TRecord;
begin
// _r := r;
asm
mov edx, eax // EDX := @r;
lea eax, _r // EAX := @_r;
mov ecx, TRecord_TypeInfo // ECX := TypeInfo(TRecord);
call System.@CopyRecord // System._CopyRecord(EAX, EDX, ECX);
end;
end;
Notemos que puesto que ésta es una función Pascal normal (no una función
enteramente en ensamblador), el compilador automáticamente genera código
para inicializar y finalizar la variable de registro local (en el
"begin" y el "end" del procedimiento respectivamente).
La combinación Move más _AddRefRecord es idéntica en efecto a la
combinación _InitializeRecord más _CopyRecord:
procedure OperateOnRecordPassedByValue(r: TRecord);
var
_r: TRecord;
asm
// Copiar los elementos de "r" (parámetro) en "_r" (copia local)
// System._InitializeRecord(@_r, TypeInfo(TRecord));
push eax // Push(EAX); // @r
lea eax, _r // EAX := @_r;
mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord);
call System.@InitializeRecord // System._InitializeRecord(EAX, EDX);
// _r := r;
lea eax, _r // EAX := @_r;
pop edx // EDX := Pop(); // @r
mov ecx, TRecord_TypeInfo // EDX := TypeInfo(TRecord);
call System.@CopyRecord // System._CopyRecord(EAX, EDX, ECX);
lea eax, _r // EAX := @_r; // opcional
// Aquí va el resto de la función. Trabajaremos sobre el registro
// "_r" (la copia local), ahora apuntado por EAX.
// Tenemos que finalizar la copia local antes de regresar
// System._FinalizeRecord(@_r, TypeInfo(TRecord));
lea eax, _r // EAX := @_r;
mov edx, TRecord_TypeInfo // EDX := TypeInfo(TRecord);
call System.@FinalizeRecord // System._FinalizeRecord(EAX, EDX);
end;
Al igual que _AddRefRecord, el procedimiento _InitializeRecord es sólo
para ser usado con registros no inicializados.
Devolviendo valores registro
============================
Devolver registros es exactamente lo mismo que devolver arreglos
estáticos. Las funciones que devuelven registros reciben un último
parámetro adicional que es el puntero a la ubicación de memoria donde
deben colocar su valor de retorno, es decir, el valor del último
parámetro es @Result. La memoria para el resultado debe ser asignada,
inicializada, y liberada por el llamador (no es responsabilidad de la
función llamada). Por ejemplo, consideremos la siguiente función:
function MakeRecord(Id: integer; const Name: string): TRecord;
begin
Result.Id := Id;
Result.Name := Name;
end;
La función se declara para recibir dos parámetros y devolver un
registro, pero internamente es como un procedimiento que recibe tres
parámetros:
1) EAX = el Id para el nuevo registro
2) EDX = el nombre para el campo Name del nuevo registro
3) ECX = la dirección del registro Result (@Result)
La función puede ser rescrita en ensamblador como sigue:
function MakeRecord(Id: integer; const Name: string): TRecord;
asm // EAX = Id; EDX = @Name[1]; ECX = @Result
mov (TRecord PTR [ecx]).Id, eax // ECX^.Id := EAX; // Id
// (@Result)^.Id := EAX;
// Result.Id := EAX;
// Result.Name := Name;
// System.@LStrAsg(@(Result.Name), @Name[1])
// System.@LStrAsg(@(ECX^.Name), @Name[1])
lea eax, (TRecord PTR [ecx]).Name // EAX := @(ECX^.Name);
call System.@LStrAsg // _LStrAsg(EAX, EDX)
end;
NOTA: No asignamos el valor de EDX antes de llamar a _LStrAsg
porque EDX ya contiene el valor deseado (pasado como
parámetro).
Llamando a funciones que devuelven registros
============================================
Consideremos el siguiente código:
a := MakeRecord(n, s);
Uno estaría tentado a pensar que el compilador lo traducirá como:
asm
mov eax, n
mov edx, s
lea ecx, a // ECX := @a; // @Result
call MakeRecord
end;
Pero las cosas no suceden de esta forma, por lo menos no en Delphi 5. El
compilador asigna e inicializa una variable local para alojar el resul-
tado, y luego copia el registro resultado al registro destino. No sólo
que tenemos una ineficiencia por realizar una copia que sería innece-
saria, sino que -como vimos arriba- la copia misma no es tan inocente
como una llamada al procedimiento Move (_CopyRecord comprueba los datos
de la información de tipo en tiempo de ejecución para localizar los
campos que requieren tratamiento especial). Desde luego, esa variable
local invisible primero es inicializada y eventualmente es finalizada.
Esta forma de hacer las cosas es terriblemente ineficiente. Si alguien
necesita velocidad, mejor llame funciones que devuelven registros
usando ensamblador como mostré arriba, pasando directamente la dirección
de la variable que recibirá el registro resultado como último parámetro
(@Result).
Bien, esto es todo por ahora. En la siguiente parte veremos algunos
conceptos básicos sobre trabajar con objetos.
__________________
NOTA: Se adjunta el código fuente completo.
________________________________________________________________________
8. Foros / listas de correo
Para quienes son miembros de nuestros foros (grupos en Yahoo! Grupos),
me gustaría recordarles que la participación en los mismos está sujeta
a ciertas reglas.
Estas reglas han sido tildadas por algunos de infantiles y déspotas, por
decir lo menos. Para los que tienen buena experiencia en foros y listas
de correo, estas reglas prácticamente no aportan nada nuevo, pero para
otros admito que pueden resultar molestas y algo chocantes. Realmente lo
siento, pero si he decidido establecerlas, no ha sido porque sí. Los
foros son para profesionales, y no para quienes desean publicar mensajes
como se les da la gana simplemente porque es gratis, sino que esperamos
un mínimo de seriedad y consideración hacia los demás.
He aquí el extracto de las reglas comunes a todos los foros:
* El asunto de los mensajes tiene importancia capital. Es lo primero y a
veces lo único que se ve de un mensaje. Por favor:
. Provee un asunto representativo en tus preguntas. Trata que sea lo
más específico posible y que incluya palabras clave que puedan
facilitar la búsqueda posterior de un determinado tema.
. Evita el uso de palabras como "duda", "problema", "socorro",
"SOS", "ayuda" (excepto que estés hablando de archivos de ayuda),
"consulta" (salvo que estés hablando de una consulta SQL), etc.
. Evita usar de signos de exclamación y escribir todo en mayúsculas.
. No modifiques la línea de asunto al responder un mensaje.
* Elimina lo más posible del mensaje original al responder, para reducir
así el tamaño de los mensajes. Como mínimo, al responder elimina las
líneas innecesarias de arriba y de abajo del mensaje original:
- Todo o parte de los encabezados. Mínimamente las líneas de:
. Destinatario (es el foro, ya lo sabemos)
. Asunto (se repite en la respuesta, ya lo sabemos)
. Fecha de envío (opcional)
- Las frases iniciales y finales como saludos, presentaciones, agrade-
cimientos, firmas ("signatures"), publicidades de los servicios de
webmail y de Yahoo!, direcciones del foro, etc.
- Los mensajes anteriores del hilo de conversación
* No envíes mensajes irrelevantes (como mensajes de agradecimiento,
o respuestas del tipo "yo lo quiero" / "yo también" cuando alguien
ofrece algo en el foro, y mucho menos mensajes fuera de tema como
cartas cadena -o "pirámide"-, alertas de virus, publicidades, etc.)
* No envíes ofrecimientos, solicitudes o apologías de cracks, warez,
seriales y otras formas de piratería informática.
* No envíes mensajes del tipo "¿Dónde puedo encontrar...?" (excepto en
el caso del foro de Componentes, que está para ese fin). Los foros
no son un motor de búsqueda humano.
* Incluye abundante información sobre el problema que planteas (como
por ejemplo versión del producto y sistema operativo que usas, etc.)
para no hacerle perder el tiempo a alguien preparando una respuesta
que después no te sirva porque no se aplica a tu plataforma.
* Todos los foristas y todos los mensajes tienen la misma prioridad,
así que por favor no envíes mensajes urgentes, con prioridad o con
su asunto todo en mayúsculas, y evita usar expresiones como "por
favor ayúdenme", "mi empleo depende de esto", "estoy desesperado",
"ya no sé que hacer", etc., así como cualquier otra forma de
intentar obtener ventaja o resaltar tu mensaje sobre los demás.
* No escribas todo en mayúsculas (parece que estuvieras gritando).
* Los archivos adjuntos en los mensajes son descartados. Si tienes
algo para compartir, contacta directamente al interesado, u ofrécelo
en el foro aclarando que las peticiones deben enviarse a tu cuenta de
email, y no a la dirección del foro.
* Por cualquier cuestión administrativa del foro (como por ejemplo
problemas con tu suscripción, reportar casos de spam, etc.) no
escribas al foro sino a su/s moderador/es.
Como siempre, 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).
http://espanol.groups.yahoo.com/group/delphi-abierto
Suscripción:
http://espanol.groups.yahoo.com/group/delphi-abierto/join
delphi-abierto-subscribe@...
* 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@...
* 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
________________________________________________________________________
9. Delphi en la Red
Por Dave Murray <irongut @ vizzavi.net>
Componentes, librerías y aplicaciones
=====================================
Freeware
--------
* Copernic Agent 6.0 - Freeware (adware, but not spyware) *NEW*
It's the successor of Copernic 2001 5.0, the top metasearch software
for the Internet ("Top Ten Internet Tools" by USA Today, 5-star rating
from ZDNet, and "Best For 2001 - Top Five Software" by PC Magazine).
Are you tired of wasting your time with meaningless results from
search engines? Try searching with Copernic. It will surprise you!
http://www.copernic.com/desktop/products/agent/index.html
* MathX
2D and 3D graphics of one- and two-variables functions using OpenGL.
Full source code is included.
http://www.opensource.as.ro/public/MathX.zip
Artículos, trucos y consejos
============================
* Migrating BDE Applications to dbExpress - by Bill Todd
This white paper discusses the migration of BDE-based applications to
dbExpress.
http://community.borland.com/article/0,1410,29106,00.html
* Cross-platform Development with Borland RAD Tools
A white paper on native cross-platform development with Borland tools.
http://community.borland.com/article/0,1410,29139,00.html
* Report to the Delphi Community on Project JEDI - by Alan C. Moore
Alan C. Moore, the Project JEDI director, provides an update on the
many projects under development with the Project JEDI team.
http://community.borland.com/article/0,1410,29157,00.html
* Borland Kylix 3 versus Linux GCC Development - by William Roetzheim
http://community.borland.com/article/0,1410,29171,00.html
* Using the .NET Preview compiler in the Delphi 7 IDE - by John Kaster
An Open Tool for using the preview compiler with the Delphi 7 IDE is
available for download.
http://community.borland.com/article/0,1410,29159,00.html
* Top Windows API Books for Delphi Developers - by Zarko Gajic
Recommended selection of top books on programming Delphi with Windows
API, with links to book reviews. Books cover all of the most common
Windows API functions, and each function has the syntax and an example
of its use in Delphi Pascal.
http://delphi.about.com/library/toppicks/aatpdelphiapibook.htm
* How to validate an IBAN?
http://www.swissdelphicenter.ch/en/showcode.php?id=1470
* How to get the installed keyboard layouts?
http://www.swissdelphicenter.ch/en/showcode.php?id=1471
* How to get/set the caret blink time?
http://www.swissdelphicenter.ch/en/showcode.php?id=1472
* How to encrypt/decrypt files with Windows NTFS functions?
http://www.swissdelphicenter.ch/en/showcode.php?id=1473
* How to get mouse wheel line count?
http://www.swissdelphicenter.ch/en/showcode.php?id=1474
* How to obtain all characters in Delphi 4+ IDE - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=179
* How to obtain object property list - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=180
* How to refresh Windows desktop - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=181
* How to draw rotated text - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=182
* How to swap two numbers without a third variable - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=183
* How to turn numlock on by code - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=184
* Access a MySQL database from Delphi Standard or Personal - TheSaviour
How to access a MySQL database from Delphi Standard or Personal using
the TMySQL component.
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=185
* How to sort a TList - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=186
* How to set the width of a TComboBox - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=187
* How to access a remote registry - by m3Rlin
www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=188
* How to change a property of all components at one time - by M. Suesens
How to change a special property of all components that own this
property at one time.
http://www.delphi3000.com/articles/article_3365.asp
* CPU ID unit - by Mironescu Tony
http://www.delphi3000.com/articles/article_3367.asp
* How to implement a Sequential List in a database - by Adesh Jain
http://www.delphi3000.com/articles/article_3372.asp
* Export ALL tables from MS jet to CSV via ADO - by John Pears
http://www.delphi3000.com/articles/article_3373.asp
* High speed parser V1.5 - by Yuriy Pisarev
Component is intended for some mathematics and logical calculations.
Works at high speed - about 10000000 operations per second for simple
mathematics formulas and a little more for logical formulas.
http://www.delphi3000.com/articles/article_3374.asp
* Safety Design with a Static Instance - by Max Kleiner
How to build a real Singleton?
http://www.delphi3000.com/articles/article_3376.asp
* Interbase Backup on the Fly in a thread - by Kim Sandell
Make interbase database backups on the fly, in a background thread.
http://www.delphi3000.com/articles/article_3378.asp
* Example of a Windows Service, with a thread - by Kim Sandell
Delphi 5&6 has a template project for services, but it is incomplete.
This example builds on that template and completes the service. It
also shows how to start a thread that beeps every 2 seconds. You can
use this as a base when developing servers as services.
http://www.delphi3000.com/articles/article_3379.asp
* Interbase Sweep on the Fly in a thread - by Kim Sandell
In the Interbase Admin components there is a IBValidationService but
is hard to use as it is. Sweeping is just one of the functions of the
validation service. This component makes doing sweeps of databases
alot easier, and also works in a thread.
http://www.delphi3000.com/articles/article_3380.asp
* How to write a TCP Redirector - by Kim Sandell
Many people ask how to write servers in Indy, this primer goes through
how a TCP server is created, and how to redirect all traffic to
another remote server. This is the same as port-mapping in firewalls.
http://www.delphi3000.com/articles/article_3381.asp
* ENTER instead of TAB key - by Léo Souza
A simple alternative to allow form navigation with the ENTER key.
http://www.delphi3000.com/articles/article_3382.asp
* How to create a animated (rotating) hourglass - Henk Schreij
http://www.delphi3000.com/articles/article_3383.asp
* How to implement an Array Property - Max Kleiner
In an interface we can't use fields so when you declare a class that
implements one or more interfaces, you must provide an implementation
of all the methods declared in the interface and the fields too.
http://www.delphi3000.com/articles/article_3387.asp
* Change the color of a Listview or TreeView - by LExter LExter
http://www.delphi3000.com/articles/article_3388.asp
* SQL without bureaucracy - by Josir Gomes
How to quickly run SQL without dropping components on your form.
http://www.delphi3000.com/articles/article_3389.asp
* Keyboard Hook - by William Egge
Creating a keyboard hook in your application.
http://www.delphi3000.com/articles/article_3390.asp
* Converting a SID to a string - by Bryan Ashby
Convert a Security Identifier (SID) into a human-readable string.
http://www.delphi3000.com/articles/article_3391.asp
* Changing Creation and Last accessed date/time for files - David Bolton
http://www.delphi3000.com/articles/article_3392.asp
* How to check if an OLE object is installed - by Mike Shkolnik
http://www.delphi3000.com/articles/article_3394.asp
* How to read contact list from MS Outlook - by Mike Shkolnik
http://www.delphi3000.com/articles/article_3395.asp
* Fast data transfer to MS Excel - by Mike Shkolnik
http://www.delphi3000.com/articles/article_3396.asp
* Multi Lingual BoolToStr() and SexToStr() - by Mike Heydon
http://www.delphi3000.com/articles/article_3398.asp
Tutoriales
==========
* Introducción a las aplicaciones MDI - por Pablo Castagnino
Introduce al programador al mundo de las aplicaciones MDI en Delphi y
aporta soluciones a problemas típicos que se presentan al programar
este tipo de interfaz.
http://webs.sinectis.com.ar/alvadel/docs/mdi.htm
* Breve introducción a la programación multihilo - por Enrique Gonzalez
Algunos conceptos básicos obre programación multihilo, y ejemplo.
http://webs.sinectis.com.ar/alvadel/docs/multihilo.htm
* Navigating and Editing a ClientDataSet - by Cary Jensen
You navigate and edit a ClientDataSet in a manner similar to how you
navigate and edit almost another other dataset. This article provides
an introductory look at basic ClientDataSet navigation and editing.
http://community.borland.com/article/0,1410,29122,00.html
* Sophisticated Delphi Pascal techniques - by Zarko Gajic
A Beginner's Guide to Delphi Programming: Chapter 7. Time to extend
your Delphi Pascal knowledge to the max. Here are some intermediate
Delphi problems and articles for everyday development tasks.
http://delphi.about.com/library/weekly/aa091702a.htm
* Designing an XML Grammar with DTDs - by Philip Page
XML is a great medium for data transfer and definition, but it must be
consistent to make it consumable. Learn more about creating a DTD to
determine consistent XML.
http://builder.com.com/article.jhtml?id=u00320021004ppg01.htm
* SQL Basics: Creating and Altering Tables - by Shelley Doll
Learn the basic DDL commands to add, alter, and remove tables and
databases with this article from the Builder.com SQL basics series.
http://builder.com.com/article.jhtml?id=u00320020902dol01.htm
* SQL Basics: Number Data Types - by Shelley Doll
Failing to understand number data types poses a DBA's greatest risk of
compromised data. The SQL92 standard dictates how database
manufacturers define number behaviors, such as length and truncation.
http://builder.com.com/article.jhtml?id=u00320020924dol01.htm
* SQL Basics: String Data Types - by Shelley Doll
Data type implementations vary from database to database but a working
knowledge of the SQL specification will always give you a good idea of
what's going on. This article breaks down the basic rules of deploying
string data types.
http://builder.com.com/article.jhtml?id=u00320020918dol01.htm
* A Guide to Automatic Garbage Collection Systems - by Paul Tyma
Java and .NET feature automatic garbage collection, which allows you
to worry about programming instead of system cleanup. Learn more about
the approaches often used to add this feature to applications.
http://builder.com.com/article.jhtml?id=u00320020930gcn01.htm
Otros enlaces
=============
* GLScene en español
La primera página sobre GLScene, OpenGL y Delphi en español. Demos,
manuales y artículos.
http://glscene.tripod.com
* Delphi 7 Update 1 - by Anders Ohlsson
This update for Delphi 7 contains an updated MSSQL driver that fixes
the problem with empty user names and passwords. It also fixes an
issue with extra NULL characters being added to VARCHAR columns. The
update also contains several documentation updates.
http://community.borland.com/article/0,1410,29209,00.html
* Delphi 7/.NET User Group Tour 2002, US / Canada - by David Intersimone
Borland is taking Delphi 7 and the Delphi Preview for Microsoft .Net
on the road in the US and Canada.
http://community.borland.com/article/0,1410,29089,00.html
* The Delphi Bug List
http://buglist.jrsoftware.org/
* Pascal and its Successors - by Niklaus Wirth
Pascal was designed in 1969 in the spirit of Algol 60 with a concisely
defined syntax representing the paradigm of structured programming.
With the advent of the micro computer it became widely known and was
adopted in many schools and universities. In 1979 it was followed by
Modula-2 which catered for the needs of modular programming in teams.
In an effort to reduce language complexity and to accommodate object-
oriented programming, Oberon was designed in 1988. This article
presents some aspects of the evolution of this family of languages.
http://www.swissdelphicenter.ch/en/niklauswirth.php
* Pascal IRC channel
There is a very nice IRC channel about Pascal on DalNet
(http://www.dal.net). The channel is just about Pascal (not Delphi).
You can find it on irc.dal.net at #turbopascal. The channel also has
a nice page with lots of sample codes. You can find their homepage at
http://www.pastcow.org - As you can imagine, a channel for Delphi
users can also be found there: #delphi.
* BorCon Europe
London UK, October 28-29 2002
http://www.borconeurope2002.com/
* BorCon Japan
Tokyo Japan, November 19-20 2002
http://www.borland.co.jp/
* BorCon France
Paris France, November 21-22 2002
http://info.borland.fr/conference/2002/
________________________________________________________________________
¡Tú puedes ayudarnos!
Por favor danos una mano y ayúdanos a llegar a los 10.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.php3
boletin-pascal-subscribe@...
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://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium
http://news.optimax.com/topdelphi/links.exe/click?id=70C517ECAE6E
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://www.programacion.net/votar-enlace.php?id=474
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/p0040.zip
________________________________________________________________________
Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/
Para suscribirse/apuntarse: boletin-pascal-subscribe@...
Para cancelar/removerse: boletin-pascal-unsubscribe@...
¿Problemas para administrar tu suscripción? edspirito@...
________________________________________________________________________
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.php3
Copyright (c) 2002 por Ernesto D'Spirito. Todos los derechos reservados.
________________________________________________________________________