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

lunes, diciembre 03, 2012

VFP: Obtener el nombre del PC en ambiente Terminal Server

Siempre he dicho que en lo simple esta la belleza del Universo y hoy vi esto confirmado una vez mas.

Es un escenario cada vez mas común el de ejecutar sistemas administrativos mediante sesiones de Terminal Server.  En este escenario, la función SYS(0) devuelve el nombre del equipo donde se esta ejecutando la sesión de TS y NO el equipo "cliente", que seria el que en definitiva nos interesaría. Este problema lo había resuelto hace tiempo mediante llamadas al API de TS que me permitían obtener el nombre del equipo cliente.

Pero hoy, el gran Maestro Luis Maria Guayan me ha sorprendido con una forma mucho mas simple y elegante de resolver el problema:


IF UPPER(LEFT(GETENV("SESSIONNAME"),3)) == "RDP" 
  lcPC = GETENV("CLIENTNAME")
ELSE
  lcPC = GETENV("COMPUTERNAME")
ENDIF



Increíblemente simple!!



lunes, febrero 20, 2012

VFP: Manejador de eventos en tiempo de ejecucion

Hay muchas situaciones en las que terminamos necesitando que una instancia de una clase propia pueda ejecutar codigo arbitrario en uno o mas de sus eventos, como por ejemplo en clases que se instancian en tiempo de ejecucion en lugar de hacerlo en tiempo de diseño.

Habiéndome topado con estos casos muchas veces, hace unos dias terminé escribiendo una clase que me permite añadir un manejador de eventos dinámicos a cualquier clase de forma sencilla. El primer paso es crear un manejador de eventos en la clase. Para esto le añadimos una propiedad que contendrá la instancia del manejador de eventos, la cual es creada en el constructor de la clase (evento Init):

THIS.Events = CREATEOBJECT("VEEventHandler")

Lo siguiente es definir los eventos que estarán disponibles y la forma de manejar los mismos. La clase permite dos formas de manejar los eventos declarados cuando estos son invocados.

Usando una clase controller
Consiste en asociar un metodo de una clase controladora al evento declarado de modo que, cuando se invoque el evento indicado este procesa a invocar el metodo indicado en la clase controladora. La principal ventaja de este metodo es que la asociacion entre el evento y su controlador puede ser modificada sin que esto afecte el código existente. La forma de definir estos eventos seria así:

THIS.Events.Add("sayHello","Hello",oController)

Lo que estamos diciendo aqui es:
define un evento llamado "sayHello" el cual, cuando sea invocado, llamará al método "Hello" del objeto "oController".

Usando codigo dinámico
Consiste en asociar un codigo VFP válido al evento, de modo que cuando este sea invocado se ejecute el método asociado al evento. La ventaja principal de este método es que no necesitamos de un segundo objeto que maneje los eventos y ademas el código asociado al evento puede cambiar dinámicamente sin afectar el resto del codigo de la aplicación. La forma de definir estos eventos es:

THIS.Events.Add("sayHi",cSayHiCode)


Como invocar los eventos
Una vez definidos los eventos que estarán disponibles en la clase, lo único que queda es invocarlos cuando sea necesario. La clase ofrece dos formas de invocar estos eventos:

Invocación directa
Consiste en invocar el evento directamente por su nombre:

miObjeto.Events.sayHello.Raise("World!")

El método Raise acepta hasta 10 parámetros, los cuales son pasados al método del controller asociado al evento, o a la sentencia PARAMETERS o LPARAMETERS del código asociado al mismo.

Invocación indirecta
Esta forma permite invocar el evento indirectamente, mediante un string que identifica el nombre del evento:

miObjeto.Events.Raise("sayHello","Mundo!")

El primer parámetro del metodo Raise recibe el nombre del evento a invocar y a partir de ahi los parámetros son pasados directamente al evento.

La clase con su código fuente y ejemplos puede ser descargada desde aqui.


NOTA IMPORTANTE
Mucha de la funcionalidad proporcionada por esta clase, sobre todo usando la figura de controllers, puede ser implementada de forma nativa en VFP a partir de la introducción del comando BINDEVENTS.  La ventaja que da esta clase es que la misma es compatible con cualquier versión de VFP (solo usando eventos manejados por controller; los eventos con código dinámico requieren de VFP 7 o superior).

sábado, febrero 04, 2012

VFP: Un parser para JSON


Para los que, como yo, nunca hayan escuchado hablar de JSON, se trata de una tecnologia ampliamente usada en la web para el intercambio de datos entre sistemas desconectados. Básicamente es lenguaje muy simple que permite definir objetos y su contenido. Los parsers JSON hacen dos cosas:

a) Toman una referencia a un objeto (normalmente JavaScript) y genera una representación JSON en forma de string
b) Toman un string JSON y devuelve el objeto original

A pesar que en el sitio JSON.org se pueden encontrar dos parsers JSON para VFP, decidi hacer mi propio parser usando solo código VFP y, de paso, incluyendo un par de mejoras al estilo fox.

La mejor de estas adiciones es lo que llame "schema". Un schema es basicamente una forma de definir uns estructura de datos reutilizable, al estilo de la construcción STRUCT en C# (pero sin la posibilidad de incluir metodos).

Por ejemplo, para definir el schema "User":

JSON.declareSchema("User","{loginName:string, fullName: string, profileId: numeric, active: boolean}")

Luego, cuando queremos trabajar con un objeto que represente un usuario, solo hacemos:

oUser = JSONObject("schema:User")
oUser.loginName = 'vespina'
oUser.fullName = 'Victor Espina'
oUser.profileId = 25
oUser.active = .T.


Supongamos ahora que queremos almacenar los datos del usuario que modifico un registro. Con JSON podriamos hacerlo asi:

SELECT clientes
REPLACE rowLastUpd WITH DATETIME(),;
        rowUpdBy   WITH oUser.ToJSON()

Luego, cuando queramos mostrar quien modifico el registro, hacemos:

oUser = JSONObject(clientes.rowUpdBy)
MESSAGEBOX("Este registro fue modificado por " + oUser.fullName)


Otro uso muy comun de JSON es crear objetos sobre la marcha de forma sencilla:

oPoint = JSONObject("{x:10, y:20}")
?oPoint.x --> 10


Como ven, las posibilidades son muchas. En la documentacion de la libreria hay multitud de ejemplos de todo lo que se puede hacer con ella.


Descargar qdfoxJSON (version 1.2 del 9 Feb de 2012).

viernes, noviembre 18, 2011

VFP: Clausula READWRITE para VFP 6

Como sabemos, los cursores creados por el comando SELECT INTO en VFP son solo lectura. A partir de VFP 7 se incluyo una clausula llamada READWRITE que nos permite indicar si queremos que el cursor sea de lectura-escritura, lo cual es sumamente útil en infinidad de situaciones.

Sin embargo, para los que por algún motivo aun debemos seguir trabajando con versiones anteriores a VFP 7, convertir un cursor a lectura-escritura representa todo un reto.  El truco mas usado para lograr esto en VFP 6 es usar la clausula NOFILTER para asegurarnos que VFP creara un cursor fisico temporal, hacer una copia fisica del cursor temporal con otro nombre, cerrar el cursor original y luego abrir el cursor temporal con el nombre original del cursor:

SELECT * FROM mitabla INTO CURSOR Q1 NOFILTER
SELECT Q1
COPY FILE (DBF("Q1")) TO ("Q1.TMP")
USE IN Q1
SELECT 0
USE ("Q1.TMP") ALIAS Q1

Pero esta solución dista mucho de ser perfecta, especialmente si el cursor original contaba con columnas cuyo nombre superara los 10 caracteres. En estos casos, el nombre de esas columnas era truncado lo cual causaba molestos problemas con las referencias a esas columnas en el código.

Hace un tiempo ayudé a un amigo a encontrar una forma de solventar este problema, y me encontré con que, como siempre, VFP me daba todas las herramientas necesarias para lograrlo; era solo cuestión de ponerlas a trabajar juntas.

En este caso, la ayuda llegó de la mano de los arrays.  Yo sabía desde siempre que VFP contaba con una función que tomaba la estructura de un DBF o cursor y la almacenaba en un array.  Lo que no sabía es que tanto el comando CREATE CURSOR como INSERT INTO contenían la clausula FROM ARRAY.

El resultado?  una función que recibe el nombre de un cursor solo-lectura y lo convierte en lectura-escritura respetando los nombres largos de columna.

Aqui les dejo la función por si les resulta útil (NOTA Nov 19: limpié y mejoré un poco el código, ademas de corregir un problema cuando el cursor original estaba vacío).

* openForUpdate()
* Función que recibe un cursor de S/L y lo convierte en L/E respetando las columnas con nombres largos
*
* Autor: Victor Espina
*
PROCEDURE openForUpdate(pcCursor)
 *
 * Obtenemos el schema del cursor
 LOCAL ARRAY aSchema[1]
 AFIELDS(aSchema, pcCursor)

 * Obtenemos el contenido del cursor
 LOCAL ARRAY aRows[1]
 LOCAL nRowCount
 SELECT * FROM (pcCursor) INTO ARRAY aRows
 nRowCount = _TALLY

 * Cerrar el cursor original, lo recreamos como un cursor de L/E
 * y lo llenamos con los datos originales (si habia)
 SELECT (pcCursor)
 USE
 CREATE CURSOR (pcCursor) FROM ARRAY aSchema
 IF nRowCount > 0 
  INSERT INTO (pcCursor) FROM ARRAY aRows
 ENDIF
 GO TOP 
 *
ENDPROC

martes, noviembre 15, 2011

VFP: Crear un buffer rapidamente

Primero, aclaremos conceptos. Llamo "buffer" a un objeto muy liviano cuya unica función es contener data temporal en forma de propiedades. En muchos sentidos vendria a ser como una versión light de un STRUCT, solo que sin la posibilidad de incluir métodos; solo propiedades.

Por ejemplo, si quiero pasar información sobre una persona de un form a otro, me seria muy útil pasar un solo parámetro que contuviera toda la información de esa persona.  Una forma de lograr esto es crear una clase "Persona" que contenga las propiedades para almacenar la información sobre una persona, pero muchas veces queremos lograr lo mismo sin la sobrecarga de crear una clase que en si misma no hace nada.

Ese objeto "temporal" que sirve para almacenar múltiples informaciones relacionadas, es lo que llamo "buffer".  La función crearBuffer hace justamente eso: crea un buffer con una lista de propiedades dada y, opcionalmente, inicializa esas propiedades con los datos suministrados; todo en una misma instrucción.

Ejemplo:

LOCAL oPersona
oPersona = crearBuffer("nombre,apellido,cedula,fechaNac,cargo","Victor","Espina","12345678",{18-11-1970})

?oPersona.Nombre --> "Victor"
?oPersona.Apellido --> "Espina"


Práctico, cierto?  aqui les dejo el código fuente.

*-- crearBuffer
*   Funcion para crear un buffer de datos e inicializarlo. Compatible con VFP 5 o superior
*
*   Autor: V Espina
*
*   Ejemplo:
*   oBuff = CFDBuffer("Nombre,Apellido","Victor","Espina")
*   ?oBuff.Nombre -> "Victor"
*   ?oBuff.Apellido -> "Espina"
*
PROCEDURE crearBuffer
LPARAMETERS pcItemList,p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19
 *
 LOCAL oBuff,i,cProp
 oBuff=CREATEOBJECT("Custom")

 LOCAL cPropName,uPropValue,nCount
 LOCAL ARRAY aProps[1]
 nCount = ALINES(aProps,STRT(pcItemList,",",CHR(13)+CHR(10)))
 FOR i=1 TO MIN(nCount,20)
  cPropName = aProps[i]
  uPropValue = EVALUATE("P" + ALLTRIM(STR(i - 1)))
  oBuff.AddProperty(cPropName, uPropValue)
 ENDFOR
 
 RETURN oBuff
 *
ENDPROC

lunes, noviembre 07, 2011

VFP: Como crear un dll en .NET y usarlo desde VFP


Alguien una vez dijo que no es cuestion de cambiarse de un lenguaje a otro sino de usar la herramienta adecuada al problema que se desea resolver. C# es excelente para muchas cosas en las que VFP no es tan bueno, por lo que el poder crear un DLL en C# y usarlo en VFP se convierte en algo muy poderoso.

Aqui les dejo todo un conjunto de tips para lograr justamente esto:

1. En tu clase C# debes incluir el namespace System.Runtime.InteropServices;

2. La clase que deseas accesar desde VFP debe estar declarada como public y tener estas directivas:

[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("className")]

donde "className" es el nombre OLE de la clase, es decir, el que usaras en el CREATEOBJECT en VFP.

3 Los metodos de la clase que quieras accesar desde VFP deben estar declarados como public y no ser estaticos.

4. Los metodos publicos de la clase no pueden tener sobrecargas, es decir, multiples versiones del mismo metodo con diferentes parametros.

5. Si un metodo devuelve un tipo estructurado, este tipo debe ser creado como class (no como struct) y cumplir con todas las condiciones indicadas en los puntos 1, 2, 3 y 4.

6. Si un metodo devuelve un array de cualquier tipo (ej, string[]), VFP lo recibe como un tipo ARRAY y no como un objeto (como lo es en .NET), por lo que no podremos acceder a las propiedaes del array, tal como Lenght, pero si a sus items:

aItems = myClass.MyMethod()
FOR EACH oItem IN aItems
 ?oItem.Property
ENDFOR

o

aItems = myClass.MyMethod()
FOR i = 1 TO ALEN(aItems,1)
oItem = aItems[i]
 ?oItem.Property
ENDFOR

7. Antes de compilar la clase, debes ir a las propiedades del proyecto, Application, boton Assembly Information y marcar la casilla "Make assembly COM-visible"

8. Debes firmar la DLL. Para esto, sigue los pasos indicados aqui

9. Una vez compilada la dll, la misma debe ser registrada de la siguiente forma antes de poder ser usada en VFP:

Windows 32 bits:
C:\WINDOWS\microsoft.net\framework\v2.0.50727\regasm mylib.dll /register /codebase /tlb

Windows 64 bits
C:\WINDOWS\microsoft.net\framework64\v2.0.50727\regasm mylib.dll /register /codebase /tlb



sábado, marzo 26, 2011

VFP: De vuelta a casa

Luego de un par de anos trabajando principalmente con .NET en todos sus sabores (ASP.NET, WinForms, Compact Framework), el mes pasado empece un proyecto que me ha llevado de nuevo a utilizar VFP como ambiente principal de desarrollo.

Y la verdad, ha sido como volver a casa. Una casa renovada, eso si, pues el proyecto esta basado en VFP 9 por lo que he podido hacer uso de varias de las nuevas opciones que vinieron en esa excelente versión.

Paradójicamente, cada vez mas me encuentro a mi mismo aplicando en VFP conceptos que aprendí en .NET y específicamente en C#. En lugar de constantes he simulado enumeraciones usando la clase Empty y la función ADDPROPERTY, en lugar de usar una variable global donde almacenar el handle de conexión devuelto por SQLEXEC() me encontré representando la conexión con una clase llamada SqlConnection. Asi que, cuando antes hacia:

LOCAL nConn,nResult
nConn = 0

TRY
nConn = SQLSTRINGCONNECT(cConnStr)
nResult = SQLEXEC(nConn,cSQL,cCursor)

CATCH TO ex

FINALLY
IF nConn <> 0
SQLDISCONNECT(nConn)
ENDIF

ENDTRY

ahora hago:

LOCAL oConn
oConn = CREATEOBJECT("SqlConnection",cConnStr)

TRY
oConn.Open()
oConn.Send(cSQL,cCursor)

CATCH TO ex

FINALLY
IF oConn.State = connectionState.Opened
oConn.Close()
ENDIF

ENDTRY

Cómico verdad? pero lo cierto es que no solamente yo me siento mas cómodo con la 2da versión, supongo que porque me recuerda a lo que he venido usando en los últimos anos, sino que ademas creo que el código final es mas legible y mucho mas reusable y encapsulado también.

Por cierto, si alguien sabe como simular el concepto de Interfaces en VFP, que me avise, porque esa es una de las cosas de .NET que quisiera tener en VFP !!


VFP + SQL: Cuidado al instalar el OLEDB Provider de VFP

Haciendo algunas pruebas con Linked Servers en SQL Server, necesite instalar el OLE DB Provider de VFP 9.0, el cual descargue directamente desde el sitio oficial de Microsoft.

Sin embargo, luego de haber descargado el instalador y haberlo corrido sin problemas, la opción de OLE DB Provider para VFP no aparecía en la lista de Providers de SQL Server (32 bits).

Luego de varios minutos de búsquedas en San Google, encontré un post donde indicaban que por alguna razón el instalador no registraba la librería correctamente, y que la misma debía ser registrada manualmente para que funcionara (aunque para el autor del post esto no soluciono del todo su problema, pero esa es otra historia).

Así que abrí una sesión de comandos y ejecute:

CD \Windows\System32
REGSVR32 vfpoledb.dll

Y efectivamente el problema se soluciono y la opción apareció en la lista de providers de SQL Server. Asi que ya saben; si necesitan instalar el provider de OLE DB de VFP, luego de instalado deben registrarlo manualmente para que funcione.

Y si alguien tiene idea de porque demonios el instalador de M$ no hace el registro automáticamente como uno esperaría, que por favor nos ilumine con su sabiduría.




viernes, marzo 27, 2009

VFP 6 SP5

Para los que aun usamos esa maravillosa herramienta llamada Visual FoxPro, pueden descargar el SP5 de la versión 6.0 desde aqui:

http://download.microsoft.com/download/vstudio60ent/SP5/Wideband-Full/WIN98Me/es/vs6sp5.exe

Pongo el post porque me resulto sorprendentemente difícil encontrar este link, asi que pense que poniendolo por aca de repente ayuda a alguien más que lo ande buscando como yo.

lunes, octubre 15, 2007

Pablo Roca y los MVP

La semana pasada M$ anunció la lista de los nuevos MVPs y donde no apareció incluido mi buen amigo Pablo Roca.

Por si a estas alturas aun queda algún programador de VFP que no sepa quien es Pablo Roca, es nada mas y nada menos que el fundador de PortalFox, el mayor portal de habla hispana sobre VFP, que ya tiene mas de 28.000 usuarios en todo el mundo.

Obviamente las razones de porque M$ excluyó a Pablo luego de varios años de otorgarle merecidamente el premio, nunca las sabremos. Pero en nuestra opinión, se trata de una represalia por la campaña MasFoxPro, que TAMBIEN fué ideada y liderizada por Pablo, dado que los méritos por los cuales M$ le venía otorgando ese premio a Pablo año tras año SIGUEN siendo válidos hoy en dia... la unica "diferencia" entre esta entrega y las otras es, justamente, la campaña MasFoxPro.

Lo que M$ no ve y nunca entenderá es que Pablo ni nadie en la comunidad Fox necesita sus "piches" premios para ser reconocido; el mejor reconocimiento es el que le damos todos nosotros cuando aplicamos tantas cosas aprendidas de él y otros que, como él, dedican algunos minutos TODOS LOS DIAS a ayudar a otros programadores Fox.

Animo Pablo! PortalFox seguirá incluso cuando nadie en M$ se acuerde que ellos vendian algo llamado "FoxPro".

lunes, septiembre 24, 2007

VFP: Ejecutar un script SQL por código

Hoy me encontré con una pregunta en foro de programadores de VFP, sobre como hacer para ejecutar un script SQL desde VFP.

Me topé con este problema hace ya unos cuantos años, y recuerdo que fue muy frustrante darme cuenta que el comando SQLEXEC() no estaba diseñado para ejecutar varias instrucciones sino mas bien una sola.

En aquel tiempo se me ocurrió que dado que en un script SQL todas las instrucciones se terminan con un comando GO, se podría utilizar este comando como un "separador", a fin de aplicar un "parsing" al script SQL y usar SQLEXEC() para ejecutar cada instrucción individualmente.

El código pueden copiarlo desde el artículo original en mi website:
Como ejecutar un script SQL desde VFP

martes, abril 03, 2007

VFP: MS Respondio a MasFoxPro

El blog de Mary Jo Foley acaba de incluir un comunicado de Alan Giver, un group manager del equipo de Visual Studio, referente a la campana MasFoxPro.
El comunicado dice lo siguiente:
"Estamos muy al tanto de la comunidad de FoxPro y esto tuvo mucho que ver con lo que anunciamos el 13 de Marzo. Nunca es sencillo anunciar que no liberaríamos una nueva versión de un producto, y este (VFP) es uno que consideramos muy cuidadosamente.
No estamos anunciando el fin de FoxPro: obviamente, las aplicaciones hechas en FoxPro continuaran funcionando. Según algunos estimados internos, hay mas aplicaciones ejecutando en FoxPro 2.6 que las que ejecutan actualmente en VFP y FoxPro 2.6 no ha sido soportado en muchos anos. Visual FoxPro 9 sera soportado por Microsoft hasta el 2015.
Para que Microsoft continuara el desarrollo de la base de FoxPro, tendríamos que considerar el crear un ambiente de desarrollo de 64 bits y esto implicaría una reprogramación casi completa del núcleo del producto. También hemos hecho inversiones en crear bases de datos escalables con SQL Server, incluyendo SQL Server Express Edition que esta disponible gratuitamente. Con relación a formar una alianza con un tercero, muchos grandes clientes de FoxPro nos han dicho que esto les haría casi imposible continuar usando FoxPro debido a que ya no estaría respaldado por un proveedor renombrado. Sentimos que haber colocado el ambiente como código abierto en CodePlex, que equilibra las necesidades de la comunidad y los grandes clientes, fue la mejor ruta a seguir."
Personalmente me parece una patética respuesta, que no dice nada nuevo sobre las razones de MS para cerrar VFP. Sobre todo eso de que "grandes clientes dicen que si VFP no es MS no pueden justificar su uso" es una patraña.

VFP: MasFoxPro la pego al techo!!

La campana de www.masfoxpro.com ha logrado su primer éxito contundente. La columnista Mary Jo Foley acaba de publicar una entrada en su blog sobre MasFoxpro, que de seguro va hacer pensar a mas de uno en MS.

Para mas información, ver:
http://blogs.zdnet.com/microsoft/?p=361

viernes, marzo 16, 2007

VFP: Un toque de nostalgia

A proposito de la gran tristeza que embarga desde hace unos dias a la gran comunidad de programadores de VFP, aqui les traigo un link hacia una pagina que contiene informacion sobre la historia de nuestro querido zorro.

Para los que como yo han usado el Fox desde los tiempos de FoxBase, de seguro esta pagina les traera muchos recuerdos agradables.

http://www.foxprohistory.org/home.htm

miércoles, marzo 14, 2007

VFP: Ha muerto el Rey

Hoy es un dia triste en verdad. Luego de mantenerse por casi 20 anos como una de las mejores herramientas para desarrollo de BD (cuidado si no la mejor), Microsoft ha decidido poner fin a la saga de FoxPro.

En un mensaje publicado en el sitio oficial de FoxPro en MS, el VFP Team ha anuciado que no habra una version 10 de VFP, aunque MS seguira dando soporte oficial a VFP9 hasta el 2015.

El SP2 de VFP9 se espera para mediados de este ano y pronto estara tambien disponible en forma gratuita el proyecto Sedna, que ampliara considerablemente las capacidades de VFP9.

Aun cuando todo esto significa que tecnicamente VFP aun sera una herramienta competitiva para crear nuevos desarrollos por unos cuantos anos mas, en la practica la realidad es otra: las empresas no querran hacer grandes inversiones en una plataforma que ya esta sentenciada, por lo que para todos los efectos VFP esta muerto desde ya.

Esto tambien sera un duro golpe para las empresas cuyos productos principales estan hechos en VFP, pues los nuevos clientes no querran comprar una aplicacion basada en una tecnologia que se quedara obsoleta en 5 o 6 anos. Estas empresas no tendran otra salida ahora que la de empezar a pensar en migrar sus aplicaciones a otras plataformas como .NET o Java, con todo el costo y las limitaciones que esto implica.

La unica esperanza que queda es que MS decida liberar el codigo fuente de VFP de modo que la comunidad OpenSource pueda "tomar el testigo" y seguir desarrollando esta magnifica herramienta.

Este es el enlace al mensaje original mostrado en el sitio web de VFP:
http://msdn2.microsoft.com/en-us/vfoxpro/bb308952.aspx

martes, julio 18, 2006

VFP entre los primeros 20

VFP ascendió por primera vez en la historia a la lista de los 20 lenguajes de programación mas usados, según el indice TIOBE.

A pesar de que este indice es criticado por algunos analistas, lo cierto es que es una medida de cuan "popular" es un lenguaje de programación dado... y tener a VFP dentro de los 20 mas usados es una excelente noticia para todos los que disfrutamos de esta excelente herramienta.