martes, julio 31, 2018

Nueva versión v1.19.51.4 de FoxBin2Prg (Bug Fix) y Extensiones para PlasticSCM v2.5.51.4

Parte 1: FoxBin2Prg / Parte 2: Extensiones para Plastic


Parte 1: FoxBin2Prg


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).


La lista de cambios es la siguiente:

v1.19.51.4

* Bug Fix: El ordenamiento alfabético de los objetos de los ADD OBJECT puede causar que algunos objetos se creen en el orden erróneo, provocando comportamientos inesperados (Jochen Kauz)
 

   En el issue#28 en GitHub se ha reportado que en un proyecto (outlooknavbar) el ordenamiento alfabético de los objetos provoca que al ensamblar el binario y ejecutar su código algunos objetos no se comporten como se espera, en este caso, dejando siempre colapsado un panel expandible que debería estar expandido. Mantener el orden original de los objetos resuelve este tipo de problemas, por lo que lo considero un arreglo de integridad funcional necesario.

   NOTA: Es importante resaltar que este arreglo seguramente va a provocar varias diferencias en los SC2/VC2, proporcionalmente a la cantidad de objetos que tenga, pero esta diferencia se generará solamente la primera vez.

 
v1.19.51.3

* Bug Fix: "Error 1098, Cannot find ... [ENDT] ... that closes [TEXT]" hay un campo llamado TEXT como primera palabra de la línea (Kirides)
 

   En el issue#26 en GitHub se ha reportado que cuando hay campo llamado TEXT y este se usa como primera palabra en una línea, se identifica incorrectamente como si fuera una estructura TEXT..ENDTEXT

Ejemplo:
SELECT ;
   TEXT ;
   FROM tabla


v1.19.51.2

* Bug Fix: Cuando se exporta un DBF que pertenece a un DBC sin eventos, falla con el mensaje "Error 11, Function argument value, type, or count is invalid" (Jairo Argüelles/Juan C.Perdomo) 
 

   Este caso ocurre porque al consultar el valor de DBCEvents, a veces devuelve vacío en vez de .F., y al comparar vacío con un booleano fallaba.

v1.19.51.1

* Bug Fix: Si se usa capitalización en la información de las vistas, entonces la información relacionada no se exporta correctamente o completamente y puede perderse (SkySurfer1) 
 

   Para todos los componentes de la DBC (vistas, tablas, conexiones, etc) se está pasando el nombre a minúsculas, pero en el caso de las vistas da problemas impidiendo que algunas sean encontradas y exportadas a texto, por lo que se respeta su capitalización original.


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 GitHub




Parte 2: Extensiones para Plastic



Las extensiones para Plastic 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 desde dentro de la interfaz de Plastic.


La lista de cambios es la siguiente:

> Actualización de FoxBin2Prg: Versión sincronizada con la última versión de FoxBin2Prg



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


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



Link de descarga de las Extensiones FoxBin2Prg para Plastic de GitHub



Thank you for your support!



Artículos 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 configuración



 Saludos!



lunes, marzo 26, 2018

Nueva versión v1.19.51 de FoxBin2Prg (mejoras) y Extensiones para PlasticSCM v2.5.51

Parte 1: FoxBin2Prg / Parte 2: Extensiones para Plastic


Parte 1: FoxBin2Prg


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).


La lista de cambios es la siguiente:

v1.19.51

* Mejora: Agregado de soporte para archivos de macros (.fky)
 

   Este es uno de los dos tipos de archivos binarios que quedaba por exportar a texto. El soporte es de sólo exportación, y se genera con la extensión FK2.

* Mejora: Agregado de soporte para archivos de memoria (.mem)
 

   Este es uno de los dos tipos de archivos binarios que quedaba por exportar a texto. El soporte es de sólo exportación, y se genera con la extensión ME2.

* Mejora: Agregada detección automática de lenguaje
 

   Hasta ahora la selección del lenguaje de la interfaz y mensajes era manual, mediante el seteo Language del archivo CFG. Ahora, si no está configurada manualmente, es automática según el lenguaje que tenga configurado el usuario. Actualmente el soporte de languajes es para: ES (Español), DE (Alemán), FR (Francés), EN (Inglés).

* Mejora: De la ayuda rápida al hacer doble-click en FoxBin2Prg

   Aunque pocos lo saben, al hacer doble-click en FoxBin2Prg, o al ejecutarlo sin parámetros, se muestra un cuadro de ayuda rápida con las configuraciones soportadas. Se ha mejorado su presentación, se ha hecho escrolable y se ha habilitado su selección para poder copiar/pegar en un CFG.


* Mejora: Algunas mejoras menores en el LOG

   Se han agregado algunos datos extra para mostrar que config.fpw se está usando, principalmente para propósitos de diagnóstico.




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 GitHub




Parte 2: Extensiones para Plastic



Las extensiones para Plastic 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 desde dentro de la interfaz de Plastic.


La lista de cambios es la siguiente:

> Actualización de FoxBin2Prg: Versión sincronizada con la última versión de FoxBin2Prg



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


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



Link de descarga de las Extensiones FoxBin2Prg para Plastic de GitHub



Thank you for your support!



Artículos 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 configuración


 Saludos!

martes, marzo 20, 2018

VFP: Obtener información del usuario de Directorio Activo

Ese es un ejemplo listo para usar para obtener información del usuario indicado, aunque realmente el mismo ejemplo sirve para obtener otro tipo de información de Directorio Activo.

Solo sirve para máquinas bajo Directorio Activo, no para PC's personales que no están en red y no están conectados a AD.

*-----------------------------------------------
* DEVUELVE UN OBJETO CON INFORMACIÓN DEL USUARIO
* (NOMBRES, APELLIDOS, ETC). FDBOZZO. 10/01/2017
*-----------------------------------------------
* PARÁMETROS:               (v=Pasar por valor | @=Pasar por referencia)
*                           (!=Obligatorio | ?=Opcional) (IN/OUT)
* tcInfo_IN                 (?v IN    ) Valor a buscar (ej: 'NUUMA')
* tcTipoInfo_IN             (?v IN    ) Tipo de valor a buscar (ej: 'mailNickName')
* toInfoUsuario             (!@ IN    ) Variable donde devolver el objeto
*----------------------------------------------------------------------------------
*
* EJEMPLO DE USO:
*
* oInfoUsuario=NULL
* DO user_info.prg WITH '','',oInfoUsuario
* ? oInfoUsuario.fullname
*----------------------------------------------------------------------------------
#DEFINE C_CR CHR(13)
LPARAMETERS tcInfo_IN, tcTipoInfo_IN, toInfoUsuario

LOCAL lcAlias, lcDomainNC, loRootDSE, lcQuery ;
   , loEx as Exception ;
   , loRS as ADODB.Recordset ;
   , loConnection as ADODB.Connection ;
   , loCommand as ADODB.Command

TRY
   toInfoUsuario = .NULL.
   loRootDSE  = .NULL.
   loUser   = .NULL.
   lcAlias   = ''
   lcQuery   = ''
   loRS   = .NULL.
   tcTipoInfo_IN = EVL(tcTipoInfo_IN, 'mailNickName')

   IF EMPTY(tcInfo_IN)
      lcAlias = ALLTRIM(SUBSTR(UPPER(SYS(0)), AT('#', SYS(0)) + 2))
   ELSE
      lcAlias = ALLTRIM(tcInfo_IN)
   ENDIF

   *-- Windows 2000 Attribute (LDAP Name)
   *-------------------------------------
   * description
   * autoReply
   * businessRoles
   * co
   * company
   * delivContLength
   * department
   * displayName
   * displayNamePrintable
   * distinguishedName
   * dnQualifier
   * employeeID
   * extensionAttribute1
   * extensionAttribute2
   * extensionAttribute3
   * extensionAttribute4
   * extensionAttribute5
   * extensionAttribute6
   * extensionAttribute7
   * extensionAttribute8
   * extensionAttribute9
   * extensionAttribute10
   * extensionAttribute11
   * extensionAttribute12
   * extensionAttribute13
   * extensionAttribute14
   * extensionAttribute15
   * facsimileTelephoneNumber
   * generationQualifier
   * homephone
   * homePostalAddress
   * houseIdentifier
   * info
   * initials
   * l
   * Language
   * mail
   * mailnickname
   * mobile
   * otherTelephone
   * otherHomePhone
   * telephoneAssistant
   * pager
   * personalPager
   * personalTitle
   * physicalDeliveryOfficeName
   * postalCode
   * secretary
   * sn
   * st
   * street
   * streetAddress
   * telephoneNumber
   * telexNumber
   * teletexTerminalIdentifier
   * textEncodedORAddress
   * title
   * userCertificate
   * userCert
   * userSMIMECertificate
   * url
   * x121Address
   * autoReplyMessage
   * importedFrom

   loConnection = CREATEOBJECT("ADODB.Connection")
   loCommand  = CREATEOBJECT("ADODB.Command")
   loRootDSE  = GETOBJECT("LDAP://RootDSE")
   lcDomainNC  = loRootDSE.GET("defaultNamingContext")
   loConnection.Provider = "ADsDSOObject"
   loConnection.OPEN("ADs Provider")
   lcQuery   = "<LDAP://" + lcDomainNC + ">;(" ;
     + tcTipoInfo_IN + "=" + lcAlias + ");adspath;subtree"

   loCommand.ActiveConnection = loConnection
   loCommand.CommandText = lcQuery
   loRS = loCommand.Execute

   IF loRS.RecordCount = 0
      ERROR (TEXTMERGE('No se encontró información de "<<lcAlias>>"' ;
       + ' sobre el tipo de dato "<<tcTipoInfo_IN>>"'))
   ELSE
      toInfoUsuario = CREATEOBJECT("EMPTY")
      loAD_User = GETOBJECT(loRS.FIELDS("adspath").VALUE)
      ADDPROPERTY(toInfoUsuario, 'EmailAddress' ;
       , IIF(TYPE('loAD_User.EmailAddress') # 'U' ;
       , NVL(loAD_User.EmailAddress,''),''))
      ADDPROPERTY(toInfoUsuario, 'FirstName' ;
       , IIF(TYPE('loAD_User.FirstName') # 'U' ;
       , NVL(loAD_User.FirstName,''),''))
      ADDPROPERTY(toInfoUsuario, 'FullName' ;
       , IIF(TYPE('loAD_User.FullName') # 'U' ;
       , NVL(loAD_User.FULLNAME,''),''))
      ADDPROPERTY(toInfoUsuario, 'LastName' ;
       , IIF(TYPE('loAD_User.LastName') # 'U' ;
       , NVL(loAD_User.LastName,''),''))

   ENDIF

CATCH TO loEx
   IF _VFP.StartMode=0
      MESSAGEBOX( TEXTMERGE("ERROR <<loEx.ErrorNo>>" ;
        + ", <<loEx.Message>>" + C_CR ;
         + "Proc.<<loEx.Procedure>>" ;
         + ", Line <<loEx.LineNo>>") )
   ENDIF
   THROW

FINALLY
   IF VARTYPE(loConnection) = "O" AND loConnection.State = 1
      loConnection.CLOSE()
   ENDIF

   STORE NULL TO loRS, loConnection, loCommand
ENDTRY


sábado, marzo 17, 2018

VFP: WScriptShell_Run - Reemplazo compatible con WScript.Shell.Run() y agregado de timeout

Cuando se quiere ejecutar un programa desde Visual FoxPro, normalmente se usa la función ShellExecute si no se necesita esperar a que termine el proceso, o WScript.Shell.Run() si se necesita esperar a que termine el proceso, aunque también sirve para el caso opuesto.

El problema con el objeto de Sistema WScript.Shell, es que en ciertos entornos los administradores pueden desactivarlo, impidiendo su uso.

Sumado a lo anterior, más de una vez necesité poder controlar también el tiempo (timeout) que un proceso puede estar ejecutándose, para evitar que se quede ejecutando (o colgado) por siempre, y por eso en su momento estuve investigando cómo poder usar con funciones API Win32 algo equivalente y compatible al WScript.Shell.Run() y con la mejora de poder disponer de un timeout programable.

El resultado es la función de más abajo, que había publicado en algún otro sitio, pero que me faltaba tener disponible en el Blog, la cual tiene lo mejor de los dos mundos:

  • Está basada en funciones API Win32, por lo que no puede inhabilitarse administrativamente
  • Permite reutilizar el parámetro de tbWaitOnReturn, que normalmente admite valores 0 (no esperar fin del programa) y 1 (esperar fin del programa), como parámetro de timeout, donde cualquier valor > 1 será el timeout en milisegundos


Para ejemplos de uso, ver los comentarios al inicio del código.



FUNCTION WScriptShell_Run(tcCmdLine as String, tnWindowStyle as Integer, tbWaitOnReturn as Boolean, tlDebug as Logical)
 * 14/09/2015 Fernando D. Bozzo - http://fox.wikis.com/wc.dll?Wiki~WScriptShellRun~VFP
 * Modificación basada en la rutina RunExitCode.prg de William GC Steinford (nov 2002)
 * pero compatible con el método Run de WScript.Shell para su reemplazo cuando no es posible usarlo.
 * http://fox.wikis.com/wc.dll?Wiki~ProcessExitCode
 *-----------------------------------------------------------------------------------------------
 * 'Run' Parameter Documentation at: https://msdn.microsoft.com/en-us/library/d5fk67ky%28v=vs.84%29.aspx
 *
 * NOTA IMPORTANTE:
 * A diferencia del WScript.Shell.Run original, el valor tbWaitOnReturn se comporta como un timeout
 * en milisegundos si se pasa un valor mayor a 1, pasado el cual se mata a la tarea invocada.
 *-----------------------------------------------------------------------------------------------
 * Ej.1: Ejecutar el comando DIR en una consola y enviar la salida stdout a un archivo dir.txt
 * ? WScriptShell_Run("c:\windows\system32\cmd.exe /c dir c:\*.* > \temp\dir.txt")
 *
 *-----------------------------------------------------------------------------------------------
 * Ej.2: Ejecutar la calculadora de Windows en ventana normal y esperar 5 segundos a que el usuario la cierre, o matarla.
 * ? WScriptShell_Run("calc.exe", 5, 5000, .T.)
 *
 *-----------------------------------------------------------------------------------------------
 * Ej.3: Ejecutar la calculadora de Windows en ventana normal y no esperar a su cierre.
 * ? WScriptShell_Run("calc.exe", 5, 0, .T.)
 *
 *-----------------------------------------------------------------------------------------------
 * Ej.4: Ejecutar el Notepad de Windows en ventana maximizada y esperar 15 segundos a que el usuario la cierre, o matarla.
 * ? WScriptShell_Run("notepad.exe", 3, 15000, .T.)
 *
 *-----------------------------------------------------------------------------------------------
 * Ej.5: Ejecutar el Notepad de Windows en ventana maximizada y esperar indefinidamente a que el usuario la cierre, o matarla.
 * ? WScriptShell_Run("notepad.exe", 3, 1, .T.)
 *
 *-----------------------------------------------------------------------------------------------

 LOCAL lnWfSO, ln_dwFlags, ln_wShowWindow, lcStartInfo, lcProcessInfo, ln_hProcess, ln_hThread ;
  , lnExitCode, ln_dwProcessId, ln_dwThreadId, tcProgFile, laDirFile(1,5), lnTimeout

 TRY
  * NOTA: Las constantes para VFP se pueden consultar en http://www.news2news.com/vfp/w32constants.php

  #DEFINE SEE_MASK_NOCLOSEPROCESS  0x00000040
  #DEFINE WAIT_MILLISECOND 3000

  #DEFINE SW_SHOW   5
  #DEFINE STILL_ACTIVE 0x103
  #DEFINE cnINFINITE  0xFFFFFFFF
  #DEFINE cnHalfASecond 500 && milliseconds
  #DEFINE cnTimedOut  0x0102

  *-- Constantes para WaitForSingleObject
  #DEFINE WAIT_ABANDONED 0x00000080
  #DEFINE WAIT_OBJECT_0 0x00000000
  #DEFINE WAIT_TIMEOUT 0x00000102
  #DEFINE WAIT_FAILED  0xFFFFFFFF

  tcProgFile  = EVL(tcProgFile, NULL)
  tcCmdLine  = EVL(tcCmdLine, NULL)
  lnTimeout  = cnINFINITE
  lnExitCode  = 0

  DO CASE
  CASE VARTYPE(tbWaitOnReturn) = "L"
  CASE VARTYPE(tbWaitOnReturn) = "N"
   * Si se indica un valor mayor a 1, se interpreta como "esperar por N milisegundos"
   IF tbWaitOnReturn > 1
    lnTimeout = tbWaitOnReturn
   ENDIF
   tbWaitOnReturn = (tbWaitOnReturn >= 1)
  OTHERWISE
   ERROR 'Invalid value for tbWaitOnReturn parameter'
  ENDCASE

  IF VARTYPE(tnWindowStyle) # "N" OR NOT BETWEEN(tnWindowStyle, 0, 10) THEN
   tnWindowStyle = 10
  ENDIF

  ln_dwFlags  = 1
  ln_wShowWindow = tnWindowStyle

  * DOCUMENTACIÓN estructura _STARTUPINFO:
  * creates the STARTUP structure to specify main window
  * properties if a new window is created for a new process

  *| typedef struct _STARTUPINFO {
  *|     DWORD   cb;                4
  *|     LPTSTR  lpReserved;        4
  *|     LPTSTR  lpDesktop;         4
  *|     LPTSTR  lpTitle;           4
  *|     DWORD   dwX;               4
  *|     DWORD   dwY;               4
  *|     DWORD   dwXSize;           4
  *|     DWORD   dwYSize;           4
  *|     DWORD   dwXCountChars;     4
  *|     DWORD   dwYCountChars;     4
  *|     DWORD   dwFillAttribute;   4
  *|     DWORD   dwFlags;           4
  *|     WORD    wShowWindow;       2
  *|     WORD    cbReserved2;       2
  *|     LPBYTE  lpReserved2;       4
  *|     HANDLE  hStdInput;         4
  *|     HANDLE  hStdOutput;        4
  *|     HANDLE  hStdError;         4
  *| } STARTUPINFO, *LPSTARTUPINFO; total: 68 bytes
  lcStartInfo = BINTOC(68,'4RS') ;
   + BINTOC(0,'4RS') + BINTOC(0,'4RS') + BINTOC(0,'4RS') ;
   + BINTOC(0,'4RS') + BINTOC(0,'4RS') + BINTOC(0,'4RS') + BINTOC(0,'4RS') ;
   + BINTOC(0,'4RS') + BINTOC(0,'4RS') + BINTOC(0,'4RS') ;
   + BINTOC(ln_dwFlags,'4RS') ;
   + BINTOC(ln_wShowWindow,'2RS') ;
   + BINTOC(0,'2RS') + BINTOC(0,'4RS') ;
   + BINTOC(0,'4RS') + BINTOC(0,'4RS') + BINTOC(0,'4RS')

  lcProcessInfo = REPLICATE( CHR(0), 16 )

  * DOCUMENTACIÓN estructura _PROCESS_INFORMATION:
  * https://msdn.microsoft.com/en-us/library/windows/desktop/ms684873%28v=vs.85%29.aspx
  *    typedef struct _PROCESS_INFORMATION {
  *        HANDLE hProcess;
  *        HANDLE hThread;
  *        DWORD dwProcessId;
  *        DWORD dwThreadId;
  *    } PROCESS_INFORMATION;
  *

  IF CreateProcess( tcProgFile, tcCmdLine,0,0,0,0,0,0, lcStartInfo, @lcProcessInfo ) = 0

   *-- Segundo intento: Si se definió un archivo (ej: un TXT,LOG,etc) intento lanzarlo
   *-- con la aplicación predeterminada
   IF ADIR(laDirFile, tcCmdLine) = 1 THEN
    LOCAL lcInfo, lnHeap, lnLen, lnPtr

    *-- Ejemplo adaptado de: http://www.foxite.com/archives/0000316611.htm
    lnLen = LEN(tcCmdLine) + 1
    lnHeap = GetProcessHeap()
    lnPtr = HeapAlloc(lnHeap, 0x8, 5 + lnLen)
    SYS(2600, lnPtr, 5, [open] + CHR(0))
    SYS(2600, lnPtr+5, lnLen, tcCmdLine + CHR(0))

    * DOCUMENTACIÓN estructura _SHELLEXECUTEINFO:
    * https://msdn.microsoft.com/en-us/library/windows/desktop/bb759784%28v=vs.85%29.aspx
    *typedef struct _SHELLEXECUTEINFO {
    *    DWORD     cbSize;            4
    *    ULONG     fMask;             4
    *    HWND      hwnd;              4
    *    LPCTSTR   lpVerb;            4
    *    LPCTSTR   lpFile;            4
    *    LPCTSTR   lpParameters;      4
    *    LPCTSTR   lpDirectory;       4
    *    int       nShow;             4
    *    HINSTANCE hInstApp;          4
    *    LPVOID    lpIDList;          4
    *    LPCTSTR   lpClass;           4
    *    HKEY      hkeyClass;         4
    *    DWORD     dwHotKey;          4
    *    union {
    *        HANDLE hIcon;            
    *        HANDLE hMonitor;         
    *    } DUMMYUNIONNAME;            4
    *    HANDLE    hProcess;          4
    *} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO;
    *

    lcInfo = ;
     BINTOC(60, [4RS]) + ;
     BINTOC(SEE_MASK_NOCLOSEPROCESS, [4RS]) + ;
     BINTOC(0, [4RS]) + ;
     BINTOC(lnPtr, [4RS]) + ;
     BINTOC(lnPtr+5, [4RS]) + ;
     BINTOC(0, [4RS]) + ;
     BINTOC(0, [4RS]) + ;
     BINTOC(1, [4RS]) + ;
     REPLICATE(CHR(0), 28)

    IF ShellExecuteEx(@lcInfo) = 0
     HeapFree(lnHeap, 0, lnPtr) && Comprobar si es correcto limpiar el puntero aqui
     IF tlDebug
      ? "Could not call process"
     ENDIF
     lnExitCode = -1
     EXIT
    ELSE
     HeapFree(lnHeap, 0, lnPtr)
     ln_hProcess = CTOBIN(RIGHT(lcInfo, 4), [4RS])
     ln_hThread = 0

     IF tlDebug
      ? "Process handle    = "+TRANSFORM(ln_hProcess)
      ? "Thread handle     = "+TRANSFORM(ln_hThread)
     ENDIF

     *IF lnProcess != 0
     * WaitForSingleObject(ln_hProcess, WAIT_MILLISECOND)
     * IF tlDebug
     *  ? "Terminating process!"
     * ENDIF
     * TerminateProcess(ln_hProcess, 0)
     *ENDIF
    ENDIF
   
   ELSE
    IF tlDebug
     ? "Could not create process"
    ENDIF
    lnExitCode = -1
    EXIT
   ENDIF
  ELSE

   * Process and thread handles returned in ProcInfo structure
   ln_hProcess  = CTOBIN( LEFT( lcProcessInfo, 4 ), '4RS' )
   ln_hThread  = CTOBIN( SUBSTR( lcProcessInfo, 5, 4 ), '4RS' )
   ln_dwProcessId = CTOBIN( SUBSTR( lcProcessInfo, 9, 4 ), '4RS' )
   ln_dwThreadId = CTOBIN( SUBSTR( lcProcessInfo, 13, 4 ), '4RS' )

   IF tlDebug
    ? "Process handle    = "+TRANSFORM(ln_hProcess)
    ? "Thread handle     = "+TRANSFORM(ln_hThread)
    ? "Process handle id = "+TRANSFORM(ln_dwProcessId)
    ? "Thread handle id  = "+TRANSFORM(ln_dwThreadId)
   ENDIF
  ENDIF

  IF tbWaitOnReturn THEN
   * // Give the process time to execute and finish
   lnExitCode = STILL_ACTIVE

   DO WHILE lnExitCode = STILL_ACTIVE
    lnWfSO = WaitForSingleObject(ln_hProcess, lnTimeout)

    IF tlDebug
     ? 'lnWfSO = ' + TRANSFORM(lnWfSO)
    ENDIF

    IF GetExitCodeProcess(ln_hProcess, @lnExitCode) <> 0
     IF lnExitCode = STILL_ACTIVE
      DO CASE
      CASE lnWfSO = WAIT_TIMEOUT
       IF tlDebug
        ? "Exit code = "+ TRANSFORM(lnWfSO) + " (WAIT_TIMEOUT)"
       ENDIF
       TerminateProcess(ln_hProcess, 0)
       lnExitCode = WAIT_TIMEOUT

      CASE lnWfSO = WAIT_FAILED
       IF tlDebug
        ? "Exit code = "+ TRANSFORM(lnWfSO) + " (WAIT_FAILED)"
       ENDIF

      CASE lnWfSO = WAIT_OBJECT_0
       IF tlDebug
        ? "Exit code = "+ TRANSFORM(lnWfSO) + " (WAIT_OBJECT_0)"
       ENDIF

      CASE lnWfSO = WAIT_ABANDONED
       IF tlDebug
        ? "Exit code = "+ TRANSFORM(lnWfSO) + " (WAIT_ABANDONED)"
       ENDIF

      OTHERWISE
       IF tlDebug
        ? "Exit code = "+ TRANSFORM( lnExitCode )
       ENDIF
      ENDCASE
     ELSE
      IF tlDebug
       ? "Exit code = "+ TRANSFORM( lnExitCode )
      ENDIF
     ENDIF
    ELSE
     IF tlDebug
      ? "GetExitCodeProcess() failed"
     ENDIF
     lnExitCode = -2
    ENDIF

    *DOEVENTS
   ENDDO
  ELSE
   lnExitCode = 0
  ENDIF

  *-- DOCUMENTACIÓN sobre cierre procesos/threads:
  *-- https://msdn.microsoft.com/en-us/library/windows/desktop/ms682512%28v=vs.85%29.aspx
  =CloseHandle(ln_hProcess)
  =CloseHandle(ln_hThread)

  IF tlDebug
   ? '> FUNCTION RETURN VALUE = '
   ?? lnExitCode
  ENDIF
 ENDTRY

 RETURN lnExitCode
ENDFUNC


Hasta la próxima! :)

martes, marzo 06, 2018

Nueva versión v1.19.50 de FoxBin2Prg (arreglos y mejoras) y Extensiones para PlasticSCM v2.5.50


Parte 1: FoxBin2Prg / Parte 2: Extensiones para Plastic

Parte 1: FoxBin2Prg


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).


La lista de cambios es la siguiente:

v1.19.50

* Bug Fix pjx: Algunos campos relacionados con el homedir de los PJX no siempre se sincronizaban bien
 

   En ocasiones podía pasar que al abrir un PJX recién regenerado en el directorio original, preguntara si se quería cambiar al nuevo directorio por defecto. En este caso particular eso ya no pasa.

* Mejora: Agregada una nueva opción de configuración (CFG) BodyDevInfo, desactivada por defecto
 

   En la versión v1.19.49.1 se notificó la ausencia en la exportación de los PJX del campo DevInfo del cuerpo como un Bug, pero realmente ese campo solo tiene info útil en la cabecera, ya que en el cuerpo es un campo compilado con información de archivos include, por lo que se regenera automáticamente al compilar el PJX. Desde esta versión ese campo se vuelve a excluir no solo porque se regenera al compilar, sino porque genera demasiadas diferencias cada vez que se compila, y no es necesario. Se puede activar su guardado usando la nueva opción de CFG BodyDevInfo:1.

* Mejora: Nueva opción de configuración PRG_Compat_Level
 

   Cuando se usa el nuevo valor PRG_Compat_Level: 1 (por defecto es 0) los comentarios de métodos de clases se guardan con el correspondiente HELPSTRING documentado en la ayuda, en vez del actual "&&". No es un cambio importante, solo acerca el código generado un poco más a las especificaciones de VFP. En el futuro este valor podría permitir nuevos valores si se incorporasen más opciones PRG-compatibles.

* Mejora: Agregado workaround para poder exportar/importar datos de tablas cuya protección se basa en los eventos de la base de datos DBC

   Las bases de datos DBC de VFP permiten usar Eventos para detectar apertura y cierre de tablas, lo que a veces puede bloquear o entorpecer la exportación o importación de datos con FoxBin2Prg. Se ha agregado un workaround para saltar este control y poder importar/exportar igualmente. Un ejemplo de esto se tiene en la tabla DVDS.DBF que viene en uno de los ejemplos de VFP del directorio de instalación, donde al intentar abrirla desde la ventana de comandos pide una clave.

v1.19.49.8

* Bug Fix vcx (issue#17): Cuando se convierte a texto un VCX corrupto con objetos duplicados, sale el error "The specified key already exists" (Kirides/Doug Hennig)

   Un bug en la detección de registros para ignorar provocaba en ciertas situaciones errores por nombres duplicados. Aquí se resuelven esos casos.

v1.19.49.7

* Bug Fix dbf: Cuando se regenera el texto de un DBF con campos llamados "X" o "I", se produce un error (Francisco Prieto)

   Este error solo ocurría en las condiciones descriptas porque los campos de tabla tienen prioridad sobre las variables de mismo nombre. Se solucionó anteponiendo "m." a esas variables.

v1.19.49.6

* Bug Fix vcx/vc2 (Issue#15): FoxBin2Prg debería ignorar los registros que ignora el diseñador (Doug Hennig)

   Una característica no-documentada de los VCX es que permite mantener registros desactivados que nunca se mostrarán en el diseñador de clases (el diseñador los ignora). El problema es que estos registros ignorados (muy poco frecuentes) FoxBin2Prg los ve y los incluye dentro del código generado. Desde esta versión se ignoran también.

* Bug Fix vcx/vc2 (Issue#16): FoxBin2Prg codifica la propiedad MemberData con CRLF, lo que puede causar el error "MemberData value is too long" (Doug Hennig)

   La propiedad MemberData tiene una longitud máxima de 8192 bytes, por lo que ahorrar cada byte es importante. Por defecto VFP guarda las propiedades de la misma forma, pero una optimización en el Property Editor (VFPx) permitió compactar esa propiedad aún más, dejando más espacio para nombres de propiedades. FoxBin2Prg ahora la guarda de la misma forma compactada que el Property Editor de VFPx.

v1.19.49.5

* Bug Fix dbf (Issue#13): Cuando se exportan datos de DB2 a DBF, los TABS al inicio de campos memo se pierden (Doug Hennig)

   Los campos memo pueden guardar cualquier tipo de información, incluso caracteres especiales. En este caso, dado que FoxBin2Prg genera una salida a texto con ajuste por tabulaciones para mantener una apariencia fácil de leer y comparar, hacía que el hacer el paso contrario (texto a DBF) se quitaran esas tabulaciones al leer los datos, pudiendo afectar a los campos memo que guarden TABS como parte de su información. Aquí queda corregido ese caso.

v1.19.49.4

* Bug Fix vcx (Issue#11): Cuando se usa ClassPerFile en modo API y se importa una clase, a veces sus nombres (propiedad name="nombre") quedan sin entrecomillar, provocando errores (Lutz Scheffler)

   Como se comenta en el asunto, esto solo ocurría en modo API (uso de FoxBin2Prg en modo clase instanciable).

v1.19.49.3

* Bug Fix dbf: Los campos tipo Double son definidos explícitamente con 0 decimales cuando no se definen decimales (Jerry Stager)

   Cuando no se indica la precisión del campo Double, por defecto VFP asume 2 decimales, pero FoxBin2Prg estaba asumiendo erróneamente 0 decimales. Aunque el dato se guarda bien igual, el problema que se produce es visual, ya que no se mostraban los decimales en este caso.

v1.19.49.2

* Bug Fix vcx: ClassPerFile no funciona correctamente desde la versión v1.19.49.1 (Lutz Scheffler)

   Un condicionado incorrecto producía que no se exporten las clases individuales.

v1.19.49.1

* Bug Fix pjx: El campo DevInfo solo debe usarse para los PJX

   El soporte para este campo en el cuerpo de los PJX estaba implementado en erróneamente para otros tipos de archivo, como vcx/scx. Ahora se circunscribe solo a los PJX.

v1.19.49

* Mejora: Se reemplaza el uso del método MoveFile del objeto Scripting.FileSystemObject por una función Win32API equivalente

   Las funciones Win32API suelen tener un mejor desempeño y fiabilidad ante errores que los objetos del sistema, por eso el cambio da mejor estabilidad.

* Mejora: Agregado soporte para archivos CFG temporales cuando se usa la API de bajo nivel

   Hay casos en los que usando la API de bajo nivel se necesita una configuración temporal que prevalezca sobre la encontrada en el disco. Con este cambio esto ahora es posible.

* Bug Fix: La exportación de una sola clase con ClassPerFile con la sintaxis "classlib.vcx::classname" como parámetro no funciona (Lutz Scheffler)

   Se corrige este caso para permitir exportar una sola clase con esta sintaxis.

* Mejora: Agregado el soporte de importación de una sola clase con la sintaxis "library.vcx::classname::import"como parámetro

   Ahora usando FoxBin2Prg desde la terminal de Windows o en modo API usando un parámetro con sintaxis "library.vcx::classname::import" permite importar la clase indicada solamente, sin importar el resto de clases.

* Mejora: Agregado el soporte de exportación de una sola clase con la sintaxis "library.vcx::classname::export"como parámetro

   Ahora usando FoxBin2Prg desde la terminal de Windows o en modo API usando un parámetro con sintaxis "library.vcx::classname::export" permite exportar la clase indicada solamente, sin exportar el resto de clases.

* Mejora: Ambas sintaxis para importar/exportar una sola clase no requieren de un archivo CFG 

   El soporte de ClassPerFile requiere que esté configurado en un CFG, pero cuando se usa la sintaxis "library.vcx::classname::import" o "library.vcx::classname::export" como sintaxis de parámetro esto no es necesario.

* Bug Fix frx/lbx: Se genera información parcialmente corrompida para los archivos FRX/LBX cuando se usan caracteres "&&"dentro de expresiones de campo (Alejandro A. Sosa)

   Usar este tipo de caracteres, no como comentario, sino como parte de una expresión, es un caso de uso válido que no estaba contemplado, ya que se estaba tomando como comentario, provocando que la expresión se corte por ese punto.

* Mejora: Uso de directorio temporal seguro con sys(2023)

   Cuando sys(2023) apunta a "Program Files", entonces FoxBin2Prg usa getenv("temp") para guardar los temporales y evitar errores por directorio de solo lectura.

* Mejora: Agregadas algunas configuraciones en el config.fpw para intentar evitar interferencias con la configuración actual de VFP



* Mejora: Agregado soporte para guardar el campo "User" (Doug Hennig)

   El campo User no es usado por VFP y por lo tanto no es imprescindible, pero al ser un campo disponible para el usuario, para quienes lo usan puede ser importante, y por eso se ha agregado su soporte.



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 GitHub



Parte 2: Extensiones para Plastic



Las extensiones para Plastic 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 desde dentro de la interfaz de Plastic.


La lista de cambios es la siguiente:

> Actualización de FoxBin2Prg: Versión sincronizada con la última versión de FoxBin2Prg



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


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



Link de descarga de las Extensiones FoxBin2Prg para Plastic de GitHub



Thank you for your support!



Artículos 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 configuración


 Saludos!

domingo, febrero 25, 2018

VFP: Cómo mover una tabla de una Base de Datos a otra

En ocasiones puede pasar que tengamos una tabla que inicialmente pertenece a una base de datos, pero que luego por algún motivo necesitemos quitarla de esa base de datos y moverla a otra distinta con todos sus datos.

A priori esto puede parecer algo sencillo, pero dependiendo de algunas circunstancias puede ser bastante más complicado.

Veamos algunos ejemplos, para los que VFP debe estar ubicado en el directorio de los datos.
Recordar siempre hacer un backup antes!




Caso 1: Tabla con nombres de campo cortos (hasta 10 caracteres) y sin triggers


Supongamos por ejemplo que tenemos una "tabla1" con un campo "NOMBRE" y un índice por el mismo, perteneciente a la base de datos BDD1

En este caso la operación es sencilla, basta con quitar la tabla de una base de datos y agregarla a la otra base de datos:


1) Para definir una situación inicial de ejemplo, creamos una BDD1 con una Tabla1 con datos e índice (en un caso real ir directo al paso 2):

CREATE DATABASE bdd1
CREATE TABLE tabla1 (nombre c(30))
INSERT INTO tabla1 (nombre) VALUES ("Francisco")
INSERT INTO tabla1 (nombre) VALUES ("Antonio")
INSERT INTO tabla1 (nombre) VALUES ("Juan")
INDEX on nombre TAG nombre
CLOSE ALL


2) Ahora movemos la tabla a la otra BDD:

OPEN DATABASE bdd1 EXCLUSIVE && Abrimos la BDD origen 
REMOVE TABLE tabla1          && Quitamos la tabla de la misma 
OPEN DATABASE bdd2 EXCLUSIVE && Abrimos la BDD destino
ADD TABLE tabla1             && Agregamos la tabla
CLOSE ALL


Listo! Esta opción es la más simple y rápida, y no implica trasvasar datos.




Caso 2: Tabla con nombres de campo largos (más de 10 caracteres) y sin triggers


Ahora supongamos por ejemplo que tenemos una "tabla1" con un campo "NOMBRE_LARGO" y un índice por el mismo, perteneciente a la base de datos BDD1

En este caso la operación sigue siendo sencilla, aunque no podremos seguir los pasos del Caso 1 porque perderemos los nombres largos y los índices que dependan de ellos dejarán de funcionar.


1) Para definir una situación inicial de ejemplo, creamos una BDD1 con una Tabla1 con datos e índice (en un caso real ir directo al paso 2):

CREATE DATABASE bdd1CREATE TABLE tabla1 (nombre_largo c(30))
INSERT INTO tabla1 (nombre_largo) VALUES ("Francisco")
INSERT INTO tabla1 (nombre_largo) VALUES ("Antonio")
INSERT INTO tabla1 (nombre_largo) VALUES ("Juan")
INDEX on nombre_largo TAG nombre
CLOSE ALL


2) Ahora movemos la tabla a la otra BDD en un directorio de trabajo y traemos todo lo hecho:

MKDIR copia                     && Creamos un dir de trabajo
COPY FILE BDD2.* TO copia\*.*   && Copiamos la BDD destino
USE tabla1 EXCLUSIVE

COPY STRUCTURE TO copia\tabla1 ;&& Copiamos la estructura de la
   WITH CDX DATABASE copia\BDD2 && tabla y CDX a la BDD destino
CLOSE ALL
OPEN DATABASE copia\bdd2 EXCLU  && Abrimos la BDD destino
USE copia\tabla1 EXCLUSIVE

APPEND FROM tabla1              && Recuperamos los datos originales
CLOSE ALL
OPEN DATABASE bdd1 EXCLUSIVE    && Abrimos la BDD origen
REMOVE TABLE tabla1             && Quitamos la tabla de la misma
CLOSE ALL 
COPY FILE copia\*.* TO *.*      && Traemos los archivos modificados


Terminado! Esta opción es la más simple y rápida, aunque implica trasvasar datos.




Caso 3: Tabla con propiedades de BDD


Otro caso que se puede presentar es tener una tabla asociada a una BDD que use propiedades específicas de esta, como reglas de validación de campos, mensajes de validación, comentarios de tabla o campos, triggers, etc.

Este caso es uno de los más complejos, porque si seguimos los pasos del Caso 1, perderemos los nombres largos y los índices que dependan de ellos dejarán de funcionar., y si seguimos los pasos del  Caso 2 tendremos los datos y los índices, pero no las propiedades de BDD comentadas.

Aquí se podría usar FoxBin2Prg, pero en este caso es necesario conocer las estructuras DB2 (tabla) y DC2 (base de datos) generadas, ya que habrá que cortar y pegar algunas secciones del DBC1 para pasarlos al DBC2, además de modificar el DB2.

Vamos a ver un ejemplo con la misma tabla que el Caso 2 y algunas propiedades de BDD.


1) Para definir una situación inicial de ejemplo, creamos una BDD1 con una Tabla1 con datos, índice y algunas propiedades de BDD (en un caso real ir directo al paso 2):

CREATE DATABASE bdd1
CREATE TABLE tabla1 ;
    ( nombre_largo C(30) ;
    CHECK NOT EMPTY(nombre_largo) ;
    ERROR "El nombre no puede estar vacío" ;
    , codigo I AUTOINC NEXTVALUE 1 STEP 1 ;
    PRIMARY KEY )

INSERT INTO tabla1 (nombre_largo) VALUES ("Francisco")
INSERT INTO tabla1 (nombre_largo) VALUES ("Antonio")
INSERT INTO tabla1 (nombre_largo) VALUES ("Juan")
INDEX on nombre_largo TAG nombre
DBSETPROP("tabla1.nombre_largo","FIELD","Comment";
    ,"Comentario del campo nombre_largo")
DBSETPROP("tabla1","TABLE","Comment","Comentario de la tabla")
CLOSE ALL



2) Creamos un archivo de configuración específico para nuestra Tabla1 (tabla1.dbf.cfg) con esta configuración:

DBF_Conversion_Support: 8 && Permite exportar e importar estructura, indices y datos


3) Creamos la vista de texto (DB2) de esta Tabla, la vista de texto (DC2) de su base de datos BDD1 y la vista de texto (DC2) de la base de datos destino BDD2 con FoxBin2Prg


4) Si abrimos la vista de texto de la tabla del ejemplo (tabla1.db2), veremos que tiene dentro toda la información de la estructura al principio y luego los registros de datos. En esta cabecera debemos cambiar el nombre de la base de datos actual (BDD1) por la que queremos que sea la nueva (BDD2) y lo volvemos a guardar:

<TABLE>
    <MemoFile></MemoFile>
    <CodePage>1252</CodePage>
    <LastUpdate></LastUpdate>
    <Database>bdd2.dbc</Database>



5) Si abrimos la vista de texto de la base de datos del ejemplo (bdd1.dc2), veremos que figuran todas las tablas con una estructura como esta:

<DATABASE>
    <Name>BDD1</Name>
    <Comment></Comment>
    <Version>10</Version>
    <DBCEvents>.F.</DBCEvents>
    <DBCEventFilename></DBCEventFilename>

    <TABLES>

        <TABLE>
            <Name>TABLA1</Name>
            <Comment>Comentario de la tabla</Comment>
            <Path>tabla1.dbf</Path>

            ... 
        </TABLE>


Los tags <TABLE></TABLE> contienen toda la información de cada tabla, sus campos, índices y comentarios, parte de los cuales están repetidos en la información de la tabla.


6) La sección completa <TABLE></TABLE> de la tabla que nos interesa pasar, la cortamos de esta estructura y la pegamos en la estructura de destino (bdd2.dc2), cuidando de insertarla en orden alfabético por el nombre de la tabla (el tag <NAME>), y guardando el cambio en ambos archivos (bdd1.dc2 y bdd2.dc2)

Nota: Si la base de datos bdd2 es nueva, no tendrá tablas dentro por lo que la estructura externa <TABLES></TABLES> que contiene la tablas tampoco existirá, por lo que podría haber que crearla manualmente, ya que estamos pasando solo una tabla.


7) Abrimos el archivo de la base de datos origen (bdd1.dc2) con FoxBin2Prg para que regenere el DBC sin la Tabla1


8) Abrimos el archivo de la tabla (tabla1.db2) con FoxBin2Prg para que la regenere con su nueva base de datos referenciada


9) Abrimos el archivo de la base de datos destino (bdd2.dc2) con FoxBin2Prg para que regenere el DBC con la nueva información de la Tabla1


¡Al fin! Ya tenemos pasada la tabla1 de la bdd1 a la bdd2 con toda su información relacionada.




Resumen:


Hay otras formas de pasar una tabla a otra base de datos, pero normalmente requieren usar programación específica para dichos casos y tener un conocimiento más técnico y profundo de cada componente.

Lo que he querido mostrar en este caso es una forma genérica que sirve con cualquier tabla y cualquier base de datos, donde todo se reduce a cortar/pegar algunas secciones de un archivo de texto a otro y dejar que FoxBin2Prg se encargue de la parte técnica.

Nota importante sobre triggers: Se debe tener en cuenta que cuando hay triggers de por medio, si bien FoxBin2Prg puede pasar la información de una BDD a otra, este caso sí requiere tener conocimientos de los mismos y de procedimientos almacenados, ya que aunque se pase automáticamente la información de la tabla y sus metadatos, no será igual con los procedimientos almacenados, los cuáles habrá que seleccionar cuidadosamente y pasar también a la base de datos de destino, pero además de esto se debe considerar si es posible hacer esta mudanza de base de datos, porque normalmente los triggers vinculan tablas (ej; si se borra o actualiza en una que se borre o actualice en cascada en otras) y es muy probable que eso implique o bien quitar triggers o bien mover varias tablas en conjunto.


Hasta la próxima! :D


viernes, enero 26, 2018

PBF: Metodología de Desarrollo bastante usada y no reconocida

Como sabemos, hay varias metodologías de desarrollo que han evolucionado desde sus comienzos hasta la actualidad, hagamos un breve repaso de ellas (definiciones tomadas de Wikipedia):


1970



1980



1990



2000



2010




Persistentes en el tiempo

Varias metodologías antiguas han desaparecido o ya casi no se usan, y hay metodologías que han comenzado alrededor del año 2000 y que han ido madurando en el tiempo, siendo (a día de hoy) las siguientes:


Pero hay una que no sale en nigún sitio, que no está documentada, con una (lamentablemente) larga fila de seguidores, y que creo que más de uno habrá visto, aunque no supiera su nombre:


Programación Basada en la Fé (PBF)
No hay link justamente porque formalmente no existe, pero está ahí...

¿Cómo funciona?
Es fácil: se hacen desarrollos en modo "para ayer", donde no hay tiempo para detenerse a comprobar la calidad o hay un mínimo de tiempo para algunas pruebas poco fiables porque ese tiempo de pruebas se ha ido reduciendo para poder meter alguna característica más.

¿Y las pruebas automatizadas?
Bien, gracias. Si hay suerte, puede que haya algunos tests semi-automatizados de hace varios años, cuando en algún momento había tiempo para los mismos, con lo que al ejecutarlos ahora podemos comprobar que algunas especificaciones de hace muchos años aún funcionan, pero de las nuevas no sabemos nada.

Obviamente, esta metodología genera muchas incidencias y los usuarios las reportan, y hete aquí dos puntos fuertes de esta metodología:

  1. Cada cierto tiempo las incidencias abiertas "se cierran", aún sin estar seguros de que se corrigieron, sobre todo las más antiguas o sangrantes, todo sea por que salgan bien las estadísticas. Si la incidencia no se reabre quiere decir que se ha acertado con la solución y que se puede seguir así, pero si se reabre, entonces significa que no hubo suerte y que hay que intentar otra cosa, incluso con el método de prueba y error, más una dosis de Fé (o sea, "ojalá que funcione").
  2. Con el tiempo los usuarios se cansan de reportar errores debido al desgaste, y entonces disminuyen las incidencias (las reportadas, no las reales), dando la señal aparente de que las cosas mejoran, cuando es todo lo contrario (nuevamente, favoreciendo las estadísticas)
De esta metodología solo puede esperarse una cosa: El Big Bang . Porque tarde o temprano ocurrirá y se irá todo al traste.

El sistema realmente es bastante perverso y las responsabilidades están repartidas entre todos los involucrados, desde los que piden cosas imposibles o innecesarias en tiempos ilusorios hasta los que debieran velar por la calidad de la solución y de la arquitectura de las aplicaciones, pasando por quienes deben gestionar todo el proceso.

Lamentablemente muchos son presa de esta metodología y aunque tengan buena voluntad, no tienen forma de luchas contra el Sistema, ya que ponerle solución implicaría hacer cambios que no todos están dispuestos a hacer. Al final la excusa de que "siempre se hizo así" muchas veces gana.

Cualquier parecido con la realidad es pura casualidad...


Seguramente alguno tendrá alguna anécdota para comentar al respecto.

Hasta la próxima!


jueves, enero 25, 2018

Visual FoxPro 9: Cómo arreglar ciertos tipos de corrupción en librerías VCX (_Memberdata)

En Visual FoxPro las librerías pueden funcionar con ciertos tipos de corrupción sin que nos demos cuenta y sin errores visibles o funcionales, pero cuando se trabaja con Control de Código fuente y se usan las vistas de texto, como la generada por FoxBin2Prg, a veces estos errores se hacen evidentes y siempre es mejor solucionarlos.

En este artículo veremos el caso de _Memberdata y cómo arreglarlo.



_Memberdata

_Memberdata es una propiedad que guarda la capitalización de las propiedades y métodos creados. Por defecto VFP crea todas las propiedades y métodos en minúsculas, pero con la propiedad _Memberdata VFP puede recordar la capitalización que le demos a la propiedad (Ver más información en la ayuda de VFP o en la web de Microsoft sobre MemberData Extensibility)

Por ejemplo, si creamos la propiedad "ValorMaximo" con el PEM Editor (de VFPx), la propiedad _Memberdada tendrá información con esta estructura:

<VFPData>
   <memberdata name="valormaximo" display="ValorMaximo"/>
</VFPData>


Donde:
  • name contiene el nombre original en minúsculas
  • type (opcionalmente) contiene el tipo de elemento ("Property" o "Method" habitualmente)
  • display contiene el nombre capitalizado a mostrar

Propiedad "ValorMaximo" creada con el PEM Editor


A medida que se van agregando propiedades capitalizadas, se irán agregando también elementos <memberdata name="xx" display="Xx">, y llegado un momento dado este campo puede llegar a ser bastante largo (puede tener hasta unos 8 KiB de longitud)

Este es un ejemplo de propiedad _Memberdata corrupta, visto con el Notepad++ desde la vista texto generada por FoxBin2Prg de la librería foxcharts.vcx en GitHub:



Pueden observarse algunos problemas evidentes:

  1. VFPData aparece varias veces, cuando solo debería aparecer 2 veces
  2. Siendo un miembro codificado en XML, debería tener un tag <VFPData> de inicio y un tag </VFPData> final, el cual no se ve
  3. El tag <VFPData> de inicio está anidado en sí mismo varias veces
Bueno, sabemos que esto está mal y queremos solucionarlo, ¿cómo se puede verificar la lista de propiedades que debería ir? Esa parte es fácil, solamente hay que echar un vistazo en la cabecera VC2 de la clase para ver qué propiedades y métodos se han definido:

*<DefinedPropArrayMethod>
    *m: caption_assign
    *m: reset        && Resets the legend GDI+ objects
    *m: rotation_assign
    *m: _drawstring
    *m: _setup
    *m: _updatemeasures
    *m: _value_assign
    *p: backcoloralpha
    *p: forecoloralpha
    *p: format        && Specifies the input and ...
    *p: format2
    *p: isparent
    *p: ogfx
    *p: rotationcenter
    *p: _forceformat
    *p: _height
    *p: _initialized
    *p: _memberdata        && XML Metadata ...
    *p: _obrush
    *p: _ofont
    *p: _orectangle
    *p: _ostringformat
    *p: _transfcaption
    *p: _value
    *p: _vartype
    *p: _width
*</DefinedPropArrayMethod>



Mirando nuevamente la propiedad _Memberdata y su contenido, puede verse un patrón de repetición de varias propiedades, donde básicamente se repite esta parte:

_memberdata = <VFPData>
   <memberdata name="autosize" ... display="AutoSize"/>
   <memberdata name="whatsthishelpid" ... display="WhatsThisHelpID"/>
   <memberdata name="_setup" ...display="_Setup"/>
   <memberdata name="forecoloralpha" ... display="ForeColorAlpha"/>
   <memberdata name="backcoloralpha" ... display="BackColorAlpha"/>
   <memberdata name="width" ... display="Width"/>
   <memberdata name="oledragpicture" ... display="OLEDragPicture"/>

   ...
</VFPData>        && XML Metadata for customizable properties
con lo que recortando cuidadosamente el resto de repeticiones hasta el tag de finalización </VFPData>, quedaría resuelta esta parte.

Nota: Revisando esta librería encontré que tiene más secciones <VFPData> con problemas más adelante, con lo que una vez detectado el problema siempre es conveniente buscar y revisar todas las estructuras <VFPData> para asegurarse de que están correctas.


Puede pasar que alguna de las propiedades descriptas en los miembros <memberdata> esté incompleta o cortada, por ejemplo podría contener solamente la parte del "name" pero no la parte del "display".

Nuevamente, conociendo que la estructura mínima de un miembro debe contener el name y el display, simplemente se puede crear manualmente la propiedad display y ponerle un valor que seguramente va a ser bastante obvio: el nombre de la propiedad capitalizada que querramos que se muestre en el IDE de VFP.



Resumen


Una vez resueltos todos los problemas, se regenera el binario desde esta vista texto (click-derecho sobre el archivo VC2, elegir "Enviar a" -> FoxBin2Prg)

Como se ve, realmente no es difícil solucionar varios de estos problemas, pero sí es necesario dedicar un rato para analizar el problema, conocer qué es lo que estamos modificando, cuál es la estructura correcta e implementar la solución.


Hasta la próxima! :D