domingo, mayo 10, 2015

Nueva versión v2.5.10 de las herramientas Visual FoxPro 9 para PlasticSCM (Incluye FoxBin2Prg.exe v1.19.43)


Estas herramientas son un grupo de scripts vbs y programas Visual FoxPro 9 que se configuran dentro de PlasticSCM (herramienta de control de versiones) para poder invocar a FoxBin2Prg (incluye solo el EXE) desde dentro de la interfaz de Plastic.


Está liberada la versión v2.5.10 de las herramientas Visual FoxPro 9 para PlasticSCM, con los siguientes cambios:






El README.txt explica como se configura en Inglés y Español, y también está explicado en esta nota: Cómo configurar las Herramientas de VFP 9 para Plastic


Nota: Los fuentes del proyecto FoxBin2Prg y el historial de ambios, están en CodePlex, en este link.


Como actualizar las existentes:
Con descargarlas y reemplazar los archivos en el sitio que los hayan puesto antes es suficiente.


Link de descarga de GitHub:
https://github.com/fdbozzo/plasticscm-tools-for-visual-foxpro-9


Saludos!

Nueva versión v1.19.43 de FoxBin2Prg (arreglos y mejoras)

FoxBin2Prg es un programa pensado para sustituir a SccText(X) y TwoFox y mejorar sus funcionalidades, generando versiones de texto estilo-PRG que pueden ser modificadas y permiten recrear el binario original. Puede ser utilizado con herramientas SCM (Administradores de Control de Código Fuente, como VSS, CVS, SVN) y herramientas DVCS (como Git, Mercurial, Plastic, and others), o como programa independiente, para hacer operaciones de Diff (ver diferencias) y Merge (mezclar cambios).

Está liberada la versión v1.19.43 de FoxBin2Prg con los siguientes cambios:

> Mejora: Nueva configuración "RemoveZOrderSetFromProps" para quitar la propiedad ZOrderSet de los objetos que cambian constantemente, provocan diferencias y a veces dan problemas de objeto encima/debajo (Ryan Harris)

   Cuando se trabaja con clases visuales o forms, muchas veces pasa que movemos objetos, algunos los ponemos más arriba o por debajo de otros (cambio del orden Z), guardamos y al volver a abrir algunos controles no aparecen en el nivel de profundidad que los habíamos dejado (por ejemplo, uno que estaba arriba de todo aparece debajo de otro). Fox guarda ese orden de dos formas; para los controles sin herencia usa el orden físico de guardado en la tabla (scx/vcx) y para los controles con herencia usa una propiedad no visible llamada ZOrderSet que solo puede verse si se abre el form o clase como tabla o si se exporta a texto. Este problema de objetos que cambian de orden Z sin sentido aparente en general ocurre porque dos o más objetos tienen el mismo valor asignado a ZOrderSet, con lo que Fox a veces pone primero a uno y otras veces al otro. El nuevo seteo "RemoveZOrderSetFromProps: 1" del archivo foxbin2prg.cfg permite quitar la propiedad ZOrderSet de los objetos, para que se muestren tal como estaban en el diseño original de la clase. Lo único que debe tenerse en cuenta es que si en la clase se usó un orden Z y en la instancia se cambió ese orden, el nuevo orden se perderá y quedará como estaba definido en la clase original. Por eso esta configuración puede ser util tenerla en un directorio particular donde se pongan temporalmente las clases visuales o forms que se quieran arreglar, para luego volver a dejar en su ubicación original.


> Mejora: Hacer que la progressbar no se convierta en la ventana de salida por defecto de los ? (Lutz Scheffler)

   Cuando se usa FoxBin2Prg como objeto para acceder a su API desde otros programas en Fox, por defecto se muestra una ventana de progreso (desactibable) para que se pueda saber en qué parte del proceso se está. El problema que algunos desarrolladores estaban teniendo, era que al intentar mostrar la salida del comando ? en sus forms o ventanas, como FoxBin es una ventana AllwaysOnTop acaparaba esa salida y terminaba impresa sobre la misma. Desde esta versión la ventana de progreso está configurada con AllowOutput=.F. para evitar quitarle el foco a otras ventanas.




> Bug Fix: Arreglo del mensaje de validación de VFP9 SP1

   En la implementación del mensaje de validación del SP1 había un error que impedía mostrar el mensaje. Ya está corregido.


> Bug Fix: FoxBin2Prg no retorna códigos de error cuando se llama como programa externo (Ralf Wagner)

    Había un error en la lógica del código que impedía devolver el código de error DOS para ser tratado por programas externos leyendo ERRORLEVEL u otros mecanismos. Ya está corregido.



> Bug Fix: FoxBin2Prg a veces genera errores OLE cuando se ejecuta más de una vez en modo objeto sobre un archivo con errores (Fidel Charny)

   Había un error en la lógica interna que acumulaba los estados de error de las ejecuciones previas, provocando que a partir del primer error detectado, todos los demás archivos se analizaran como si tuvieran errores. Ya está corregido.


> Bug Fix: Cuando un form tiene AutoCenter=.T., hay veces en que al regenerar el binario y ejecutarlo no se muestra centrado (Esteban Herrero)

    Al generar la vista texto de un form, y luego regenerar el binario, si se usa la propiedad AutoCenter=.T. hay veces que al ejecutar el form no se muestra centrado como se espera. Esto ocurría porque al ensamblar el binario, la propiedad AutoCenter se estaba guardando antes que Top/Left/Width/Height, lo que en algunos casos le impedía a Fox calcular el autocentrado. Ya está corregido.




Como actualizar el FoxBin2Prg existente:Con descargar el zip y reemplazar los archivos en el sitio que los hayan puesto antes es suficiente.


Link  de descarga de VFPx:
https://vfpx.codeplex.com/releases/view/116407

Link de descarga de GitHub:
https://github.com/fdbozzo/foxbin2prg


Relacionados:

FoxBin2Prg, el sucesor mejorado del Scctext

FoxBin2Prg: Detalle de vistas, Datos de uso, Configuraciones y más

FoxBin2Prg: Guía rápida de uso y confogiración


 Saludos!

viernes, mayo 01, 2015

Técnicas de optimización en VFP: Tablas, archivos de texto, accesos al disco y buffering

Por: Fernando D. Bozzo

Este es un artículo de una serie que se enfocará en técnicas de programación y optimización en distintas áreas.

Un buen desarrollador asimilará estas técnicas como parte de su forma de trabajo habitual, independientemente de que haga un sistema, ún módulo, una rutina o un programa de pruebas personales, ya que le permitirá programar siempre orientado a la eficiencia, la velocidad de ejecución, la encapsulación, la reutilización y la legibilidad y mantenibilidad del código, o sea, las buenas prácticas.

Al tener en cuenta estas técnicas en cada parte del código y en cada rutina, al final lo que se logra es que el sistema completo esté más optimizado porque sus partes lo están.



Una de las operaciones más costosas a nivel de recursos en una PC es la de accesos al disco, ya que el disco es probablemente el componente más lento del sistema. Aunque los discos SSD minimizan el problema, la solución real pasa por la programación, ya que tanto los discos rígidos y sobre todo las redes, son los principales cuellos de botella.

Muchas veces un programa o un sistema comienza teniendo un único usuario o unos pocos, pero puede que con el tiempo esa condición cambie, y que comiencen a haber más usuarios, incluso muchos usuarios, y es aquí cuando comienzan a verse realmente los problemas de las decisiones de diseño tomadas y donde ya es tarde para cambiarlo por el esfuerzo que puede requerir, implicando a veces un rediseño, por eso cada línea de código cuenta.



Optimizaciones en el uso de archivos y accesos a disco


Caso 1: Los LOGs


Normalmente, cuando se quiere hacer un log al disco de alguna información importante para nosotros o para su posterior explotación o análisis, se suele usar STRTOFILE() por comodidad o por falta de tiempo. He aquí algunos ejemplos típicos:


1.a) Ejemplo de un LOG dentro de un bucle que puede tener cualquier cantidad de registros:

SCAN FOR <condición>
  ...
  IF <condicion_que_requiere_loguear>
     STRTOFILE( 'alguna información importante', 'LOG.txt', 1 )
  ENDIF
ENDSCAN


1.b) Ejemplo de un LOG en un evento Timer que podría ejecutarse varias veces por segundo:

PROCEDURE TIMER
   ...
  IF <condicion_que_requiere_loguear>
     STRTOFILE( 'alguna información importante', 'LOG.txt', 1 )
  ENDIF
ENDPROC


1.c) Ejemplo de un LOG en un método de cálculo reutilizable, que podría llamarse cientos de veces desde distintas partes de un sistema:

PROCEDURE Sumar_Porcentaje
  ...
  IF <condicion_que_requiere_loguear>
     STRTOFILE( 'alguna información importante', 'LOG.txt', 1 )
  ENDIF
ENDPROC


1.d) Ejemplo de un LOG en un método de proceso largo, donde se realizan diversas operaciones y por cada una se va actualizando el LOG:

PROCEDURE Proceso_Largo
  <operación-1> 
  STRTOFILE( 'alguna información importante', 'LOG.txt', 1 )
  <operación-2> 
  STRTOFILE( 'alguna información importante', 'LOG.txt', 1 )
  ...
  <operación-n>
  ...
ENDPROC

En todos los casos, STRTOFILE() escribirá al disco para ir añadiendo información al LOG. Para unas pocas repeticiones esto puede no ser un problema, pero siempre hay que asumir un caso de uso desfavorable, en este caso intensivo.

Veamos qué ocurre cuando la repetición es de 100 iteraciones:
  • Para 1 usuario implicará 100 accesos a disco
  • Para 5 usuarios implicará 500 accesos a disco
  • Para 10 usuarios implicará 1000 accesos a disco

Puede verse que el impacto se multiplica rápidamente a medida que crece la cantidad de usuarios, lo que puede causar que el sistema de archivos se vaya poniendo cada vez más lento por la metralla de escrituras, pudiendo llegar incluso a no dar abasto en responder las peticiones y provocar el colapso del sistema.

Otro error común al escribir LOGs es que se suele elegir un nombre (ej: LOG.txt) y se olvida el hecho de que esto puede ser usado por varios usuarios concurrentes, lo que puede provocar la de contención de recursos porque dos ó más personas intentan usar el mismo archivo con el mismo nombre; también puede provocar errores, en el caso de que el LOG se use en exclusiva (con FOPEN/FWRITE) o también puede provocar pérdida de información de partes del LOG, ya que mientras un usuario está escribiendo en el LOG, otro no puede hacerlo y STRTOFILE() simplemente no escribe y tampoco generará un error por ello (STRTOFILE solo devuelve la cantidad de bytes escritos, 0 en caso de no poder escribir, pero nadie suele verificar esto)



Soluciones


Para resolver el problema de los usuarios concurrentes, se puede usar un LOG distinto por usuario, que puede tener una numenclatura parecida a "LOG_UsuarioDeRed.txt", o una más completa como "LOG_UsuarioDeRed_NombreMaquina.txt". Si se quiere un historial de LOGs diarios, se podría agregar la fecha al nombre del LOG. Como se ve, variantes hay para todos los gustos.

Para minimizar el impacto de estas escrituras se suele usar la estrategia del buffering (o caché), que implica ir guardando la información que se quiere escribir en una variable de memoria o una propiedad y cada tanto realizar la escritura de todo lo guardado. En este caso hay varias alternativas, que van desde un objeto LOG que autogestione la cantidad de texto que puede guardar para hacer escrituras automáticas al llegar a cierto tamaño acumulado, o a algo más sencillo y práctico como hacer dos métodos, uno de escritura en buffer y uno de flush al disco, que es la técnica que usé en FoxBin2Prg y que muestro a continuación, simplificado y adaptado para el ejemplo:


DEFINE CLASS C_LOG
  #DEFINE CR_LF CHR(13)+CHR(10)
  c_TextoLog = ''
  c_LogFile  = ''

  PROCEDURE Init
      c_LogFile = ADDBS( SYS(2023) ) + 'LOG_' ;
          + CHRTRAN( SYS(0), ' ', '_' ) + '.TXT'
  ENDPROC
 
  PROCEDURE Destroy
      THIS.writeLog_Flush()
  ENDPROC
 
  PROCEDURE writeLog
    LPARAMETERS tcText

    TRY
      WITH THIS
        .c_TextoLog = .c_TextoLog + EVL(tcText,'') + CR_LF
      ENDWITH
    CATCH && En este caso no me interesa reportar errores
    ENDTRY
  ENDPROC

  PROCEDURE writeLog_Flush

    WITH THIS
      IF NOT EMPTY(.c_TextLog)
        STRTOFILE( .c_TextLog + CR_LF, .c_LogFile, 1 )
      ENDIF
      .c_TextLog    = ''
    ENDWITH
  ENDPROC 


ENDDEFINE


En el ejemplo, en el Init() se define en nombre del LOG, luego se usa el método writeLog() para escribir el texto que se quiera en la propiedad c_TextoLog y a la que se agrega un fin de línea, y finalmente el método writeLog_Flush() que se encarga de escribir al disco todo el texto acumulado y de vaciar la propiedad que lo acumula. Cuando se descarga la clase, también se escribe automáticamente al disco lo que quede por escribir.

Este es un método muy simple y efectivo que permite sustituir los STRTOFILE() de los ejemplos anteriores por writeLog() y que solo requiere ubicar las llamadas a writeLog_Flush() fuera de los bucles de repetición o de las rutinas de uso frecuente, pero siempre teniendo en cuenta que en puntos estratégicos debe ser ejecutado, para evitar que el LOG se acumule indefinidamente y cause problemas de memoria.

Usando esta técnica, las escrituras al disco o por la red se pueden minimizar de forma muy notoria, y lograr que el sistema siga siendo responsivo.



Caso 2: Actualización de TABLAS


En Fox, una de las cosas que más se usa son las tablas y los cursores, tanto para guardar datos como para guardar información temporal de proceso, ya que Fox está optimizado para eso a tal punto que manejar un cursor de un millón de registros es más rápido que manejar un array de la misma cantidad de filas.

Pero esta orientación a datos muchas veces no es bien implementada, y es muy común encontrarse con código como este:


DO WHILE <condición>
  ...
  REPLACE campo1 WITH valor1
  REPLACE campo2 WITH valor2
  REPLACE campo3 WITH valor3
  REPLACE campo4 WITH valor4
  REPLACE...
  ...
ENDDO


Al igual que en el Caso 1, si este bucle se repite en 100 iteraciones y usando solo 5 REPLACE:
  • Para 1 usuario implicará 500 accesos a disco
  • Para 5 usuarios implicará 2500 accesos a disco
  • Para 10 usuarios implicará 5000 accesos a disco

Puede notarse que en este caso todavía es peor que en el anterior, ya que muchos están acostumbrados a poner varios REPLACE en sucesión, por motivos como que "es más fácil para copiar" o simplemente porque les gusta verlos separados...



Esta es otra variante bastante común que se puede encontrar, donde es necesario hacer REPLACE solo bajo ciertas condiciones y muchos lo implementan así:

SCAN FOR <condición>
  ...
  IF <cond.1>
    REPLACE campo1 WITH valor1
  ENDIF
  IF <cond.2>
    REPLACE campo2 WITH valor2
  ENDIF
  IF <cond.3>
    REPLACE campo3 WITH valor3
  ENDIF
  IF <cond.4>
    REPLACE campo4 WITH valor4
  ENDIF
  IF ...
    REPLACE...
  ...
ENDSCAN


La única diferencia con el caso anterior, es que en cada iteración la cantidad de reemplazos no es fija porque depende de condiciones, pero como siempre, hay que ubicarse en el peor caso donde todas las condiciones puedan ser verdaderas, por lo que en este caso se puede llegar a los mismos 5 REPLACE del ejemplo anterior y a las mismas estadísticas de accesos a disco por usuario.


La realidad es que tanto esta como la otra son malas prácticas y deben evitarse a toda costa, porque penalizan mucho el rendimiento, y más en una red.



Soluciones


Básicamente las soluciones pasan por realizar estos reemplazos en una sola operación, y estas técnicas sirven para cubrir ambos casos con mucha facilidad, como puede verse en los siguientes ejemplos.


Ejemplo 1: Usando un objeto de registro


DO WHILE <condición>
  ...
  SCATTER NAME loReg && o SCATTER FIELDS para más precisión
  loReg.campo1 = valor1
  loReg.campo2 = valor2
  loReg.campo3 = valor3
  loReg.campo4 = valor4
  loReg.campoN...
  GATHER NAME loReg && Un único reemplazo
  ...
ENDDO


Ejemplo 2: Usando variables

SCAN FOR <condición>
  ...
  lc_campo1 = valor1
  lc_campo2 = valor2
  lc_campo3 = valor3
  lc_campo4 = valor4
  lc_campoN...
  REPLACE campo1 WITH lc_campo1 ;
    , campo2 WITH lc_campo2 ;
    , campo3 WITH lc_campo3 ;
    , campo4 WITH lc_campo4 ;
    , campoN WITH lc_campoN && Un único REPLACE para todos
  ...
ENDSCAN


Ejemplo 3: Usando un array

SCAN FOR <condición>
  ...
  SCATTER TO ARRAY laReg && o SCATTER FIELDS para más precisión
  laReg(1) = valor1
  laReg(2) = valor2
  laReg(3) = valor3
  laReg(4) = valor4
  laReg(N)...
  GATHER FROM laReg && Un único reemplazo
  ...
ENDSCAN


Y para reemplazos condicionados se puede usar el mismo código de los ejemplos, solo que condicionando las asignaciones de las variables o propiedades.



El caso anterior fue para reemplazos de datos existentes, pero para registros nuevos es lo mismo, con la salvedad de que como primera opción más recomendable se agrega el Insert-SQL:

Ejemplo 1: Usando Insert-SQL para un registro

SCAN FOR <condición>
  ...
  SCATTER BLANK NAME loReg
  loReg.campo1 = valor1
  loReg.campo2 = valor2
  loReg.campo3 = valor3
  loReg.campo4 = valor4
  loReg.campoN...
  INSERT INTO <tabla> FROM NAME loReg
  ...
ENDSCAN


Ejemplo 2: Usando Insert-SQL para varios registros

SCAN FOR <condición>
  ...
  DIMENSION laReg(3,5)
  laReg(1,1) = valor1
  laReg(1,2) = valor2
  laReg(1,3) = valor3
  laReg(1,4) = valor4
  laReg(1,5...
  ...
  INSERT INTO <tabla> FROM laReg
  ...
ENDSCAN


Otra variante es usar un cursor con la misma estructura que la tabla, realizar los Inserts en el cursor y luego volcarlo en la tabla.

Como se puede ver opciones hay muchas, y cada una puede ser más óptima que la otra dependiendo de nuestras necesidades o de cómo queramos implementarlo, pero para el buffering en memoria podemos usar tanto variables como cursores, pudendo incluso usar buffering de tablas o registros mediante CursorSetprop().

Nota: En el caso de usar cursores o buffering de tablas o registros, recordar cada tanto usar la función SYS(1104) para liberar los buffers de memoria. Como ejemplo, cada 100 registros reemplazados se podría forzar la limpieza de los buffers para liberar memoria.




Caso 3: Escritura de grandes cantidades de texto (>1 MB y <= 2 GB)


Hay situaciones en las que tenemos un proceso que requiere ir generando información al disco en formato texto. La diferencia con un LOG, como vimos al inicio, es que un LOG puede desactivarse o condicionarse, mientras que en este caso hablamos de un proceso que sí o sí debe escribir al disco para generar un archivo de texto en múltiples pasos o subprocesos. Un ejemplo de este tipo de proceso puede ser un parser o un conversor, donde se va interpretando el origen de datos (o un archivo origen) y a la vez se va generando la información de salida a un archivo de texto.

Si bien la primera reacción puede ser la de usar la técnica del buffering del Caso 1 mediante el uso de una variable o una propiedad acumuladora, es necesario saber que para textos superiores a 1 MB FoxPro se empieza a poner lento, principalmente por la acumulación en una variable que va creciendo y que además va consumiendo cada vez más memoria.

En estos casos, lo más óptimo es usar funciones de bajo nivel como FOPEN/FWRITE con buffering, lo que da una velocidad que como mínimo duplica al buffering por variable o propiedad acumulada.

Aunque pueda parecer contradictorio decir que una función de manejo de archivos de bajo nivel sea más rápida en estos casos que una variable, la explicación está en que no solo Fox no se maneja bien con variables con mucho contenido que se actualiza constantemente, sino que además las funciones de manejo de archivos a bajo nivel con uso de buffering están optimizadas para justamente para no escribir todo el tiempo al disco, sino que implementan su propio esquema de buffering.




Caso 4: Generación de texto con TEXT/ENDTEXT


No se puede hablar de generación de texto sin hablar de TEXT/ENDTEXT, que es uno de los comandos más potentes y versátiles de FoxPro desde los inicios, pero primero veamos un ejemplo de la salida que queremos conseguir:




Fecha: 01/05/2015                    Fernando Caimo

Número de viajes: 30
Kilómetros hechos: 275

------------------------------------------------------------



Así es cómo se puede generar este texto usando variables y contenido dinámico:


cTexto = ''
cTexto = cTexto + chr(13)+chr(10)
cTexto = cTexto + 'Fecha: ' + dtoc(date()) + space(20) ;
  + cApellidoYNombre + chr(13)+chr(10)
cTexto = cTexto + chr(13)+chr(10)
cTexto = cTexto + 'Número de viajes:  ' + TRANSFORM(nViajes) ;
  + chr(13)+chr(10)
cTexto = cTexto + 'Kilómetros hechos: ' + TRANSFORM(nKM) ;
  + chr(13)+chr(10)
cTexto = cTexto + chr(13)+chr(10)
cTexto = cTexto + replicate('-', 60)



Y esta es la forma en que se hace con TEXT/ENDTEXT:

TEXT TO cTexto ADDITIVE TEXTMERGE NOSHOW FLAGS 1+2 PRETEXT 1+2

Fecha: <<date()>>                    <<cApellidoYNombre>>

Número de viajes: <<nViajes>>
Kilómetros hechos: <<nKM>>

------------------------------------------------------------

ENDTEXT


....no hay contraste! TEXT/ENDTEXT es tan increíblemente versátil y claro para generar este tipo de documentos que deja en vergüenza al resto de métodos disponibles.

Pero tanta potencia tiene sus casos de uso y su coste, por lo que tampoco es cuestión de usarlo para cada línea de texto que se quiera generar, y es que para líneas individuales, y más si se usa de la siguiente forma, no es recomendable:

FOR X=1 TO loProcedure._ProcLine_Count
  TEXT TO lcMemo ADDITIVE TEXTMERGE NOSHOW FLAGS 1+2 PRETEXT 1+2
    <<loProcedure._ProcLines(X)>>
  ENDTEXT
ENDFOR

STRTOFILE(lcMemo, 'salida.txt', 1)



Como puede verse, aunque la parte de escritura con STRTOFILE está optimizada porque está fuera del bucle, lo que de por sí impactará poco en accesos a disco, el TEXT/ENDTEXT está en un bucle de repetición cuya iteración puede ser alta, y el problema con este caso en particular es que por cada línea que se genera y agrega a lcMemo, se requiere inicializar una estructura de parseo, parsear cada uno de los parámetros indicados y luego procesar el texto entre TEXT y ENDTEXT usando los parámetros indicados. Y todo esto para una línea en un bucle es demasiado coste para la CPU.

En su lugar, y para este caso particular, lo más conveniente y óptimo es usar una variable:

FOR X=1 TO loProcedure._ProcLine_Count
  lcMemo = lcMemo + chr(13)+chr(10) + loProcedure._ProcLines(X)
ENDFOR

STRTOFILE(lcMemo, 'salida.txt', 1)




Resumen


El objetivo siempre debe ser hacer el código más óptimo para cada situación, y en el caso de los archivos siempre se trata de minimizar los accesos a disco usando técnicas de buffering.

Lo que se logra con estas técnicas, específicamente, es:
  • Menor cantidad de accesos a disco (o red)
  • Menor uso de recursos del sistema
  • Disminución de posibilidades de contención
  • Mayor velocidad de proceso
  • Alargamiento de la vida útil de los discos, tanto rígidos como SSD

Puede que hayan quedado fuera algunas cosas, pero quería hacer un resumen con algunas de las más importantes a tener en cuenta.


Hasta la próxima! :D



miércoles, abril 22, 2015

Rutinas VFP: Cómo obtener todos los nombres de archivo de un directorio y subdirectorios

Por: Fernando D. Bozzo

Esto parece que es algo bastante solicitado, así que ahí va, lista para usar en una librería de procedimientos PRG.

Solo requiere indicar el directorio, un array por referencia y una variable por referencia para devolver la información.



*-- USO: Suponiendo que se copie en LIB.PRG
SET PROCEDURE TO lib.prg
DIMENSION aFiles(1,1)
nCount = 0
get_FilesFromDirectory( "C:\DESA\Plastic\", @aFiles, @nCount)
? nCount




    PROCEDURE get_FilesFromDirectory
        LPARAMETERS tcDir, taFiles, lnFileCount
        EXTERNAL ARRAY taFiles

        LOCAL laFiles(1), I, lnFiles

        IF TYPE("ALEN(laFiles)") # "N" OR EMPTY(lnFileCount)
            lnFileCount = 0
            DIMENSION taFiles(1)
        ENDIF

        tcDir    = ADDBS(tcDir)

        IF DIRECTORY(tcDir)
            lnFiles = ADIR( laFiles, tcDir + '*.*', 'D', 1)

            *-- Busco los archivos
            FOR I = 1 TO lnFiles
                IF SUBSTR( laFiles(I,5), 5, 1 ) == 'D'
                    LOOP
                ENDIF
                lnFileCount    = lnFileCount + 1
                DIMENSION taFiles(lnFileCount)
                taFiles(lnFileCount)    = tcDir + laFiles(I,1)
            ENDFOR

            *-- Busco los subdirectorios
            FOR I = 1 TO lnFiles
                IF NOT SUBSTR( laFiles(I,5), 5, 1 ) == 'D' ;

                       OR LEFT(laFiles(I,1), 1) == '.'
                    LOOP
                ENDIF
                get_FilesFromDirectory( tcDir + laFiles(I,1), ;

                   @taFiles, @lnFileCount )
            ENDFOR
        ENDIF
    ENDPROC



Saludos!

martes, abril 21, 2015

Visual FoxPro - Selección de artículos y notas sobre problemas y troubleshoting para tener a mano

[2015/04/21]

Esta es una selección de artículos y notas que iré actualizando con el tiempo, ya que a medida que van saliendo nuevas versiones de Windows, aparecen nuevos problemas para las aplicaciones que usan file-sharing como FoxPro, Acces y varias otras. Las más nuevas las iré agregando arriba.


(Muy buena explicación técnica sobre cacheo en samba)
Re: Network Slow Query / Christof Wollenhaupt / 2014.12.18

http://leafe.com/archives/msg/497230


Data corruption when multiple users perform read and write operations to a shared file in the SMB2 environment
https://support.microsoft.com/en-us/kb/2028965

 .

sábado, abril 18, 2015

Nueva versión v2.5.9 de las herramientas Visual FoxPro 9 para PlasticSCM (Incluye FoxBin2Prg.exe v1.19.42)


Estas herramientas son un grupo de scripts vbs y programas Visual FoxPro 9 que se configuran dentro de PlasticSCM (herramienta de control de versiones) para poder invocar a FoxBin2Prg (incluye solo el EXE) desde dentro de la interfaz de Plastic.


Está liberada la versión v2.5.9 de las herramientas Visual FoxPro 9 para PlasticSCM, con los siguientes cambios:

  • Actualizada la versión de FoxBin2Prg (solo el EXE) a la versión v1.19.42 
  • Adaptados los scripts vbs y exe a la nueva versión de FoxBin




El README.txt explica como se configura en Inglés y Español, y también está explicado en esta nota: Cómo configurar las Herramientas de VFP 9 para Plastic


Nota: Los fuentes del proyecto FoxBin2Prg y el historial de ambios, están en CodePlex, en este link.


Como actualizar las existentes:
Con descargarlas y reemplazar los archivos en el sitio que los hayan puesto antes es suficiente.


Link de descarga de GitHub:
https://github.com/fdbozzo/plasticscm-tools-for-visual-foxpro-9


Saludos!

Nueva versión v1.19.42 de FoxBin2Prg (arreglos, mejoras, internacionalización API)

FoxBin2Prg es un programa pensado para sustituir a SccText(X) y TwoFox y mejorar sus funcionalidades, generando versiones de texto estilo-PRG que pueden ser modificadas y permiten recrear el binario original. Puede ser utilizado con herramientas SCM (Administradores de Control de Código Fuente, como VSS, CVS, SVN) y herramientas DVCS (como Git, Mercurial, Plastic, and others), o como programa independiente, para hacer operaciones de Diff (ver diferencias) y Merge (mezclar cambios).

Está liberada la versión v1.19.42 de FoxBin2Prg con los siguientes cambios:

  • Mejora: Agregada validación de existencia del runtime VFP 9 SP1.
    • Esta validación está agregada porque quienes no tengan como mínimo el SP1, el programa les funcionará mal, pudiendo dar errores extraños o generando mal menus u otros componentes. Esto es debido a todos los bugs que tenía VFP 9 original, y que fueron corregidos mayormente con el SP1.
  • Bug Fix: Compatibilidad con SourceSafe rota porque ocurre un error cuando sccapi consulta el soporte de archivo (Tuvia Vinitsky).
    • Para quienes usen FoxBin con SourceSafe, esta versión soluciona el problema comentado.
  • Bug Fix scx/vcx: Procesar solo un nivel de TEXT/ENDTEXT, ya que este comando no se puede anidar (Lutz Scheffler).
    • Se estaban procesando TEXT/ENDTEXT anidados, lo que podía provocar errores en casos especiales muy raros.
  • Mejora: Hacer las descripciones de algunos mensajes de error más descriptivos y precisos (Lutz Scheffler).
    • Algunos mensajes de error no eran del todo claros y no ayudaban mucho a localizar el problema. Se han mejorado algunos mensajes agregando más detalles técnicos que facilitan localizar el problema dentro de las librerías con problemas.
  • Mejora (API): Permitir especificar un archivo de entrada con path relativo (Lutz Scheffler).
    • Hasta la versión anterior solo se podían indicar archivos con path absoluto, ahora se pueden indicar también de forma relativa, como "dir\subdir\*.pjx".
  • Bug Fix scx: Los metadatos del entorno de datos no se generan bien cuando el entorno de datos es renombrado.
    • Cuando se crea un form, por defecto el entorno de datos se llama "Dataenvironment", pero se puede cambiar ese nombre. FoxBin no reconocía el entorno de datos cuando si hacía esto.
  • Bug Fix: Agregada generación del PJX/PJ2 cuando se indica "file.pjx", "*" (Lutz Scheffler).
    • Hasta ahora, cuando se indicaba procesar un proyecto con todos sus archivos, solamente se procesaban los archivos del proyecto, pero no el proyecto en sí. Ahora también se procesa el PJX/PJ2 al indicar "*".
  • Algunas mejoras en las translaciones al Alemán (Lutz Scheffler)
    • Lutz corrigió algunas de las traducciones que estaban hechas con Google Translate :)
  • Mejora: Agregado multiprocesamiento de proyectos (*.pjx, *.pj2) cuando se especifica "file.pjx", "*" (Lutz Scheffler)
    • Hasta ahora solo se podía procesar un projecto completo por vez; ahora se pueden procesar múltiples proyectos en un mismo procesamiento, lo que permite optimizar el procesamiento de los archivos compartidos entre esos proyectos, procesándolos una sola vez.
  • Mejora: Cambiada la clase de base de FoxBin2Prg a Session (Lutz Scheffler).
    • Antes era de tipo Custom, ahora al ser de tipo Session permite que los cursores internos no alteren el entorno de trabajo cuando se usa FoxBin desde la ventana de comandos de Fox con DO.
  • Mejora: Permitir procesar todos los archivos de los proyectos sin convertir los PJX/2, con "*-" (Lutz Scheffler).
    • Esta nueva opción "*-" permite procesar los archivos de los proyectos excluyendo al propio proyecto PJX/PJ2.
  • Bug Fix pjx/pj2: Si se usa un fin de línea (CR+LF) en los datos de propiedades del diálogo "Build" del pjx, la generación del pj2 queda malformada.
    • En un proyecto, al intentar generar un ejecutable o DLL se habilita un cuadro de diálogo que permite seleccionar el botón "Version" donde hay varios metadatos del proyecto, incluyendo la versión y varios otros como Comments, Company Name, etc. Si en alguno de estos campos se pega un valor que contiene fines de línea (Fox no permite usar Enter en ellos, pero al copiar/pegar desde otro sitio, no lo valida), cuando se exporta el proyecto a texto esos valores quedan corrompidos porque se exportan en formato multi-línea en vez de en una sola línea, no permitiendo regenerar el proyecto original. Se corrige ese problema codificando los fines de línea para luego restaurarlos.
  • Mejora: El archivo de configuración por defecto foxbin2prg.cfg ha sido renombrado a foxbin2prg.cfg.txt para no sobreescribir los setetos del cfg del usuario (Lutz Scheffler).
    • Muchos usuarios usan un archivo de configuración  personalizado en el directorio de FoxBin, y cuando se descargan las nuevas versiones se les pisa con el que se incluye por defecto. Como el archivo incluido está solamente a efectos de documentación y para usarlo como modelo en el que basarse, se ha renombrado a foxbin2prg.cfg.txt para evitar que vuelva a sobreescribir las configuraciones de los usuarios en la actual y futuras versiones.
  • Mejora: Agregado el soporte de salida DOS errOut e implementado en el método writeErrorLog cuando se loguean errores.
    • El sistema operativo tiene una entrada estándar (stdIn) una salida estándar (stdOut) y una salida especial para errores (stdErr). Cuando ocurre un error durante el procesamiento de FoxBin, se envía la información del mismo a la salida de error stdErr, además de generar el log de errores. Esto permite que se pueda capturar dicha salida desde otros programas en tiempo real.
  • Mejora: Agregado el soporte completo de máscaras de archivo *? para multi-procesamiento de archivos de la misma extensión (Lutz Scheffler).
    • Hasta ahora solo se soportaba el procesamiento de un archivo (fullpath-filename) o de todos los archivos de una extensión (fullpath-*.ext), ahora se permite cualquier combinación de comodines, lo que permite filtrar los archivos por criterios mucho más flexibles.
  • Mejora (API): Agregado nuevo parámetro para permitir un archivo CFG principal alternativo (Lutz Scheffler).
    • El método API execute() permite un nuevo parámetro cCFG_File para indicar un archivo de configuración en una ruta distinta a la predeterminada, que actualmente es la ruta de instalación de FoxBin.
  • Mejora (API): Agregado nuevo método get_Processed() para obtener información acerca de los archivos procesados (Lutz Scheffler).
    • El nuevo método API get_Processed() devuelve un array de 6 columnas (cFile, cInOutType, cProcessed, cHasErrors, cSupported, cExpanded) con información de procesamiento según los parámetros indicados de entrada. Si además se usa la propiedad l_ProcessFiles=.F. antes de realizar la consulta, se puede obtener información parcial de procesamiento, pero sin realizar el procesamiento, lo que lo hace muy rápido y útil cuando se quiere obtener la lista de archivos que se procesarán.
  • Mejora: Agregada nueva salida DOS stdOut para el procesamiento de archivos (Lutz Scheffler).
    • Mientras se procesan archivos, se envía la información del proceso a la salida estándar del sistema (stdOut) con la misma información que devuelve get_Processed, pero ubicando la columna del nombre de archivo al final. Esto permite que se pueda capturar dicha salida desde otros programas en tiempo real.
  • Bug Fix: Arreglada la cancelación del proceso con la tecla Esc.
    • En la versión anterior no se podía cancelar el proceso con la tecla Esc, ni con el botón Cancelar. Ya está solucionado.
  • Mejora: Ordenar alfabéticamente los campos de las tablas/vistas para facilitar el diff (ver diferencias) y el merge (mezclar cambios), mientras se mentiene una lista con el ordenamiento original de los campos para la correcta regeneración del DBC (Ryan Harris).
    • Cuando se modifican vistas o miembros de una Base de Datos, la información no mantiene siempre el mismo orden, lo que puede provocar que al exportar a texto se vea como si se hubieran realizado muchos cambios, cuando realmente por ahi se agregó solo un campo en una vista. Ahora se ordenan los miembros del DBC (conexiones, tablas, vistas, índices y campos de tablas y vistas) alfabéticamente, lo que disminuye notoriamente las diferencias al hacer estos cambios.
  • Mejora: Aplicar ClassPerFile a las conexiones, tablas, vistas y SPs de los DBC (Ryan Harris).
    • Cuando una Base de Datos tiene mucha información, tablas, vistas, etc, el archivo de texto generado puede ser muy grande y difícil para comparar. Ahora es posible aplicar el mismo concepto de ClassPerFile a los DBC, donde se generará un archivo separado por cada conexión, tabla o vista y uno extra para todos los procedimientos almacenados, lo que facilita mucho comparar los miembros que cambien solamente.
  • Bug Fix vw: Ahora se guardan y restauran las propiedades Path y RecordCount de las vistas.
    • Cuando se usan vistas desconectadas, no se estaban exportando estas dos propiedades, y esa información se perdía al regenerar las vistas desconectadas. Ahora se generan.
  • Mejora: Nuevo icono App e imagen de fondo por defecto para FoxBin2Prg, configurable con el nuevo seteo de CFG "BackgroundImage".
    • Al fin, FoxBin tiene un icono e imagen de fondo que lo define :)
  • Bug Fix mnx: Los nombres de Pad de menú vacíos no se mantienen cuando se regenera un menú definido con nombres de Pad vacíos (Lutz Scheffler).
    • Cuando se crean menus, las opciones del primer nivel permiten que se defina el nombre del Pad, o que se deje en blanco. Si se dejaba en blanco FoxBin reemplazaba el nombre por uno secuencial único. Ahora se vuelve a dejar en blanco.
  • Mejora (API): Nueva propiedad "l_ProcessFiles" que permite configurarse a .F. cuando se usa foxbin2prg como objeto para obtener información de procesamiento de archivos con el método get_Processed sin realizar procesamiento real.
    • Esto es lo ya comentado antes para el método get_Processed()
  • Bug Fix frx/lbx: Limpiados algunos CR,LF,TAB sobrantes de algunos Tag de los FR2/LB2 que fueron introducidos en versiones anteriores (Ryan Harris).
    • Las versiones anteriores de FoxBin dejaban un fin de linea demás en algunas cláusulas Tag de los reportes (FRX) y etiquetas (LBX). Ahora se quita ese fin de línea demás.
  • Bug Fix scx/vcx/dbc: Eliminar archivos ERR cuando se procesa con el switch UseClassPerFile (Ryan Harris).
    • Cuando se usa ClassPerFile, no se estaban borrando los archivos .ERR al iniciar el proceso, como se hace con los archivos cuando no se usa este seteo. Ya está solucionado.
  • Mejora: Implementada la herencia de archivos CFG entre directorios.
    • Esta es una característica que ahorra muchísimos archivos de configuración cuando se usa FoxBin en múltiples proyectos, ya que además del archivo de configuración principal, que es el del directorio de instalación de FoxBin (si existe), se pueden usar archivos de configuración en uno o más directorios cuyas configuraciones se irán heredando en los subdirectorios, permitiendo tener unos pocos archivos CFG donde se cambien solo los seteos específicos que se necesiten y que controlen todos los subdirectorios bajo ellos. Por ejemplo, ahora es posible que un directorio tenga activada la compatibilidad de extensiones con SourceSafe y tener otro CFG en otro directorio con la configuración opuesta.
  • Mejora (API): Nuevo metodo get_DirSettings() que retorna un objeto CFG con los seteos para el directorio indicado (Lutz Scheffler).
    • Este nuevo método API permite devolver un objeto con los seteos que se aplican en el directorio indicado. Esto puede ser útil cuando se usa FoxBin para crear una interfaz con una herramienta SCM/DVCS como git, donde puede ser necesario conocer esta información.
  • Mejora: Permitir la generación de texto para una clase sola de una librería cuando se usa ClassPerFile (Lutz Scheffler).
    • FoxBin genera archivos completos, de una pieza o en partes (con ClassPerFile:1 ó 2). Ahora se puede solicitar la generación de una sola clase a texto, lo que permite ahorrar bastante tiempo cuando se quiere controlar el proceso a bajo nivel.
  • Mejora (API): Renombrados los nombres de los métodos a Inglés para facilitar su entendimiento internacional (Mike Potjer).
    • Este es un cambio importante que requiere que quienes usan FoxBin como objeto cambien algunos nombres de métodos que estaban en español a la nueva nomenclatura inglesa. El motivo del cambio sugerido es que FoxBin ha sustituido al SccText(X) y a TwoFox y se usa en varios países de habla inglesa y no inglesa (algunos países de América Latina, EEUU, Alemania, Francia, España y algunos otros) y los desarrolladores no Españoles tienen algunos problemas para adaptarse a los nombres en Español, lo que les dificulta usar la API, por lo que opté por usar nomenclatura estándar en Inglés, que es lo que usa la mayoría de programas. Igualmente los métodos publicados para usarse eran unos pocos (menos que los de la tabla de abajo), así que no requerirá mayor esfuerzo hacer las adaptaciones para usar los nuevos nombres. Además, al haber ahora una lista de métodos oficiales, ya no habrá cambios en este sentido. Se recomienda no usar los métodos no documentados, ya que esos sí pueden cambiar o incluso desaparecer.

Esta tabla resume los métodos más usados:

Antiguo nombre Nuevo nombre
Ejecutar Execute
TieneSoporte_* hasSupport_*
EvaluarConfiguracion EvaluateConfiguration
AvanceDelProceso updateProgressbar
cargar_frm_avance loadProgressbarForm
descargar_frm_avance unloadProgressbarForm


Ejemplo de uso de FoxBin desde la ventana de DOS para generar salida a stdOut:

C:\>foxbin2prg.exe "tests\datos_test\fb2p_dbc.dbc" | find /V ""
I,P1,E0,S1,X0,c:\tests\datos_test\fb2p_dbc.dbc
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.connection.remote_connection_dbf.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.connection.remote_connection_oracle.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.table.fb2p_depto.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.table.nombrelargodeldbf.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.view.rv_db_debug_setup.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.view.vista_local.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.view.vw_local_encuestas.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.view.vw_ora_convenios.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.view.vw_ora_dual.dc2
O,P1,E0,S1,X1,c:\tests\datos_test\fb2p_dbc.database.storedproceduressource.dc2

La última línea indica:
Output file (O), Processed (P1), Without Errors (E0), Supported (S1), Not Extended file (X0) and the Full path filename


Como actualizar el FoxBin2Prg existente:Con descargar el zip y reemplazar los archivos en el sitio que los hayan puesto antes es suficiente.


Link  de descarga de VFPx:
https://vfpx.codeplex.com/releases/view/116407

Link de descarga de GitHub:
https://github.com/fdbozzo/foxbin2prg


 Saludos!