Mostrando entradas con la etiqueta xBase. Mostrar todas las entradas
Mostrando entradas con la etiqueta xBase. Mostrar todas las entradas

viernes, 30 de enero de 2015

Manejando bits con Harbour

En esta entrada del blog usaremos algunas de las funciones para operar con números binarios que nos aporta Harbour.
No entrare a explicar porque se usa el sistema binario en los ordenadores (supongo que como programadores es un tema que conocéis), y solo explicare brevemente el sistema binario, si ya lo conoces puedes saltarte el siguiente párrafo.
El sistema binario es un sistema de numeración en el cual los números se representan utilizando las cifras 0 y 1 . Cada dígito de un número en este sistema se denomina bit y al igual que en base 10 (el que utilizamos habitualmente), el valor depende de la posición donde se halla.
Veamos un ejemplo y nos quedará más claro:

Normalmente la manipulación de binarios queda reducida a la programación a bajo nivel, y en el resto de programación podemos sobrevivir sin manipular bits. Pero para poder ilustrar el uso de alguna de las funciones de manejo de bits de Harbour, os propondré el uso de binarios para la gestión de estatus, variables lógicas que solo puedan ser verdadero o falso, en nuestro caso 0 o 1.

Estos campos estatus habitualmente los solucionamos mediante el uso de campos lógicos, o tipo carácter de una posición grabando S o N, según corresponda. El tamaño de estos campos, o variables, es de 1 Byte (es el tamaño mínimo que podemos definir), pero 1 Byte son 8 bits, con lo cual el valor grabado en 1 Byte nos permite identificar hasta 8 estatus diferentes.

 Imaginemos que en una factura deseamos controlar varios estatus:
  • Tiene IVA
  • Tiene Recargo de Equivalencia
  • Esta cobrada
  • Esta impresa
  • Es un contado
  • Factura Rectificativa
Toda esta información podemos grabarla en un solo campo carácter de una posición, y aun nos sobra para dos estatus más.
Crearemos una tabla llamada FraCab que entre otros atributos, contendrá uno llamado Estatus del tipo carácter y de una posición.
Para el ejemplo trabajaremos sobre un único registro, y dispondremos de controles CheckBox para poder activar o desactivar nuestros estatus, que se grabaran en un campo de una sola posición.
Los ocho estatus los manejaremos mediante un Hash, por comodidad las tablas hash y algunas otras variables las he definido como estáticas al proyecto para no tener que pasarlas como parámetros.

Leeremos el campo estatus de la tabla y asignaremos a cada posición del hash el valor lógico que le corresponda. 
 Para este fin usamos la función hb_BitTest( nParam, nBit )→ lResult , le pasamos como primer parámetro el valor ascii de nuestro campo estatus y como segundo el número de bit que deseamos leer. hb_BitTest nos devuelve un valor lógico dependiendo si dicho bit vale uno o cero.


Una vez que hemos recuperados los valores de cada uno de los estatus, dibujamos el dialogo que nos permitirá ver y modificar los valores.

Para su modificación usamos controles CheckBox, que en el evento onChange, actualizan el campo, y refrescan los valores de la pantalla.
Esta es la función encargada de actualizar los valores

Recibe como parámetro la clave del hash, busca cual es su posición y lo graba en la variable nPos, esto nos permite saber que bit es el que debemos manipular.
Y procedemos a calcular el valor del nuevo estatus en función de si es falso o verdadero, mediante el uso de las funciones hb_BitSet y hb_BitGet
hb_BitSet( nParam1, nBit ) → nResult Retorna el entero resultante de activar el bit nBit en el entero nParam1
hb_BitReset( nParam1, nBit ) → nResult Retorna el entero resultante de desactivar el bit nBit en el entero nParam1.

Como podéis ver, necesitamos el código ASCII para pasar a la función de manipulación de bits, y convertimos el número obtenido a su valor ASCII, para ello usamos las funciones Asc() y Chr().

Este ejemplo ha sido programado usando FWH 15.01, Harbour 3.2.0 y BCC 5.82. Para su correcto funcionamiento es necesario linkar la libreria hbMisc.lib

Descarga código fuente
Descarga exe comprimido

 Espero que os haya resultado interesante, si además podéis extrapolarlo y hacer uso en algún caso práctico mucho mejor. Y en el peor de los casos deseo que os haya servido para conocer algunas de las funciones que incorpora Harbour y que no solemos utilizar frecuentemente.
Agradezco comentarios y sugerencias, saludos desde Mallorca.

Por cierto, en http://harbouradvisor.blogspot.com.es/2012/02/funciones-para-manipular-bits.html de mi amigo Carlos Mora, podéis encontrar una explicación de todas las funciones para manejo de bits que nos proporciona Harbour.


jueves, 27 de agosto de 2009

Usando ficheros comprimidos ZIP desde Harbour.

Como pone en la página web de harbour (http://www.harbour-project.org/), harbour es software libre 100 % compatible con compiladores Clipper.

Pero ademas de las funciones Clipper, el compilador ha ido añadiendo una serie de funciones nuevas (todas las que su nombre empieza por HB_... ), que muchas veces desconocemos, y que nos aportan funcionalidad extra.
Estas funciones son usadas escasamente por la comunidad de usuarios, bien porque desconoce su existencia, bien porque la documentación es excasa o nula para poderlas usar.

En la entrada de hoy vamos a centrarnos sobre dos funciones en concreto, HB_ZipFile y HB_UnZipFile, que nos va a permitir comprimir y descomprimir archivos en formato ZIP.

HB_ZipFile( cZipName, cFile | aFiles, nLevel, bBlock, lOverRide, cPassWord, lWithPath, lWithDrive, pFileProgress )


cZipName Nombre del fichero ZIP a crear

cFile | aFiles

Nombre del fichero a comprir o array de ficheros a comprimir. Unidad y Path pueden ser usados.

nLevel

Nivel de compresión. De 0 a 9

bBlock

Code block a ejecutar mientras se comprime

lOverRide

Sobreescribir el fichero si existe.

cPassWord

Password para encriptar el fichero ZIP

lWithPath

Guardar el path si/no

lWithDrive

Guardar la unidad si/no

pFileProgress

Code bloclk para progreso de archvios.




HB_UnZipFile( cFile, bBlock, lWithPath, cPassword, cPath, cFile | aFiles, FileProgress )

cFile Nombre del fichero a descomprimir

bBlock

Code block a ejecutar a medida que se va descomprimiendo

lWithPath

Permitir crear directorios si es necesario

cPassWord

Contraseña a usar para descomprimir el fichero

cPath

Path donde estraer los ficheros

cFile | aFiles

Nombre de un archivo, o array de nombres de archivos a extraer

pFileProgress

Code block para visualizar progreso de los ficheros


Las dos funciones retorman un valor booleano, informandonos si el proceso a concluido con éxito.


Veamos un primer ejemplo sencillo de comprimir / descomprimir.
scr1

Vamos a complicarlo y elaborarlo un poco mas, en el fondo haremos lo mismo, pero con una barra de evolución tanto para comprimir como descomprimir. Simplemente haremos uso de los parametros adicionales que nos permiten ejecutar codeblocks.

scr2

Como podeis ver en el fuente, hemos creado dos funciones comprime y descomprime, que son muy sencillas de usar. Comprime recibe dos parámetros, nombre del fichero zip que queremos crear, y array (arreglo) con los ficheros que queremos comprimir. Descomprime recibe como único parámetro el nombre del fichero ZIP a descomprimir.
En el interior de cada una de las funciones creamos un dialogo para visualizar la evolución, y montamos e code block necesario para que se vaya actualizando.
Como visteis al principio existen más parámentros en la función HB_ZipFile y Hb_UnZipFile, se trata de que vayais experimentando con ellos y usando los que os hagan falta. Este ejemplo es un ejemplo básico, pero creo que cubre las necesidades más comunes de compresión y descompresión.

Existen una serie de funciones adicionales para manejo de ficheros Zip, pero os las dejo para que vosotros mismos las investigueis, en descomprime por ejemplo hacemos uso de hb_GetFilesInZip ,para determinar el número de archivos contenidos en el fichero comprimido.

Ya habeis visto que no es nada complicado, si solo quereis comprimir / descompriir, con dos funciones y usando dos parámetros ya lo esta listo.

El último escollo, pero también importante, es linkar las librerías adecuadas para poder hacer uso de las funciones de manejo de ficheros ZIP.
Las librerias a ñadir a vuestro proyecto son:
hbZiparc.lib
hbmzip.lib
hbzlib.lib


Descargar fuentes

Espero que os sea de utilidad, saludos desde Mallorca.
Biel Maimó

viernes, 6 de febrero de 2009

Actualización automatica de aplicaciones via FTP

updftp01
En esta entrada del blog vamos a diseñar un sistema para automatizar las actualizaciones de las versiones de nuestros programas.
Para ello, necesitaremos disponer de un servidor FTP. El motivo de usar un servidor FTP, es que nos va a permitir tanto una solución vía intranet como Internet.

Vamos a explicar la teoría de este sistema, y después iremos con la practica y el código fuente.

1º Dispondremos de un servidor FTP, en el cual montaremos una estructura de carpetas, donde el nombre de la carpeta, será el de la aplicación, y dentro de esta carpeta alojaremos la última versión de nuestro programa ejecutable.

2º Especificaremos a nuestro programa, la información necesaria para poderse conectar al servicio FTP( ip, usuario,...), con esa información se conectara, y comprobara la antigüedad del fichero alojado en el servidor, la comparara con él mismo, y si la versión del FTP es más nueva, procedera a la descarga del fichero ejecutable, en un fichero temporal en nuestra unidad local.

3º Si había actualizaciones para descargar, y la descarga se ha completado con éxito, ejecutaremos otro proceso, que borrara la versión antigua, re nombrara y lanzara su ejecución.
updftp01
Y con esto ya tendremos actualizado nuestro programa. Viendo la teoría, no parece muy complicado ;-)

Esta solución, la tengo implementada como parte de una clase (tAppl), que utilizo en todas mis aplicaciones, aquí para poderos poner un ejemplo auto contenido, y no complicarnos explicando la creación de las clases, lo realizaremos con funciones, aunque luego el lector lo podrá adaptar a una clase o de la manera que le parezca mas cómoda para su forma de trabajo.

Nuestra aplicación, empezara definiendo la dirección del servidor FTP, y la carpeta donde comprobar el fichero a actualizar, seguidamente ejecutaremos la función ChkUpdFtp, y aquí se bifurcara, dependiendo de si hay que actualizar o no.
Funcion Main
Veamos ahora la función(ChkUpdFtp) encargada de determinar si existen versiones nuevas de nuestro programa, para ello utilizaremos las clases tInternet y fFtp.
Lo primero que haremos será comprobar si disponemos de los parámetros necesarios para conectarnos, y si existe el programa ActVer(mas adelante veremos su código fuente) que será el encargado de renombrar y relanzar nuestro programa. Pasado este primer filtro, pasamos a intentar realizar la conexión con el FTP, si dicha conexión tiene éxito, solo nos resta comprobar las fecha y hora de creación de los archivos y determinar si debe o no actualizarse, en caso de que se deba actualizar llamaremos a la función Actualiza.
Funcion Main
En este proceso usamos funciones que no vamos a comentar su funcionamiento, simplemente os incluyo su codigo fuente (prg y C). ( TimeToSec, FileTimes, FileSize ) O bien podeis utilizar alguna alternativa vuestra si en vuestro set de funciones ya las teneis definidas.

La función actualiza, es la encargada de descargar el nuevo fichero ( GetFile ), y visualizar en pantalla una barra de evolución de la descarga .
Funcion Main

Al llegar a este punto, si la función Actualiza nos devuelve verdadero, significará que ya tenemos descargado nuestra nueva versión en la carpeta local, solo debemos cerrar la versión actual, e invocar ActVer que se encargará de borrar la versión anterior, y renombrar el fichero que acabos de recibir, y posteriormente ejecutarlo.

Veamos el sencillo código de ActVer.exe, recibe como parámetro el fichero a actualizar, borra la versión anterior, renombra y ejecuta.
ActVer
Y con esto ya tenemos implementado nuestro sistema de actualizacion via Ftp de nuestras aplicaciones.
Dos cosas importantes a tener en cuenta si vuestro servidor de FTP es LINUX, los nombre de ficheros, en linux son case sensitive, y la hora de creacion del fichero se guarda en UTC.
UpdFtp.prg
ActVer.prg
UpdFtp.exe y ActVer.exe

Nota: me comentan que al compilar con xHarbour falta la función HB_IsChar, simplemente sustituir HB_ISCHAR( cTime ) por ValType(cTime)=='C'.

martes, 8 de julio de 2008

LetoDB (RDD cliente servidor para [x]Harbour)

Hasta ahora la mayoría de entradas de este bloc, hacen referencia al acceso a datos mediante el uso de ADO, como alternativa al uso clásico de los ficheros DBF.
LetoDB
En esta nueva entrada, nos vamos a quedar en medio de la dos tecnologías, y hablaremos de un proyecto Open Source creado por Alexander S. Kresin, llamado LetoDB.

LetoDB, es un servidor de base de datos multi plataforma(Windows y Linux), escrito en Harbour y que nos permite trabajar con ficheros DBF e indices CDX o NTX. Se implementa como un RDD de harbour, y por tanto la forma de usarlo es prácticamente igual que usar DBFs de toda la vida, solo nos harán falta unas pocas instrucciones para realizar la conexión al servidor.

LetodDB se divide en dos partes fundamentales, el servidor (letodb.exe) y la parte cliente (rddleto.lib). También se incluye un programa de consola, que nos permite monitorizar el estado del servidor.
Aunque el tema es amplio y habría mucho que comentar sobre la implementación del servidor, el uso de multihilos, etc, como siempre intentare ser eminentemente práctico, y que de una manera rápida y senciilla podáis probar ester servidor, y si os interesa el tema vosotros mismos podréis ir profundizando.

Para empezar nuestras pruebas, deberemos descargarnos una serie de archivos, aunque os los podéis descargar de sourceforge (http://sourceforge.net/projects/letodb/ ) y compilarlos con la version que deseeis, aquí os pongo un enlace para que os podeis descargar un fichero en el que ya estan los ejecutables y la librería listas para usar.

Descargar letodb.zip
Este archivo contiene lo siguiente
Letodb.exe ->Ejecutable del servidor
rddleto.lib -> RDD (compilado con Harbour 1.0.0RC1(Rev.8835) )
manage.exe -> Consola del servidor.
Una vez tengamos descargado este archivo, podemos crear una carpeta (C:\letoDB por ejemplo), y descomprimimos letodb.zip.

El servidor de LetoDB, por defecto trabaja sobre el puerto 2812, y el path por defecto es c:\, estos parametros pueden ser modificados mediante el uso del fichero letodb.ini, si no existe este fichero, se utilizaran los valores por defecto, y si existe los tomara del fichero. Debe crearse en la misma carpeta donde se encuentra ubicado letodb.exe.
Ejemplo letodb.ini

Port = 2812
DataPath =c:/letodb
Logfile = "letodb.log"
Default_Driver = CDX

Para arranncar el servidor, simplemente ejecutaremos letodb.exe, el servidor no tiene ventana asociada, y solo nos arrancara un proceso en nuestro sistema. Para detener el servidor, ejecutaremos el “letodb stop”.
Administrador de tareas

Ahora ya podemos arrancar la utilidad de consola, que nos va a permitir monitorizar nuestro servidor. Para conectarnos al servidor, debemos especificar la IP donde esta letoDb ejecutandos, el port que usamos y pulsar sobre el boton Go. Si lo estamos ejecutando en local sobre nuestro PC, en la dirección pondremos 127.0.0.1 o bien localhost. consola LetoDB
El tercer campo que por defecto tien 2 como valor, son los segundos que tarda en refrescar la información.


Una vez hemos pulsado GO, dispondremos de tres vistas, que podremos ir cambiando pulsando los botones Main, Users, Tables. La información de cada una de estas pantallas es autoexplicativa, en la primera información general del servidor, en Users veremos los usuarios y procedimientos conectados, y en Tables veremos cada proceso con los ficheros que tiene abiertos.
consola Tables
A estas alturas ya estamos listos para escribir el código fuente que nos permita conectar con el servidor letoDB, y realizar nuestras primeras operaciones cliente servidor usando ficheros DBF.

El código fuente es el siguiente, y para que funcione tenéis que linkarlo junto con RddLeto.lib, y por supuesto el servidor debe de estar ejecutándose.

Source

Como podeis apreciar prácticamente todo son comandos estándar de harbour, el único comando no estándar es el que nos comprueba si tenemos conexión con el servidor, el resto son todos nativos de Harbour.
Observar también que debemos definir la dirección IP y el puerto que uso nuestro servidor LetodDB.
El ejemplo es muy sencillo, todo el código esta comentado y creo que se entiende perfectamente.
Ademas de los visto, letoDB incorpora otras funciones adicionales, pero que ya podréis descubriendo si os interesa este sistema cliente servidor, entre las cosas destacadas comentaros que incorpora control de transaccional ( leto_BeginTransaction(), leto_Rollback(), leto_CommitTransaction()), y que el autor tiene pensado en un futuro implementar procesos almacenados que estarán escritos en Harbour.

Desde aqui podeis descargar el código fuente, y el ejecutable.
Descargar ejecutable
Descargar código fuente
Descargar letodb.zip
Podréis encontrar más información sobre letoDB, y descargaros el proyecto completo con su código fuente en sourceforge (http://sourceforge.net/projects/letodb/ )

Ya que son varios los que me lo habeis pedido, aqui os pongo un fichero .bat y .bc para que podais construir la libreria y el ejecutable del servidor con Harbour(1.0.0RC1)y Borland C 5.5, para versiones anteriores de Harbour no vale, ya que se han renombrado las libreriras. Estos ficheros asumen el compilador de C en C:\Borland\bcc55\bin y Harbour en c:\harbour. Si los tienes así, copia estos dos archhivos en la carpeta donde te has descargado letoDB(desde sourcefourge)y basta con ejecutar make_b6 desde la linea de comandos(Si quieres asegurarte de limpiar copias antiguas, ejecuta primero make_b6 clean).
Descargar make_b6.bat
Descargar makefileb6.bc



Gracias a Alex Kresin, y a todos los que han contribuido en el desarrollo de LetoDB, que nos brinda otra alternativa de acceso a datos para el mundo xBase.

Saludos desde Mallorca.



...

jueves, 11 de octubre de 2007

Usando List & Label 12

Recientemente en el foro de FiveWin se viene hablando del generador de informes List & Label. Y aunque se comenta que es muy bueno, que funciona muy bioen, etc, la verdad es que no hay hasta la fecha ningún ejemplo funcional para ser usado con FiveWin. Bueno realmente si hay una primera aproximación de Jairo Centeno (gracias), que expone un ejemplo de como usar el generador de informes.

En esta entrada intentare poner un ejemplo de como usar el diseñador, y otro de lanzar un informe.

El primer punto a destacar es la filosofía de uso de este generador de informes. Mientras que la mayoria de generadores se basan en la conexion a un origen de datos, LL recibe los datos al momento, y su origen puede ser cualquiera, dejando en manos del programador el envio de datos al reporte.

La función DefineData, es la encargada de pasar los datos al reporte, tanto para diseño como para impresión. El primer paso es convertir los datos xBase a formato LL, y el segundo definir el campo o la variable dependiendo del valor recibido como parametro en lAsField. El segundo parametro oLl, es un objeto valido LL ya creado.

Para lanzar el diseñador de informes, llamamos a la funcion Disenya los pasos a seguir son muy simples.
1.- Creamos un nuevo objeto LL oLl = TActiveX():New( oWnd, "L12.List-Label12_Ctrl_32.1" )
2.- Ajustamos varias propiedades del objeto LL
3.- Llamamos a la función DefineData, que le enviara los campos al diseñador de informes.
alias->( DefineData(.t., oLl) )
4.-Invocamos el diseñador
oLl:Do("LlDefineLayout",oWnd:hWnd,"Designer",LL_PROJECT_LIST, "informe.lst")
Facil y sencillo, ahora manipulamos y diseñamos nuestro informe, y lo guardamos. Ya tenemos listo un informe para ser llamado desde nuestra aplicación.

Ya tenemos un informe creado, ahora vamos a llamar el informe desde nuestro programa, podeis revisar el código de la función Imprime que teneis más abjo, el funcionamiento es el siguiente:
1.- Creamos un nuevo objeto LL oLl = TActiveX():New( oWnd, "L12.List-Label12_Ctrl_32.1" )
2.- Ajustamos varias propiedades del objeto LL (inicio de variables, de campos, immpresora por defecto, ...)
3.- Llamamos a la función DefineData, que le enviara los campos al diseñador de informes.

Hasta aqui igual que para el diseñador.

4.- Empezamos la impresión

oLl:Do("LlPrintStart",LL_PROJECT_LIST,"Informe.lst",LL_PRINT_PREVIEW)
oLl:DO("llPrint")

5.- Montamos un bucle que recorra los registros que queremos imprimir, y para cada registro llamamos la Funcion DefineData (convierte y envia los datos aLL) y oLl:DO("llPrintFields") que imprime los campos.

5.- Una vez que hemos terminado los registros que queriamos imprimir, solo nos queda indicarle a LL que ya esta finalizado el listado.
oll:DO("llPrintEnd",0)
oLl:DO("LlPreviewDisplay", "informe.lst", , oWnd:hWnd)



Esto es un ejemplo muy sencillo, pero espero os aclare un poco el modo de funcionar, para ver con más profundidad todas las funciones y parametros, revisad la ayuda del producto.
En el código fuente incluido, se utiliza una tabla cuyo alias es tmp, y se presupone abierta, y una ventana oWnd tambien se presupone definida, y con scope en las funciones.
En la transformacion de tipos de datos, el formato fecha lo convierto a texto, aunque podria convertirse tambien a formato juliano si quisieramos hacer algún tipo de operación con la fecha dentro del informe.
Nada más, esta es mi primera entrada en el blog, así que espero os guste y os sea de utilidad. En la medida del tiempo disponible ire añadiendo nuevas entradas.











Código:


#include "cmbtll12.ch"

FUNCTION Disenya()

   LOCAL oLL,oWnd,hJob,cName:='qsl'

   oLl = TActiveX():New( oWnd, "L12.List-Label12_Ctrl_32.1" )

   oLl:Do( "LlSetOption", LL_OPTION_MULTIPLETABLELINES, 1)    //Permite multiples tablas

   oLl:Do( "LlDefineFieldStart" ) //Limpia buffer de campos

   oLl:DO( "LlDefineVariableStart")//Limpia buffer de variablesx

   Tmp->( DefineData(.t., oLl) )



   oLl:Do("LlSetPrinterDefaultsDir",hJob,"C:\Windows\Temp") //Arranca diseñador

   oLl:Do("LlDefineLayout",oWnd:hWnd,"Designer",LL_PROJECT_LIST, "log.lst")



RETURN NIL

//------------------------------------

FUNCTION Imprime()

   LOCAL oLL,oWnd,hJob,cName:='log.lst',cDir:=oAppl:cDirLocal

   oLl = TActiveX():New( oAppl:oWnd, "L12.List-Label12_Ctrl_32.1" )

   oLl:Do( "LlSetOption", LL_OPTION_MULTIPLETABLELINES, 1)

   oLl:Do( "LlDefineFieldStart" )

   oLl:DO( "LlDefineVariableStart")

   Tmp->( DefineData(.t., oLl) )

   oLl:Do("LlSetPrinterDefaultsDir",cDir)

   oLl:Do("LlPrintStart",LL_PROJECT_LIST,cName,LL_PRINT_PREVIEW)

   oll:DO("llPrintOptionsDialog",oAppl:oWnd:hWnd)

   oLl:DO("llPrint")

   Tmp->(DBGoTop())

   DO WHILE !Tmp->(Eof())

      Tmp->(DefineData(.T.,oLl))

      oLl:DO("llPrintFields")

      Tmp->(DBSkip())

   ENDDO

   oll:DO("llPrintEnd",0)

   oLl:DO("LlPreviewDisplay", cName, , oAppl:oWnd:hWnd)

   oLl:DO("llPreviewDeleteFiles",cName,cDir)

RETURN NIL

//----------------------------------------------

FUNCTION DefineData(lAsField,oLl)

   LOCAL FldType, FldContent, lExpr,i,DateBuffer:=Replicate(Chr(0),255)

   //Convierte formato xBase a List & Label

   FOR i:=1 TO FCount()

      DO CASE

      CASE FieldType(i)=='N'

         FldType:=LL_NUMERIC

         FldContent:=Str(FieldGet(i))

      CASE FieldType(i)=='C'

         FldType:=LL_TEXT

         FldContent:=Trim(FieldGet(i))

      CASE fieldType(i)=='M'

         FldType:=LL_TEXT

         FldContent:=FieldGet(i)

      CASE FieldType(i)=='L'

         FldType:=LL_BOOLEAN

         FldContent:=IF(FieldGet(i),'TRUE','FALSE')

      CASE FieldType(i)=='D'

         FldType:=LL_TEXT

         FldContent:=DToC(FieldGet(i))

      ENDCASE

      IF lAsField

         oLl:Do("LlDefineFieldExt",FieldName(i), FldContent, FldType )

      ELSE

         oLl:Do("LlDefineVariableExt", FieldName(i), FldContent, FldType )

      ENDIF

   NEXT

RETURN NIL