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.


sábado, 30 de agosto de 2014

Setter y Getter, buenas prácticas en OOP.


Ha pasado muchísimo tiempo desde la última entrada, quizás demasiado, pero por aquí seguimos.
A diferencia de la mayoría de entradas de este blog, que son eminentemente prácticas, hoy haré una entrada más bien teórica.

Voy a hablar de los DATA ( variables de instancia ), a estas alturas todos hemos leído bastante sobre OOP (poo), y estamos familiarizados con su uso y bondades (polimorfismo, encapsulación, …).
Una de las costumbres habituales de los programadores de Harbour es manipular directamente las variables de instancia(los DATA de las clases), que es algo que va en contra de la encapsulación, pero también es cierto que nos vemos abocados a hacerlo porque muchas clases no disponen de métodos set y get o setget.

Lo normal y correcto es que los datas sean privados a la clase, y solo puedan ser manipulados desde sus métodos públicos.

Para ilustrar la importancia, lo haremos usando un ejemplo de código de la clase tAppl, que contiene, entre otras cosas, un data con los objetos font que se utilizan en la aplicación.
En su día este data se implemento usando un Array, y en la clase existían dos métodos uno para asignar y otro para recuperar.





Desde la aplicación, la clase se usa como ilustra el siguiente código, como podéis ver no se accede directamente a la variable, añadimos nuevos fonts usando AddFont y los recuperamos con GetFont.
Para facilitar el uso y la legibilidad del código, se realizaba un define para cada font del tipo
#define COUR10BOLD 3, que identificaba el font y tamaño con la posición del array.




Pasados los años en Harbour se incluyo el tipo de dato hash (arrays asociativos), que entre uno de los muchos beneficios que aporta, nos permite manejar de una forma más elegante estructuras de datos como el caso de los fonts.
La idea es sustituir el array que guarda los fonts por un hash, y cambiar la forma de recuperar el font, pasando como parámetro su nombre y no su indice, pero queremos mantener la compatibilidad con todas nuestras aplicaciones.
En este momento es cuando es importante el no haber manejado el data aFont directamente desde nuestras aplicaciones, ya que ahora cambiaremos el tipo de dato y la forma de recuperarlo.
La nueva forma de recuperar los fonts será oAppl:GetFont(“COUR10”)
Empezamos cambiando el nombre del data que pasa a ser hFont, solo este cambio rompería la compatibilidad si hubiéramos accedido directamente al data desde nuestros programas.
Inicializamos hFont en el método new(), hFont={=>}, hash vacio.

Y ahora pasamos a modificar los métodos AddFont y GetFont

oop3



En addFont hemos añadido un parámetro adiconal, que es el nombre por el que queremos recuperar el font ( la clave para el hash), como que este parámetro en la versión anterior no se pasaba, lo que hacemos es construirlo en el caso de que no lo recibamos como parámetro.

En GetFont, recibimos un nombre, o un indice numérico (para mantener compatibilidad). Comprobamos el tipo de parámetro recibido, si es numérico retornamos el font ubicado en la posición que indica el parámetro, de lo contrario retornamos el elemento cuya clave sea el parámetro recibido. Como posible mejora podemos comprobar previamente si existe la clave en la tabla hash, para evitar errores en tiempo de ejecución.(IF hb_hHasKey( ::hFont, uFont) )
De esta forma hemos modernizado nuestra clase tAppl para usar hash, manteniendo compatibilidad con las versiones anteriores.

Espero que este ejemplo haya ilustrado la importancia de definir Setters y Getters en lugar de acceder directamente a los datas de las clases.


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ó

lunes, 6 de abril de 2009

Actualización automática de aplicaciones

En la entrada anterior publique un sistema para realizar actualizaciones de nuestras aplicaciones via FTP. Hoy seguiremos hablando del tema, y le daremos otra vuelta de tuerca.

Para actualizar nuestra aplicación usábamos un pequeño programa “ActVer”, que se encargaba de renombrar y lanzar la ejecución de nuestra aplicación.

Al realizar este programa en [x]Harbour, nos queda de un tamaño bastante considerable teniendo en cuenta lo poco que hace, eso es debido a que como poco cualquier ejecutable Harbour se lleva dentro la maquina virtual.
Para mejorar el tamaño crearemos una versión de ActVer, pero escrita en C, con lo que pasaremos de los 1,46 MB de la versión de harbour a los 16 KB de la versión compilada en C (1,519,616 bytes menos).

Ademas para no tener que distribuir dos ejecutables, embeberemos ActVer.exe dentro de nuestra aplicación, concretamente en el fichero de recursos, y lo extraeremos solo en el momento que sea necesario actualizar nuestra aplicación.

El codigo de ActVer.C queda de la siguiente manera.

#include "stdio.h"
#include "windows.h"

int main( int argc, char *argv[])
{
Sleep(700);
DeleteFile( argv[1]);
rename( "tmp.exe",argv[1]);
WinExec(argv[1],1);
return EXIT_SUCCESS;
}


Ahora vamos a insertarlo dentro de nuestra aplicación, para ello añadimos la siguiente linea en nuestro fichero de recursos, xxxxx.RC

ACTVER RCDATA "ActVer.exe"

RCDATA nos permite insertar directamente datos binarios en nuestro ejecutable (graficos, otros exe, …)

Con esta linea, la siguiente vez que compilemos nuestra aplicación, actver quedara incrustado dentro de nuestro programa, ahora solo nos hace falta el poder extraerlo, cosa que haremos con el siguiente código (también en c).

Insertamos el código de la función en c, en nuestro fuente prg Harbour.

#pragma BEGINDUMP
#include "Windows.h"
#include "hbApi.h"
HB_FUNC( RESTOFILE )
{
HRSRC res=FindResource(NULL,"ACTVER",RT_RCDATA);
LPDWORD bytesWritten;
int size=SizeofResource(NULL,res);
HGLOBAL hRes=LoadResource(NULL,res);
unsigned char *pRes=(unsigned char *)LockResource(hRes);
HANDLE hFile=CreateFile("ACTVER.EXE",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
WriteFile(hFile,pRes,size,&bytesWritten,NULL);
CloseHandle(hFile);
}
#pragma ENDDUMP


Y con esto ya veis que es muy sencillo el incorporar ficheros binarios en nuestro EXE, esta es una posible utilidad, pero seguro que le encontrareis alguna otra.

Para que el PRG de nuestro anterior hilo incorpore esta funcionalidad, debemos añadir el codigo antes citado, y realizar una llamada a la función ResToFile() en la linea anterior a WinExec.

UpdFtp2.prg
ActVer.c
UpdFtp2.zip contiene UpdFtp2.exe
ActVer.rc
ActVer.Exe

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, 25 de noviembre de 2008

Multi Idioma con [x]Harbour + FiveWin

   En esta ocasión vamos a implementar un sistema multi idoma, basado en DLL de recursos. Esto os podrá servir para distribuir vuestras aplicaciones en diferentes países, o en un mismo país con diferentes idiomas. En Baleares al igual que en otras comunidades autonomas de España, existen dos idiomas cooficiales, Castellano y Catalán. Es habitual, y sobre todo en organismos públicos que soliciten las aplicaciones en multi-idioma, o directamente traducidas al Catalán.

Para solventar este problema, sin que tengamos que modificar los fuentes de nuestra aplicación, ni debamos mantener varias versiones en paralelo, lo que haremos sera mantener tantas DLL de recursos como idiomas queramos disponer en nuestra aplicación. 

Para ello contaremos con una DLL de recursos vaciá ( vacio.dll ), que usaremos como base,  e iremos introduciendo los diálogos, y crearemos un "string table" con los mensajes que utilicemos en nuestra aplicación. (Para editar las DLL usaremos un editor de recurso cualquiera, en nuestro caso el que viene integrado con PellesC)
editor de recursos


La tabla de cadenas tiene un formato parecido al siguiente, un código y su mensaje asociado. Desde nuestro programa iremos recuperando las cadenas de texto por su numero de código.
String Table

La idea es al principio de la aplicación cargar la DLL que queramos según el idioma seleccionado, y automáticamente tendremos los diálogos y cadenas de texto en el idioma deseado.
La carga de la DLL deseada la hacemos mediante la función SetResources( via de acceso + nombre de fichero.dll), el nombre y la via de acceso puede estar guardada por ejemplo en el registro de windows, un fichero ini, o directamente solicitar la elección del idioma por parte del usuario.

En su día tuve la idea de usar Shell.Applucattion y las extensión de NTF, para grabar junto a la DLL la descripción del idioma, pero resulto ser un fiasco, debido a que solo funciona con NTF, y en instalaciones con FAT32 no funciona, y ademas al comprimir o hacer paquetes de instalación, se pierde la información adicional de los ficheros, a modo de curiosidad, y para el que quier ver los fuentes y el manejo de Shell.Application, aquí os dejo los fuentes.

Recapitulando, al iniciar el programa, cargamos la DLL de recusos deseada
Ejemplo:
oAppl:cLng:=GetLng() //Funcion que recupera el idioma
SetResources(oAppl:cDirLocal+"Resource\ppcinst"+oAppl:cLng+".dll")
oAppl:hDll:=GetResources()
Los diálogos, ya los tenemos traducidos directamente en el diseño del recurso.
Para recuperar las cadenas de texto, usaremos la función LoadString(hDll, nResid ), le debemos pasar dos parametros, el primero es el handler del modulo que contiene las cadenas de texto, y el segundo el codigo de la cadena que quermos cargar.
LoadString( oAppl:hDll, 10001 )

Para hacerlo más fácil de leer y manejar, os recomiendo crear un fichero CH para incluir en vuestro programa, a modo de diccionario, donde iremos definiendo un texto mas legible asociado a la función y código de cadena a recuperar.

include


De esta forma por ejemplo visualizar un mensaje avisando de que la aplicación ya esta en ejecución pasaría de un críptico

MsgStop( LoadString( oAppl:hDll, 10001 ) )
a
MsgStop(TXT_EXERUNNING)

bastante mas descriptivo.

Aquí os pongo un pequeño ejemplo para ver el funcionamiento de lo descrito anteriormente.
prgmultilng

MultiLng.prg
MultiLng.zip

viernes, 21 de noviembre de 2008

Un año de vida del Blog

Como pasa el tiempo, parece que fue ayer mismo cuando escribí la primera entrada de este blog, y ya ha pasado mas de un año desde esa primera entrada.

Desgraciadamente, y por falta de tiempo no he podido escribir todo lo a menudo que hubiera deseado, pero el tiempo disponible es finito, y el blog de momento no nos da de comer ;-).
En un primer momento pensaba escribir mucho mas sobre ADO, pero la realidad es que el trabajo es el que marca un poco el rumbo y la tendencia de este blog, y se alimenta de las investigaciones personales que tengo que ir realizando en base a los requerimientos de los proyectos que este desarrollando.

El tema ADO no queda abandonado, pero si un poco aparcado, de todas formas en este periodo, han ido apareciendo muchos buenos aportes sobre ADO y xBase (ADOBASE, SQLWIN, ...) y en el foro no estamos tan huérfanos de información como antaño.

En este año, el número de visitas,y comentarios ha ido creciendo progresivamente, aunque no es un volumen bestial, si me alegra el ver que va en aunmento, y ademas cuenta con lectores fieles. Como digo, eso me alegra, y ademas es el motor y el combustible necesario para animarme a seguir escribiendo.

Pues nada mas, simplemente agradeceros a todos vuestra lectura, comentarios, desear que os haya sido de utilidad, y espero seguir contando con vuestra visitas.

Un abrazo. Y hasta la proxima entrada, que será muy pronto e ira de multi-idioma con xHarbour y DLL de recursos.