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

21 de enero de 2020

Chequear si una conexión a internet esta activa

La mejor manera para chequear si una conexión a Internet se encuentra disponible es beneficiarse de un acceso PING. He aquí una función pequeña que recibe una dirección de servidor como parámetro para chequear si una conexión a Internet se encuentra disponible. Nótese que puede ser que usted quiera especificar otra dirección de servidor y así asegurarse probando con otro servidor

? CheckInternetConnection("http://www.google.com")

FUNCTION CheckInternetConnection
LPARAMETERS tcWebAddress
DECLARE Integer InternetCheckConnection ;
   IN WININET.DLL ;
   String Url, Long dwFlags, Long Reserved
IF InternetCheckConnection(tcWebAddress,1,0) # 0
   RETURN .T.
ELSE
   RETURN .F.
ENDIF

21 de octubre de 2017

Aplicaciones VFP a traves de Internet usando VPN

Hace algunos años, allá por la versión FoxPro 2.6 para Windows, necesitaba ejecutar una aplicación a través de Internet. Desde entonces hasta la fecha he probado un sin fin de productos. Desde FoxWeb, FoxNet, WestWind Connection, AFP, Fox#,VFPServer, VFP Framework, etc. Desde servidores construidos por el propio código VFP hasta CGI o interacción con ASP.

Primeramente, que es una aplicación VFP a través de Internet? Necesito en verdad crear una interface a través de un navegador para acceder a las bases de datos de la aplicación? Necesito controles llamativos como DBI o Voodo para una interfaz mas rica al usuario? Necesito solamente la transferencia de datos desde un lugar remoto para actualizar, capturar, consultar?

Por cierto que hay varios tipos de necesidades, desde las del usuario (interfaz, facilidad, compatibilidad) hasta las técnicas (sobrecarga del servidor, memoria de la aplicación, instalaciones en los clientes, etc.) Es decir, en algunos casos es necesario tener un servidor web configurado para traer los datos bajo demanda, rápidamente y sin tener que instalar aplicación alguna en los clientes a control remoto, basta un simple navegador para tener los datos. Pero en otros casos, quizá lo que realmente necesitamos es: CONECTAR NUESTRA APLICACION CON LOS DATOS A TRAVES DE INTERNET COMO SI LO HICIERAMOS A TRAVES DE LA RED LOCAL.

Para ello necesitamos lo siguiente:

  1. Una VPN
  2. Una aplicación VFP cualquiera que una pequeña modificación para acceder a la base de datos.
  3. Una conexión a Internet (banda ancha de preferencia)
  4. Un servidor y un cliente.

Recordemos que necesitamos un servidor donde residen nuestra base de datos, esto funciona bien para nuestra LAN, y un cliente o clientes donde tendremos nuestra aplicación. Es posible que varias PCs, laptops tengan el sistema VFP que queremos usar, así que este estará instalado completamente con todas sus pantallas e interfaces, lo único que haremos será conectarnos con el servidor de datos para trabajar con ellos.

PASOS A SEGUIR

El primer paso es usar una VPN (Virtual Private Network). Se que hay varias soluciones tanto de hardware como de software, pero aquí mismo me encontré hace tiempo una excelente: HAMACHI. Pequeña, gratuita y fácil de manejar.

Así, descarguemos Hamachi desde su pagina https://secure.logmein.com/hamachi.msi. En 10 minutos estaremos usando nuestra VPN.

Una vez descargado, necesitamos instalar el Hamachi en lo que será nuestro servidor, puesto que funciona de manera transparente a través de Firewalls, la instalación es por demás sencilla. Necesitamos crear nuestra red, y dar de alta el nombre de nuestro server, así como su password. La empresa entonces nos dará una IP especial a esta computadora. Podemos hacer que Hamachi cargue de manera automática al encender la PC Servidor (aunque si es Servidor, este nunca se apagara).

Ahora, instalamos Hamachi en la computadora cliente, siguiendo casi los mismos pasos, pero en este caso, al ya tener una red hecha, nos incorporaremos a esta, de tal manera que tanto el servidor como el cliente estén en la misma red para que puedan reconocerse. Como los grupos de trabajo de Windows.

Carguemos el Hamachi entonces para conectarnos con el Servidor.

Aquí vemos el Hamachi en acción. Para proteger la identidad se han borrado los nombres de la red y números de IPs.

En este caso, la computadora cliente esta conectada a 3 redes Hamachi distintas. 2 en la misma empresa y una externa. Vemos que solamente 1 PC esta en línea (la segunda) que es el servidor que nos queremos conectar.

Aquí suponemos que el Hamachi esta ejecutándose del lado del servidor.

Y ESO ES TODO LO QUE NECESITAMOS PARA QUE NUESTRAS APLICACIONES FUNCIONEN A TRAVES DE INTERNET. No necesitamos un IP Fija, o conocer direcciones o dominios. Ya que Hamachi ha hecho todo esto por nosotros. Lo importante aquí, es conocer el numero de IP que este le esta dando al servidor.

Ahora, veamos como hacemos la conexión de nuestra aplicación.

En la imagen vemos que para comenzar a usar la aplicación primeramente tenemos que seleccionar la empresa. En este caso tenemos 2 empresas pero se acceden de 3 maneras diferentes:

  1. De manera local (en la propia PC o laptop)
  2. De manera Red (LAN)
  3. O por Internet.

Aunque eliminare algo de código por cuestiones de seguridad, creo que tendrán la idea. Esto funciona teniendo una tabla DBF (que puede ser libre) con unos cuantos campos como nombre de la empresa, fecha de alta, etc. Pero un campo donde pondremos la dirección donde reside.

Así, en el primer caso de manera local, tendremos:

C:\sistemas\clientes\basedato\empresa\ 

Para la red, tendremos algo así:

\\servidor\e\clientes\basedato\empresa\. 

(Noten que no estoy usando un mapeo de la unidad sino una dirección de red, que se puede sustituir por su IP si se desea).

Para internet:

\\5.61.46.25\e\clientes\basedato\empresa 

Aquí notaran la primera parte que es la dirección IP de nuestro VPN Hamachi. Estamos diciéndole que acceda al servidor que esta en esta red. El único requisito es que si vamos a usar esta aplicación con datos a través de internet, es necesario primero activar la Hamachi y que este conectado, de otra manera fallara. Y listo. Una vez configurada la ruta de los archivos, abrimos los formularios, reportes, consultas y estaremos trabajando directamente con el servidor.

Esta ruta la almacenamos en una variable publica que esta presente en toda la sesión de trabajo.

Una vez terminado, cerramos la aplicación, cerramos el Hamachi (si queremos) y listo.

La ventaja de esta técnica es que disponemos de toda la riqueza visual de VFP en nuestros formularios, controles ActiveX, etc., y el código sin grandes modificaciones ya que no nos preocupamos donde reside. He trabajado con esto desde hace meses con tablas que rondan los 200MB y solamente hay un pequeño tiempo de espera la primera vez que se conecta, pero una vez abiertos los archivos, la captura, edición, borrado de los registros es extremadamente rápida, los índices se actualizan sin problema, casi no notamos que estamos trabajando a través de internet. En lo personal me ha servido para actualizar ciertos datos desde casa en el trabajo. O hacer consultas a la base de datos.

Desventajas. Por cierto que las hay. Aunque Hamachi es gratuito, tiene un limite de 16 clientes en una misma red, es decir, solo 16 PC conectadas. Si su ancho de banda es buena, y su código es eficiente, no debe tener problemas para acceder a varios clientes al mismo tiempo, (yo lo he usado con hasta 4 clientes a la vez). El cliente tiene que tener la aplicación compilada y sus runtimes.

A su favor, es que no modificamos gran cosa en el código, mas que la ruta de los archivos, no tardamos mas de 10 min en instalar y configurar Hamachi... y es gratuito... además, no solamente nos servirá para esto, ya que al tener la VPN, podremos hacer muchas mas cosas como control remoto a distancia (con VNC por ejemplo), mejor que un ftp y mas rápido, conexiones seguras y algunas futuras aplicaciones.

Por supuesto para conexiones mas robustas y con mayor cantidad de clientes en la conexión, habrá que usar otras herramientas, pero si desean hacer una conexión rápida, barata, sencilla, fácil de administrar, creo que esta opción es bastante recomendable.

Que estén bien.

Sergio Hugo Sanchez

8 de marzo de 2016

Conversor de divisas utilizando la API de Google

? ConvertirDivisa(1, "USD", "ARS")  && 1 US Dolar -> Peso de Argentina 
? ConvertirDivisa(10, "EUR", "ARS")  && 10 Euros -> Peso de Argentina
? ConvertirDivisa(100, "ARS", "USD") && 100 Pesos de Argentina -> US Dolar

FUNCTION ConvertirDivisa(pnMonto, plFrom, plTo)
  LOCAL lc, lcUrl, la(1)
  DECLARE LONG URLDownloadToFile IN URLMON.DLL ;
    LONG, STRING, STRING, LONG, LONG
  ERASE "cambio.txt"
  lcURL = "https://www.google.com/finance/converter?a="+TRANSFORM(pnMonto)+"&from="+ plFROM +"&to=" + plTO
  IF 0 = URLDownloadToFile(0, lcURL, "cambio.txt", 0, 0)
    TRY
      INKEY(1)
      lc = FILETOSTR("cambio.txt")
      ALINES(la,lc,1,"<div id=currency_converter_result>")
      lc=la(2)
      ALINES(la,lc,1,"</span>")
      lc = STRTRAN(la(1),"<span class=bld>", "")
    CATCH
      lc =  "Error de divisas"
    ENDTRY
  ELSE
    lc =  "No hay conexion"
  ENDIF
  RETURN lc
ENDFUNC

Los códigos válidos de las distintas divisas están en la siguiente tabla:

21 de diciembre de 2015

Google Calendar API y oAuth 2.0 con Visual FoxPro 9

Aquí les dejo mi regalo de Navidad para todos los colegas de la Comunidad de Visual FoxPro en Español. Felices Fiestas y un excelente Año 2016 para todos!!!

Google Calendar API y oAuth 2.0 con Visual FoxPro 9

José Enrique Llopis
Alicante - España
Kansas City - Missouri - USA
jellopis@rocketmail.com
www.multilinkcrm.com
Mi perfil online: https://es.linkedin.com/in/pepellopis

A - Configurar Google Authentication OAuth 2.0

Ejecute la Google Developers Console https://console.developers.google.com y haga clic en Create a new Project:

Complete los datos del proyecto

Debe activar las API’s que usará en este proyecto, para hacer esto haga clic en el botón Enable and Manage APIs.

Seleccione Google Calendar API

Por supuesto esta técnica le abre la puerta para usar todas las restantes API de Google, no solo Calendar

Haga clic en el botón Enable API, como se muestra en la imagen

¡¡¡MUY IMPORTANTE!!! Debe crear las credenciales oAuth 2.0

Haga clic en Credentials y seleccione oAuth client ID

Ahora, usted podrá configurar la pantalla que Google mostrará al usuario para autorizar la aplicación, para hacer esto haga clic en Configure Consent Screen.

Complete los datos de la pantalla “Consent” y haga clic en Save

La siguiente cosa que debe hacer es seleccionar el tipo de aplicación, en este caso debe elegir la opción OTHER y hacer clic en Create.

Ahora tendrá las credenciales que le permitirán usar la aplicación con Google Calendar

¡¡¡GUARDE ESTOS DATOS!!!

Estos datos los puede ver posteriormente desde la Google Developers Console

B - Configurar la aplicación de ejemplo

Edite el fichero include: ./Include/xGCDefs.h

Debe cambiar donde pone YourClientID y YourClientSecret por las credenciales personalizadas que ha obtenido en el paso anterior

#define GC_CLIENT_ID YourClientID
#define GC_CLIENT_SECRET YourClientSecret

Cambie el texto YourClientID por el dato Client_ID

Cambie el texto YourClientSecret por el dato ClientSecret

Guarde el incluye y ahora podrá ejecutar la aplicación de ejemplo

Haga clic en CONNECT y ESPERE!!!!

Ahora haga clic en el botón Permitir

En este momento usted podrá seleccionar el calendario correcto, por defecto corresponde con la dirección de GMAIL, aunque usted ha podido crear otros adicionales.

Por ultimo podrá ver los datos de los eventos de Calendar en un cursor local de Fox:

NOTA:

No puede duplicar una referencia de evento, incluso si lo borra del calendario

NOTA:

En mis aplicaciones yo uso Web Connection http://www.west-wind.com para hacer las llamadas a Internet, como no es un producto gratuito las he sustituido en este ejemplo por Microsoft.XMLHTTP

C - Descarga del proyecto

La descarga del proyecto completo y la documentación está disponible en el siguiente enlace: GCalconnect.rar (832 KB)

LEGAL DISCLAIMER

THIS SAMPLE CODE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) SUSTAINED BY YOU OR A THIRD PARTY, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ARISING IN ANY WAY OUT OF THE USE OF THIS SAMPLE CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Jose Enrique Llopis
jellopis@rocketmail.com
www.multilinkcrm.com

5 de diciembre de 2014

Cualquier dirección con StreetView !!!

Artículo original: StreetView any address !!!
http://vfpimaging.blogspot.com.br/2014/12/streetview-any-adress.html
Autor: Cesar VFPIMAGING
Traducido por: Luis María Guayán


Acabo de recibir un pedido de un usuario que le permita ver la Vista de la Calle (Google StreetView) de nuestros clientes en los formularios de clientes.

Esto es útil especialmente para los casos de entregas, lo que permite al equipo saber el aspecto de la calle, y planificar mejor las entregas de mercaderías.

He encontrado varios ejemplos, pero todos ellos necesitaba las coordenadas GPS (Global Positioning System - Sistema de Posicionamiento Global), la latitud y la longitud para hacer que funcione.

Después de hacer algunas búsquedas, he encontrado la solución en el BLOG JAYCODE DESIGN, de Jordan Clist.
"A todos nos gusta el servicio Streetview que ofrece Mapas de Google. Naturalmente, la API de Google ofrece la posibilidad de incrustar Streetview dentro de una aplicación web, si le das los datos y coordenadas correctas.
Lo qué no hace de forma natural y fácil es apuntar la cámara StreetView automáticamente a una dirección.
Esto es algo que pensé que sería fácil, pero en realidad era más difícil de lo que parece.
Aunque es bastante simple crear una vista panorámica de la calle en un punto GPS dado, lo que es más complicado es asegurarse de que la cámara StreetView esté apuntando en la dirección correcta (punto de vista, u orientación)"
Por lo que es el trabajo era un poco complicado, ya que el ejemplo original proporcionado trabajó solamente con algunas direcciones ya conocidos por Google. Eso hizo que falle en la mayoría de los casos, debido a la ortografía, o incluso una coma puesta en el lugar equivocado.

Así que usé otra API de Google Maps, la "Google Maps Directions API", que se utiliza originalmente para decirnos las rutas que podemos tomar entre 2 direcciones. Esta API funciona muy bien con las direcciones sin un buen formato, y devuelve la versión de Google de la dirección. Adapté un ejemplo de Mike Gagnon, de su artículo publicado en http://atoutfox.org "Calcular la distancia entre 2 direcciones, y mostrar el recorrido completo"

Después de conseguir esta dirección, lo paso al script  de Jordan Clist y Voila! Funciona !

Copiar y pegar el siguiente código y guardarlo en un archivo .PRG y llamado "GOOGLESTREETVIEW.PRG"

Y para probarlo, escriba en la ventana de comandos:

=GoogleStreetView("Arche du Triumph") 

o, por supuesto, pasando la dirección completa:

=GoogleStreetView("Place Charles de Gaulle, 75008, Paris, France")

Bonito no es así?



9 de julio de 2014

Mas sobre el envio de mensajes de correo electrónico desde Visual FoxPro

Existen muchas herramientas y formas de enviar mensajes de correo electrónico desde Visual FoxPro, y seguramente por ello, existen muchos artículos y códigos escritos sobre este tema. La razón de escribir mas de los mismo, es comentarles sobre mi experiencia y elección personal, de la herramienta que utilizo actualmente, la cual me satisface y cubre todas mis necesidades. Estoy convencido que lo mismo le sucede a muchos desarrolladores y espero que al terminar de leer estas líneas, esto ayude a muchos mas.

The winner is ...

Como indico arriba, esto es "mi experiencia y elección personal", obviamente no digo que sea "la mejor", ni digo tampoco "la peor", por lo tanto no entraré en comparación con otras herramientas, solo hablaré de CDO.

¿Qué es CDO?

CDO (Collaboration Data Objects) es un componente COM (Component Object Model) que simplifica la escritura de código para crear y manipular mensajes de Internet y es parte integrante (Cdosys.dll) de los sistemas operativos Windows 2000 y superiores, siendo esta la primer gran ventaja, ya que no necesitamos descargar, comprar, ni licenciar, ninguna otra herramienta extra. CDO no necesita que tengamos un servidor SMTP local, solo necesita acceso a la Web, a algún servidor SMTP que nos permita enviar los mensajes de correo electrónico.

Usando VFP

Otra ventaja adicional, es que se puede utilizar CDO con lenguajes que soporten COM y Automation, y Visual FoxPro lo soporta. A continuación un sencillo código para enviar un mensaje de correo electrónico con CDO desde VFP. Recuerde configurar correctamente el nombre del servidor SMTP, el puerto SMTP, el nombre de usuario y contraseña.
loCfg = CREATEOBJECT("CDO.Configuration")
WITH loCfg.Fields
  .Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.mail.com"
  .Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
  .Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
  .Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "user@mail.com"
  .Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "password"
  .Update
ENDWITH

loMsg = CREATEOBJECT ("CDO.Message")
WITH loMsg
  .Configuration = loCfg
  .From = "user@mail.com"
  .To = "user1@mail.com.ar"
  .Subject = "Prueba desde VFP"
  .TextBody = "Este es un mensaje de prueba con CDO desde Visual FoxPro."
  .Send()
ENDWITH

Adjuntando archivos en nuestros mensajes

Para enviar archivos adjuntos en un mensaje de correo electrónico, solo debemos llamar al método AddAttachment() del objeto Message, tantas veces como archivos deseamos adjuntar, con la ruta y el nombre del archivo como parámetros:
WITH loMsg
 .AddAttachment("C:\Imagenes\Foto1.jpg")
 .AddAttachment("C:\Imagenes\Foto2.jpg")
 .AddAttachment("C:\Imagenes\Foto3.jpg")
ENDWITH

Autenficación y cifrado SSL

Algunos servidores SMTP, necesitan autentificación y cifrado SSL para iniciar sesión. Para ello debemos configurar las propiedadades smtpauthenticate y smtpusessl del objeto Configuration con el valor .T..
WITH loCfg.Fields
  .Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = .T.
  .Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = .T.
ENDWITH

Mensajes a múltiples destinatarios

Para enviar el mensaje de correo a mas de un destinatario, solo debemos separar las direcciones de correos electrónicos, con el caracter "," (coma) en la propiedad To. También podemos agregar una copia para otro destinatario, como así también una copia oculta, configurando las propiedades Cc y Bcc respectivamente.
WITH loMsg
 .To = "user1@mail.com, user2@mail.com"
 .Cc = "user3@mail.com"
 .Bcc = "user4@mail.com"
ENDWITH
Podemos hacer que aparezca el nombre completo del remitente y/o del destinatario, anteponiendo el nombre a la dirección de correo electrónico, y encerrando ésta última entre "<" y ">"
WITH loMsg
 .From = "Jose Fox <jose@fox.org>"
 .To = "Usuario Uno <user1@mail.com>, Usuario Dos <user2@mail.com>"
ENDWITH

Mensajes con formato HTML

Con CDO muy fácilmente se puede enviar mensajes de correo electrónico con formato HTML, solamente configurando la propiedad HTMLBody con código HTML válido, en lugar de configurar la propiedad TextBody que es válida para los mensajes con texto sin formato.
WITH loMsg
  .HTMLBody = "<p>Este es un texto HTML con <b>negritas</b> o con <i>cursivas</i>.</p>"
ENDWITH
Esta propiedad la podemos configurar con el contenido de un archivo HTML con la función FILETOSTR() como se muestra a continuación.
WITH loMsg
  .HTMLBody = FILETOSTR("C:\Archivo.htm")
ENDWITH
Mas adelante veremos otra manera de dar formato HTML a un mensaje de correo electrónico, a partir de un archivo HTML ubicado en el disco local o la Web.


14 de marzo de 2013

Buscar direcciones postales en Google Maps con VFP (Actualizada API)

Esta es una actualización de la utilidad que ya publiqué hace tiempo, con motivo del cambio de la API de Google Maps.

Además, ajusto el formulario a la zona visible de la pantalla, y una vez localizada la dirección, se muestra el mapa con un globo de información en el que se incluyen las coordenadas correspondientes a la dirección buscada.

Se puede descargar de:

https://sites.google.com/site/archivosvfp/archivos/gmaps.zip

Un saludo.

José Antonio Blasco, Zaragoza, España

12 de enero de 2013

Saber si una URL existe

Esta función, sirve para saber si una URL está en funcionamiento o existe en el momento de su ejecución:

?ExisteURL("http://www.webservicex.net/globalweather.asmx?WSDL")
?ExisteURL("http://www.noexiste.net/")

*-----------------------------------------------------
FUNCTION ExisteURL(tcURL)
*-----------------------------------------------------
* Esta función se utiliza para saber si una URL existe 
* o está funcionando en este momento, utiliza cURL.exe
* para realizarlo (http://curl.haxx.se/)
*
* Recibe de parámetro la url a validar y regresa un 
* lógico que indica si se encontró la URL o no.
*-----------------------------------------------------

 IF !FILE("curl.exe")
  * --- Debes tener el cURL y sus dlls en el directorio de trabajo ---
  RETURN .F.
 ENDIF 

 lRet = .F.
 * --- Se declara una variable que contendría el contenido de la página web o wsdl --- 
 sFile = ADDBS(SYS(2023)) + SYS(2015)+".txt"
 * --- Se prepara un bat que invoca al cURL ---
 TEXT TO xBAT NOSHOW ADDITIVE TEXTMERGE PRETEXT 7
 echo off
 cd <<SYS(5)+CURDIR()>>
 curl.exe <<ALLTRIM(tcURL)>> >> <<sFile>>
 ENDTEXT
 * --- Se almacena en un bat ---
 uidBat = ADDBS(SYS(2023)) + SYS(2015) + [.bat]
 STRTOFILE(xBAT,uidBat)
 * --- Ejecutamos el bat ---
 oShell = createobject("WScript.Shell") 
 oShell.Run(uidBat,0,.T.) 
 RELEASE oShell 
 DELETE FILE (uidBat) 
 * --- Si no creo el archivo resultado ocurrió un error con cURL ---
 IF !FILE(sFile)
  lRet = .F.
 ELSE
  * --- Si existe el archivo y su longitud es diferente de 0, la url existe! ---
  lcFile = FILETOSTR(sFile)
  lRet = (LEN(lcFile) > 0 )
  DELETE FILE (sFile)
 ENDIF 
 RETURN lRet 
ENDFUNC 
*-----------------------------------------------------

Hasta la próxima!!

Baltazar Moreno
Guadalajara, Jalisco, México

8 de septiembre de 2012

Chequear el estado de un sitio de Internet

En una consulta en el foro de la Comunidad de Visual FoxPro en Español, Daniel preguntaba cómo chequear si un sitio específico de Internet daba error (por ejemplo 404).

Esto podría hacerse usando un request HTTP con Microsoft.XMLHTTP, a continuación un ejemplo simple:
#define HTTP_OK        200
CLEAR 
LOCAL oIE as InternetExplorer.Application

sUrl = "http://www.moogle.com.ar"

nStatus = CheckHttpConnection(sUrl)
IF nStatus==HTTP_OK
    oIE = CREATEOBJECT ("InternetExplorer.Application")
    WITH oIE
        .Visible = .T.
        .Navigate (sUrl)
        DO WHILE .Busy
            DOEVENTS 
        ENDDO

        * .Quit()
    ENDWITH
ELSE 
    ? sUrl, "error", nStatus
ENDIF 
RETURN 

FUNCTION CheckHttpConnection(sUrl)
LOCAL oHTTP AS Microsoft.XMLHTTP

oHTTP = CREATEOBJECT("Microsoft.XMLHTTP")
oHTTP.Open("GET", sUrl, .F.)
oHTTP.Send()
RETURN oHTTP.Status

Espero les sea de utilidad.

Saludos,

Mario Lopez

15 de abril de 2012

Función Traductor Ingles-Español con Google Translator

Función que traduce texto del ingles al español recibe como parámetro el texto en ingles y lo devuelve en español, utilizando para esto el motor de traducción de Google.

Esta funcion utiliza automatizacion de un objeto IE (Internet Explorer) y extrae el codigo HTML y depura el texto traducido.
? TraducirIE("Nothing runs like a fox")

FUNCTION TraducirIE
  LPARAMETERS ATRA
  LOCAL cUrl, Texto, oIE, cResul, oDoc,XPI,XPF,TRA,X
  IF TYPE("oIE")#'O'
    oIE = CREATEOBJECT("internetexplorer.application")
  ENDIF
  oIE.Navigate2("about:blank")
  cUrl=LOWER('http://translate.google.com.mx/?hl=es&tab=TT#en|es|'+ALLTRIM(ATRA))
  oIE.navigate2(cUrl)
  Texto ='Traduciendo.'
  DO WHILE oIE.busy OR oIE.readystate!=4
    WAIT WINDOW Texto TIMEOUT 0.1
    Texto=Texto+'.'
    IF OCCURS('.',Texto)>3
      Texto ='Traduciendo.'
    ENDIF
  ENDDO
  WAIT WINDOW 'Terminado' TIMEOUT 1
  oDoc = oIE.DOCUMENT
  Texto = TYPE('oDoc.body')
  cResul = oDoc.body.innerHTML()
  oDoc = .NULL.
  RELEASE oDoc
  oIE.QUIT
  RELEASE oIE
  XPI = AT('',cResul)+32
  cResul = SUBSTR(cResul,XPI)
  XPF = AT('',cResul)-1
  cResul = SUBSTR(cResul,1,XPF)
  X=OCCURS('<',cResul)
  TRA=''
  DO WHILE X>0
    XPI=0
    XPF=0
    XPI=AT('<',cResul)-1
    XPF=AT('>',cResul)+1
    TRA=TRA+SUBSTR(cResul,1,XPI)
    cResul=SUBSTR(cResul,XPF)
    X=OCCURS('<',cResul)
  ENDDO
  TRA=TRA+cResul
  RETURN TRA
ENDFUNC
Erick J. Mireles Riojas

29 de marzo de 2012

Clase que encapsula funciones de javascript para URLs

Esta clase encapsula las funciones de javascript necesarias para trabajar con Urls.

Utiliza el msscript control. Fácilmente puedes extenderla para incluir cualquier otra funcion de javascript que necesites.

*----------------------------------------------------------------
* Clase: UrlEncDec
* Autor: Marco Plaza, 28/03/2012
* Eencapsula las siguientes funciones de javascript via MsScriptcontrol:
* Escape/Unescape
* EncodeUriComponent/DecodeUriComponent
* EncodeUri/DecodeUri
*
* referencia de las funciones:http://msdn.microsoft.com/en-us/library/ht8a077w%28v=vs.94%29.aspx
*
* fácilmente extensible a otras funciones de javascript
*
*-----------------------------------------------------------------
* uso:
* o = create('urlEncDec')
* ? o.decodeuri('http://msdn.microsoft.com/en-us/library/ht8a077w%28v=vs.94%29.aspx')
* ? o.oResult
*
*------------------------------------------------------------------


*************************************
DEFINE CLASS URLENCDEC AS CUSTOM

  oJs = ''
  oResult=''

  *---------------------------
  PROCEDURE INIT

    WITH THIS
      .oJs=CREATEOBJECT('MSScriptcontrol.scriptcontrol.1')
      .oResult=CREATEOBJECT('EMPTY')
      ADDPROPERTY(.oResult,'ResultadoJs')
      .oJs.LANGUAGE =[JavaScript]
      .oJs.ADDOBJECT('oResult',.oResult)

    ENDWITH
  ENDPROC

  *------------------------------------------
  FUNCTION ESCAPE(lcCadena AS STRING ) AS STRING
    RETURN THIS.ejecutar('escape',lcCadena)
  ENDFUNC

  *------------------------------------------
  FUNCTION encodeURI(lcCadena AS STRING ) AS STRING
    RETURN THIS.ejecutar('encodeURI',lcCadena)
  ENDFUNC

  *--------------------------------------------
  FUNCTION decodeURIComponent(lcCadena AS STRING ) AS STRING
    RETURN THIS.ejecutar('decodeURIComponent',lcCadena)
  ENDFUNC

  *------------------------------------------
  FUNCTION encodeURIComponent(lcCadena AS STRING ) AS STRING
    RETURN THIS.ejecutar('encodeURIComponent',lcCadena)
  ENDFUNC

  *------------------------------------------
  FUNCTION unescape(lcCadena AS STRING ) AS STRING
    RETURN THIS.ejecutar('unescape',lcCadena)
  ENDFUNC

  *------------------------------------------
  FUNCTION decodeURI(lcCadena AS STRING ) AS STRING
    RETURN THIS.ejecutar('decodeURI',lcCadena)
  ENDFUNC

  *------------------------------------------
  FUNCTION ejecutar(lcComando,lcCadena AS STRING ) AS STRING
    THIS.oJs.ExecuteStatement( [oResult.ResultadoJs=]+lcComando+[(']+lcCadena+[')] )
    RETURN THIS.oResult.resultadoJs
  ENDFUNC

ENDDEFINE
*---------------------------------
Marco Plaza

22 de noviembre de 2011

Consumir cualquier Web Service con Visual FoxPro

Este artículo es para mostrarles como podemos invocar cualquier Web Services desde Visual FoxPro.

Primero quisiera comentar, que NO soy un experto en ésto de los WEB services, pero he tomado algo de experiencia en el uso de los mismos. En éste artículo trataré de explicarlo con palabras simples.

Es importante leer al respecto, siempre es sencillo encontrar información en Wikipedia.

Vamos comenzando por la lógica de "ejecución" de un web service:

1. Creamos la petición de ejecución de una función del Web Service, ésto se llama XML REQUEST.
2. Enviamos el REQUEST al web service, la cual es una URL que termina con wsdl.
3. Obtenemos un XML RESPONSE, que es el resultado de haber invocado la función específica del WS.

Vamos por partes:

Para saber como es el REQUEST, utilizo una aplicación Open Source llamada soapUI, la cual pueden descargar desde: http://www.soapui.org/

Lo descargamos, al abrir el soapUI vemos ésto:

Image Hosted by ImageShack.us

La URL del web service es: http://www.webservicex.net/globalweather.asmx?WSDL

Image Hosted by ImageShack.us

Con ésto, soapUI interperta las funciones que existen en ése WebService, y nos crea un ejemplo de REQUEST por cada una de ellas, en éste caso observaremos como el REQUEST de la función GetWeather:

Image Hosted by ImageShack.us

Hasta aquí, tenemos cubierto el paso 1 de la ejecución de un Web Service, el dos es simple.. damos clic en el botón de "PLAY" y se cumple el segundo paso, al mismo tiempo que obtenemos el XML RESPONSE:

13 de agosto de 2011

¿Necesitas rellenar formularios WEB desde VFP?

Bueno este ultimo aporte se comprueba la ejecución de código PHP desde VFP como también el paso de parámetros con el método POST que hace posible poder reemplazar o emular un formulario web siempre y cuando este no posea el famoso captcha que son esas imágenes deformadas que debemos tipear en un campo, bueno esta método o mejor dicho algo parecido es el que usan esos robot form o roboform o como sean que les llamen a esos programas que rellenan un par de miles de veces el mismo formulario con el objetivo de colapsar el servidor.

Mas sobre formularios, para poder rellenar un formulario WEB desde VFP debes abrir este con el navegador y verificar el código que te trae, y busca los nombres de campos que quieres rellenar y que método generalmente es algún PHP) es el que procesa esta información, posteriormente armas tu PRG en VFP para el envío de estas variables y su contenido.

¿Que otras cosas podríamos hacer?Por ejemplo:
  • lanzar un backup de mysql desde un terminal VFP
  • lanzar cualquier comando y/o programa siempre y cuando tenga acceso en el servidor
  • analizar la comunicación entre el servidor y el cliente web
A pesar de que existen herramientas mejores como ssh para ejecuciones remotas y otras herramientas de análisis de trafico pero en fin.

Se muestran a continuación 4 segmentos de código:
  1. el código FOXPRO que hace las llamadas y recibe los resultados.
  2. código PHP que recibe los parámetros del VFP y ejecuta instrucciones y devuelve el control al VFP
  3. el formulario HTML que estamos emulando desde el VFP
  4. la base de datos MySql para hacer la prueba
Requerimientos
  • WIN7 no probé versiones inferiores
  • VFP8.0 o superior debería funcionar desde la 6.0 pero no lo probé
  • Yo use fedora 10 con apache 2.2.10, PHP 5.2.6 y MySql 5.0.67 pero alternativamente podríamos utilizar xampp (win32 portable), o wamp o similares que se ejecutan en la misma maquina
*//--------------------------------------------------------------------------------------------
*//............................................................................................
*//......Fecha:   18-08-2011.............................................................
*//......Autor:   OMAR DUILIO ROJAS FORNERON.............................................
*//......Localidad:  Encarnación-Paraguay...orodrf@gmail.com................................
*//......Funcion:  Ejecuta un metodo php y envia ciertos parametros por metodo POST.......
*//......    Es basicamente un metodo que ejecuta una rutina php en un servidor y...
*//......    que envia parametros, para que me podia servir esto? bueno supongamos..
*//......    que hay algun servidor que tiene un formulario y que deseas rellenar...
*//......    desde el VFP (como los formsbot o roboform) siempre y cuando no tenga..
*//......    el famoso captcha que es una imagen distorcionada de letras/numeros....
*//......    que debemos ingresar tambien en un campo...............................
*//......    Requisitos:
*//......    Tener un servidor web con soporte php y mysql ((como apache)) y un ....
*//......    terminal con posibilidad de correr VFP................................. 
*//......    server.................................................................
*//......    Existen excelentes herramientas como WAMP, XAMPP (portable) y otros en.
*//......    la que se ejecutan en la misma maquina sin necesidad de un servidor....
*//......    .......................................................................
*//......Compatib.:  Ha sido desarrollado y probado con VFP8.0 y en win7 con firefox5.y.....
*//......    como servidor use un fedora10 c/apache 2.2.10, php5.2.6 y mysql 5.0.67.
*//............................................................................................
*//--------------------------------------------------------------------------------------------
LOCAL oHttp,vhost,dbase,vuser,vpass


*// estas variables son utilizadas en php
* $dbhost=$_POST["vhost"];   /*HOSTNAME*/
* $dbasen=$_POST["dbase"];   /*DATABASE*/
* $dbuser=$_POST["vuser"];   /*USERNAME*/
* $dbpass=$_POST["vpass"];   /*USERPASS*/
xhost ="192.168.1.3"     &&nombre del servidor obs: usen IP los nombres no siempre funcionan
xbase ="xdbase"      &&nombre de la base de datos mysql
xuser ="admin"      &&nombre del usuario
xpass ="admin"      &&contraseña

&&parametros que pasaremos a php
xparameters="&vhost="+xhost+;
   "&dbase="+xbase+;
   "&vuser="+xuser+;
   "&vpass="+xpass

oHttp = CreateObject( "MSXML2.XMLHTTP" )
oHttp.Open( "post", "http://"+xhost+"/php/x.php", .f.)
oHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
oHttp.Send( xparameters )

* muestra los estados de respuesta
? oHttp.Status
? oHttp.StatusText
? "Response length:", LEN(oHttp.StatusText)
? oHttp.GetAllResponseHeaders()
? oHttp.GetResponseHeader("content-type")

* envia codigos html devueltos por php a un archivo de texto y al navegador
DELETE FILE ([C:\TEMP\RESPONSE.HTML])
STRTOFILE( oHttp.ResponseText, [C:\TEMP\RESPONSE.HTML] )
MODI FILE ([C:\TEMP\RESPONSE.HTML]) NOWAIT
RELEASE oHTTP
RUN [EXPLORER C:\TEMP\RESPONSE.HTML] /N
RETURN
*// FIN DEL PROGRAMA VFP
*//----------------------------------------------------------------------------------------------------------

* codigo php llamado desde VFP
 /*
 //--------------------------------------------------------------------------------------------
 //............................................................................................
 //......Fecha:   18-08-2011............................................................
 //......Autor:   OMAR DUILIO ROJAS FORNERON............................................
 //......Localidad:  Encarnación-Paraguay...orodrf@gmail.com...............................
 //......Funcion:  Metodo de ejemplo, para ser llamado desde VFP por metodo POST.........
 //......    lo unico que hace es conectarse al servidor mysql y mostrar una tabla.
 //......    llamada producto de la base de datos xbase............................
 //--------------------------------------------------------------------------------------------
 */
 
 echo "<html>";
 echo "<head>";
 echo "<title>Codigo php ejecutado desde VFP, by ODRF++</title>";
 echo "</head>";
 echo "<body>";
 
 
 $dbhost=$_POST["vhost"];   /*HOSTNAME*/
 $dbasen=$_POST["dbase"];   /*DATABASE*/
 $dbuser=$_POST["vuser"];   /*USERNAME*/
 $dbpass=$_POST["vpass"];   /*USERPASS*/
 
 echo "debug: [";
 echo $dbhost,$dbasen,$dbuser,$dbpass;
 print( "]\n" );
 
 // connect to mysql
 if ( !($link=mysql_connect( $dbhost, $dbuser, $dbpass)) ) {
  echo "Error conectando a MYSQL";
  exit();
 }
 if (!mysql_select_db( $dbasen, $link)) {
  echo "Error seleccionando la base de datos";
  exit();
 }
 
 // consultamos la base de datos
 $res=mysql_query( "select * from producto limit 40", $link );
 if (!$res) {
  echo "la consulta fallo".mysql_error();
  exit();
 }
 
 // traemos registros seleccionados
 echo "<table summary=summary frame=box>";
 while ( $frec=mysql_fetch_array( $res ) ) {
  echo "<tr>";
  echo "<td>",$frec["codigo"],"</td>";
  echo "<td>",$frec["nombre"],"</td>";
  echo "<td>",$frec["codiva"],"</td>";
  echo "</tr>";
 }
 echo "</table>";
 mysql_free_result($res);
 
 
 // disconnect
 mysql_close( $link );
 echo "</body>";
 echo "</html>";

*// el siguiente es codigo del formulario que suplantamos o emulamos desde vfp podria nombrarse como INDEX.HTML
*<body>
* <h1>*** Datos de productos ***, testing php, mysql and httpd</h1>
* <form method="post" action="x.php">
* <fieldset>
*  <legend>Login de usuario:</legend>
*  <table>
*   <tr>
*    <td><label>Hostname</label></td>
*    <td><input name="vhost" type="text"  value="" size="20" maxlength="50"></td>
*   </tr>
*   <tr>
*    <td><label>Database</label></td>
*    <td><input name="dbase" type="text" value="" size="20" maxlength="50"></td>
*   </tr>
*   <tr>
*    <td><label>User</label></td>
*    <td><input name="vuser" type="text"  value="" size="20" maxlength="50"></td>
*   </tr>
*   <tr>
*    <td><label>Pass</label></td>
*    <td><input name="vpass" type="text"  value="" size="20" maxlength="50"></td>
*   </tr>
*  </table>
*  <hr width="100%">
*  <input name="send" type="submit" value="Enviar">
*  <input name="defa" type="reset"  value="Descartar">
*  <br>
* </fieldset>
*
*</body>
*</html>
*//----------------------------------------------------------------------------------------------------------
<!--pagebreak-->

*// y por ultimo la base de datos mysql guardarlo en algun lugar accesible al servidor mysql como x.sql
*//    para importarlo al mysql ejecutar 
*// WIN-SHELL  mysql -u root -p < C:\TEMP\x.sql
*// LINUX-SHELL  mysql -u root -p < /home/x.sql
*//  ambos deben tener la direccion exacta donde guardaron este fichero
-- MySQL dump 10.13  Distrib 5.1.41, for Win32 (ia32)
--
-- Host: localhost    Database: xdbase
-- ------------------------------------------------------
-- Server version 5.1.41

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table 'producto'
--
CREATE DATABASE xdbase IF NOT EXISTS;
USE xdbase;


DROP TABLE IF EXISTS 'producto';
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE 'producto' (
  'CODIGO' char(15) NOT NULL,
  'NOMBRE' char(55) NOT NULL,
  'CODIVA' char(3) NOT NULL,
  'CODMED' char(5) NOT NULL,
  'XTIPO' char(1) NOT NULL,
  'XGRUPO' char(3) NOT NULL,
  'CODBAR' char(20) NOT NULL,
  'foto' mediumblob NOT NULL,
  PRIMARY KEY ('CODIGO','NOMBRE','CODBAR')
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table 'producto'
--

LOCK TABLES 'producto' WRITE;
/*!40000 ALTER TABLE 'producto' DISABLE KEYS */;
INSERT INTO 'producto' VALUES ('0002GOL','ESPEJO','10G','1  1','1','1','','ABCDEFG'),
('00093','RETEN','10G','1  1','1','1','','ABCDEFG'),
('00101004','SOPORTE','10G','1  1','1','1','','ABCDEFG'),
('00105180MB','GOMA RADIADOR INFERIOR','00E','1  1','1','1','','ABCDEFG'),
('001120','LIMPIA PARABRISAS','10G','1  1','1','2','','ABCDEFG'));º
/*!40000 ALTER TABLE 'producto' ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2011-07-29 16:50:16
*----------------------------------------------------------------------------------------------

6 de enero de 2011

Trabajar con GPS y Google Maps

Estuve trabajando en un proyecto sobre GPS que me gustaría compartir con ustedes, y surge la pregunta como trabajar con GPS, Google Maps y VFP9.

Por supuesto la respuesta es mas fácil de lo que esperaba, primero configurar el GPS, cada GPS es diferente lo que hay que hacer es indicarle que envíe datos a través GPRS a un IP fija, abrir un puerto en el "receptor" y lograr que escuche ese puerto.

Bueno aquí la primer solución, usando iFox, excelente herramienta, por supuesto que pueden usar WinSock pero me ha resultado mas complicado.

Nota: Pueden descargar iFox desde este enlace

Aquí el código con IFOX.
*** abrir puerto para escuchar ***
THIS.iFoxSockets = CREATEOBJECT("iFox.Sockets")
THIS.Socket = 0

*** indico el puerto que quiero escuchar en el gps debe indicar enviar datos a ese puerto
THIS.ListenSocket = THIS.iFoxSockets.LISTEN(1123)
IF THIS.ListenSocket <> 0
  * THIS.txtServer.VALUE = "Esperando conexión ..." && pueden manejar con un text box
  UPDATE gp_gps SET conectado = "Esperando..." && o pueden almacenar en una tabla
  THIS.tmrRead.INTERVAL = 400 && un timer que lee el puerto cada cierto tiempo
ELSE
  UPDATE gp_gps SET conectado = "Error"
ENDIF

*** codigo del timer ***
LOCAL nSocket, cRes
IF THISFORM.ListenSocket <> 0
  nSocket = THISFORM.iFoxSockets.AcceptConnections(THISFORM.ListenSocket)
  IF nSocket <> 0
    * Aunque es posible en este ejemplo no aceptamos mas de una conexión
    THISFORM.iFoxSockets.CLOSE(THISFORM.ListenSocket)
    THISFORM.ListenSocket = 0
    THISFORM.Socket = nSocket
    UPDATE gp_gps SET conectado="Conectado " && + THISFORM.iFoxSockets.GetRemoteHost(nSocket) + ;
      " (" + THISFORM.iFoxSockets.GetRemoteHostIP(nSocket) + " )"
  ENDIF
ENDIF
IF THISFORM.Socket <> 0
  cRes = THISFORM.iFoxSockets.READ(THISFORM.Socket)
  IF !EMPTY(cRes)
    THISFORM.edtRespuesta.VALUE = cRes + THISFORM.edtRespuesta.VALUE
    THISFORM.lbldatos.CAPTION = "Ultimos datos:" + TTOC(DATETIME())
    THISFORM.edit1.VALUE = mc_splitgps(cres,puerto)
    ***
    *** el GPS envia todo en una sola cadena lo que hago aca es separar los datos, 
    *** el GPS envia algo asi:
    *** (013666666666BP05000013666666666110106A2309.0321S06419.0954W009.811302384.57000000000L000860AF)
    *** el mc_split separa los datos en latitud longitud, fecha, hora velocidad
    *** todo es cuestion de observar un poco, por supuesto en cada gps es diferente pero 
    *** en este caso en particular queda algo asi:
    *** 013666666666BP05000013666666666
    *** Fecha 110106
    *** A23
    *** Latitud 09.0321S
    *** Longitud 06419.0954W
    *** Velocidad 009.8
    *** Hora 113023
    *** 84.57000000000L000860AF
    ***
  ELSE
    IF THISFORM.iFoxSockets.SocketClosed
      THISFORM.iFoxSockets.CLOSE(THISFORM.Socket)
      THISFORM.Socket = 0
      UPDATE gp_gps SET conectado = "Finalizado..."
    ENDIF
  ENDIF
ENDIF
***
Bueno para que lo puedan ir digiriendo los dejo un rato, en la siguiente entrega, ver los datos en Google Maps.

Bueno hasta aquí la lectura del puerto.

Saludos y hasta la próxima.

Carlos [Caremi66]

14 de marzo de 2009

Envío de correo electrónico por el servidor SMTP de Windows Live Hotmail

Actualmente Windows Live Hotmail habilitó el acceso POP3 y SMTP en todas las cuentas de correos de Hotmail.


Está publicado el artículo Envío de correo electrónico por el servidor SMTP de Gmail del cual partiremos y solo cambiaremos las configuraciones necesarias que son:
  • Nombre de usuario: El nombre de tu Windows Live ID, (Ejemplo: usuario@hotmail.com)
  • Contraseña: La contraseña de tu cuenta Windows Live
  • Servidor SMTP: smtp.live.com
  • Puerto SMTP: 25
  • Autenticación requerida: Si
  • Cifrado TLS/SSL requerido: Si
Para los que quieren saber los datos del servidor POP3 de Windows Live Mail, no necesarios para este artículo:
  • Servidor POP3: pop3.live.com
  • Puerto POP3: 995
  • POP3 SSL requerido: Si
El siguiente es el código modificado que nos permite el envió de correo electrónico a través del servidor SMTP de Windows Live Hotmail.
TRY
  LOCAL lcSchema, loConfig, loMsg, loError, lcErr
  lcErr = ""
  lcSchema = "http://schemas.microsoft.com/cdo/configuration/"
  loConfig = CREATEOBJECT("CDO.Configuration")
  WITH loConfig.FIELDS
    .ITEM(lcSchema + "smtpserver") = "smtp.live.com"
    .ITEM(lcSchema + "smtpserverport") = 25
    .ITEM(lcSchema + "sendusing") = 2
    .ITEM(lcSchema + "smtpauthenticate") = .T. 
    .ITEM(lcSchema + "smtpusessl") = .T.
    .ITEM(lcSchema + "sendusername") = "miusuario@hotmail.com"
    .ITEM(lcSchema + "sendpassword") = "micontrasenia"
    .UPDATE
  ENDWITH
  loMsg = CREATEOBJECT ("CDO.Message")
  WITH loMsg
    .Configuration = loConfig
    .FROM = "Usuario Hotmail <miusuario@hotmail.com>"
    .TO = "usuario@gmail.com"
    .Subject = "Prueba desde Windows Live Hotmail"
    .TextBody = "Este es un mensaje de prueba con CDO con " + ;
      "autenticación y cifrado SSL desde Windows Live Hotmail."
    .Send()
  ENDWITH
CATCH TO loError
  lcErr = [Error: ] + STR(loError.ERRORNO) + CHR(13) + ;
    [Linea: ] + STR(loError.LINENO) + CHR(13) + ;
    [Mensaje: ] + loError.MESSAGE
FINALLY
  RELEASE loConfig, loMsg
  STORE .NULL. TO loConfig, loMsg
  IF EMPTY(lcErr)
    MESSAGEBOX("El mensaje se envió con éxito", 64, "Aviso")
  ELSE
    MESSAGEBOX(lcErr, 16 , "Error")
  ENDIF
ENDTRY
Para conocer muchas mas opciones que nos brinda CDO para el envío de correos electrónicos desde VFP, podemos ver el siguiente artículo: Mas sobre el envio de mensajes de correo electrónico desde Visual FoxPro

Hasta la próxima,

Luis María

19 de octubre de 2008

ActiveVFP Liberado

Home of ActiveVFP - Foxpro on the World Wide Web

Ya esta para descargar gratuitamente este excelente framework para desarrollar fácilmente aplicaciones Web con Visual FoxPro.

Pueden descargar libremente la última versión de:

http://activevfp.codeplex.com/releases/view/53030

ActiveVFP es un proyecto de código abierto completamente gratuito que permite crear fácilmente aplicaciones Web con Visual FoxPro, ya que provee un framework fácil de usar para crear DLL multihilos con código VFP puro.

La página de este proyecto es:

http://activevfp.codeplex.com

29 de febrero de 2008

Actualización de VFPCompression - Corrección de rutas relativas

Artículo original: VFPCompression Update - Relative Path Fixes
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,41e05bd6-f856-4837-93f0-b295ff1d2bb6.aspx
Autor: Craig Boyd
Traducido por: Luis María Guayán

Actualización de VFPCompression

Esta actualización contiene la corrección a los problemas sobre las rutas relativas al comprimir carpetas y archivos que no estan relacionados unos a otros, o con el archivo zip que se esta creando. Antes de esta versión los desarrolladores tenían que pasar .T. para el parámetro lIgnorePath cuando llamaban a las funciones con el fin de crear el zip con los archivos y carpetas que no eran relativos. Este no era una solucion aceptable cuando la ruta relativa para subcarpetas sería ignorada luego también. Este ha sido un escollo para algunos que implementaron este FLL, entonces me alegro que finalmente este corregido y lamento que esto me tomó demasiado tiempo para corregir este problema.

Un agradecimiento muy especial para Sergey Berezniker y Elliot Selick. Ellos de manera independiente me informaron sobre el tema de las rutas relativas y ayudaron con las pruebas una vez que yo me había ocupado del tema.

El código de ejemplo para usar la biblioteca y la documentación de biblioteca puede ser encontrado en mi artículo anterior. Aquí está el enlace habitual para la descarga...

Actualización de VFP Compression:

Descarga de VFPCompression FLL (35 KB aprox.)

25 de febrero de 2008

Actualización de aplicación VFP de manera sencilla

Artículo original: VFP Application Updating Made Simple
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,06bb82c3-9e4c-4048-9b3b-87cc8579eb59.aspx
Autor: Craig Boyd
Traducido por: Luis María Guayán

Corrección del 16/02/2008: Gracias a Dave Crozier por encontrar un error en el código (vea los comentarios más abajo). Un nuevo archivo .Zip de AppUpdate.zip se ha subido al servidor. Si ya ha descargado el .Zip, puede descargarlo de nuevo o hacer los cambios que se describen en los comentarios de este artículo.

Actualizando aplicaciones Visual FoxPro

Una de las cosas que he tratado de hacer en mi artículo sobre ClickOnce, era proporcionar a los desarrolladores Visual FoxPro, la comprensión de una de las tantas soluciones disponibles para la actualización de las aplicaciones una vez que ya se han distribuido. Aunque ClickOnce es muy poderoso, hay momentos en que una solución mas de cosecha propia, puede ser más apropiada. La actualización de aplicaciones, con aplicaciones que los desarrolladores han escrito y/o poseen el código fuente, también pueden ofrecer un grado de flexibilidad y poder que sólo no se encuentra disponible en soluciones como ClickOnce, ni en el servicio de actualización de InstallShield. Así que en esta entrada del blog, presentaré una aplicación que he creado usando Visual FoxPro SP2 que se puede utilizar para actualizar prácticamente cualquier aplicación Visual FoxPro.

AppUpdate.exe AppUpdate.exe

AppUpdate.exe es lo que se conoce como un 'cargador' (loader). Esto significa básicamente que es responsable de la comprobación de las actualizaciones, la instalación en caso de que estas existan, y luego la ejecución de la aplicación que se actualizó. Basándose en gran medida en la utilización de las nuevas vfpcompression.fll y vfpconnection.fll, AppUpdate.exe está diseñada para permitir a los desarrolladores de Visual FoxPro, añadir rápida y fácilmente, la capacidad de actualización de sus aplicaciones. Aquí hay un par de capturas de pantalla de AppUpdate ejecutándose...





Si bien estoy incluyendo el código fuente del proyecto AppUpdate, este no es necesario para modificar AppUpdate con el fin de configurarlo para actualizar una aplicación particular. AppUpdate obtiene su información del archivo appupdate.ini que proporciona una gran variedad de secciones y de entradas que le permitirá especificar qué aplicación se supone que la carga, cuando comprobar si hay actualizaciones, la manera de cargar la aplicación, lo que se muestra al usuario, donde copia localmente las actualizaciones con el fin de que la misma actualización sólo debe descargarse una vez en un entorno multiusuario, cuando mostrar las actualizaciones, que código VFP se ejecutará una vez que la actualización se ha instalado, etc. Así que, como desarrollador sólo necesita modificar el archivo ini para adaptar AppUpdate, con su conjunto de aplicaciones VFP.

En la descarga ...

En la descarga he incluido un ejemplo totalmente funcional de AppUpdate, junto al código fuente. He incluido sampleapp.exe que es la versión 1.0 y he puesto una actualización para este (la versión 2.0) en el servidor de SweetPotato Software para este fin, por lo que puede realmente probar AppUpdate y ver cómo funciona directamente del servidor. He incluido muchos comentarios en el archivo appupdate.ini que deseara examinar y un par de archivos ReadMe.txt. Simplemente descargue el archivo zip de abajo, extráigalo en algún lugar, lea rápidamente el archivo ini y el archivo ReadMe.txt incluidos, y luego ejecute AppUpdate.exe. Cuando comience a querer crear sus propias actualizaciones, puede echar un vistazo a la carpeta ServerUpdate que he incluido en la descarga, y contiene los archivos que están en el servidor de SweetPotato Software que está actualizando sampleapp.exe provisto en la descarga.

Quiero saber lo que piensas

Si prueba AppUpdate, me encantaría saber lo que piensas de él. Si tiene alguna sugerencia para mejorar o encuentra algún error, no dudes en dejar un comentario y yo responderé.
Hasta la próxima… Visual FoxPro Rocks!

Descarga del Proyecto AppUpdate y Ejemplo (922 KB aprox.)


Comentarios:

Craig,
Hay un problema en el proyecto ApplicationUpdate. En el formulario UpdateMessage, en el método downloadupdate() hay dos líneas mal:
* Lineas antiguas 
* m.lcUpdateFile = "File://" + m.lcUpdateFile 
* m.llReturn = FileGet(this.updateFile, m.lcUpdateURL,"m.goThisForm.ProgressHandler()") 
* Nueva linea 
This.UpdateURL = "File://" + This.UpdateURL m.llReturn = ;
  FileGet(this.updateurl, m.lcUpdateFile, "m.goThisForm.ProgressHandler()") 
Saludos,
Dave Crozier

Gran captura Dave!!
He subido un archivo .Zip corregido. El código en el método downloadupdate() fue modificado por el siguiente ...
OTHERWISE 
  IF UPPER(LEFT(This.UpdateURL, 7)) != "FILE://" 
    This.UpdateURL = "File://" + This.UpdateURL 
  ENDIF 
  m.llReturn = FileGet(This.UpdateURL, m.lcUpdateFile, "m.goThisForm.ProgressHandler()") 
ENDCASE 
Craig Boyd

22 de febrero de 2008

Cargando/descargando en VFP de manera sencilla

Artículo original: VFP Uploading/Downloading Made Simple
http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,e7cccb45-7d99-4c35-85bc-e123c81c8794.aspx
Autor: Craig Boyd
Traducido por: Luis María Guayán

Cargando, descargando y mas

Siempre he querido algunos comandos en lenguaje Visual FoxPro que realizaran cargas y descargas de archivos de sitios Web, de sitios FTP u otra ubicación, realmente fácil. Ah, también he querido un indicador de progreso para estas cargas y descargas que sea fácil de implementar. Habiéndome enredado con varias librerías de clases, y componentes de terceros por algunos años, decidí que era tiempo de escribir una FLL que haga lo que yo quería, entonces cree vfpconnection.fll.

VFP Connection FLL

Internamente vfpconnection.fll está utilizando la biblioteca LibCurl (aquí esta su licencia), por lo que hay toneladas de otras características que puedo, y podría, agregar como esto avance. Por ahora proveeré a los desarrolladores VFP con lo que creo que es mas fácil y sencillo, funciones de carga y descarga de archivos en Visual FoxPro (FTPGet, HTTPGet, FileGet, FTPPut, HTTPPut, FilePut, FTPToStr, y HTTPToStr). También se ofrece la posibilidad de descargar archivos de FTP y HTTP en la memoria (similar a la función FileToStr) que evita la escritura y lectura de disco, y puede resultar muy conveniente cuando un desarrollador sólo quiere leer algo de un servidor. Y ofrece una vía sumamente fácil de implementar un indicador de progreso a través de una llamada a una función o método.

Lo que viene ...

He creado un ejemplo bien bonito de lo útil de tener una FLL como ésta. En mi próxima entrada de mi blog, presentaré una aplicación VFP que he creado llamada AppUpdate, con lo que usted será capaz de usar Cargas/Descargas para sus aplicaciones Visual FoxPro como muy avanzado (aunque sea simple). Un agradecimiento especial a un cliente mío por permitirme salir antes de tiempo y compartir con mis lectores. ¡Pase lo que pase, hasta próxima … Visual FoxPro Rocks!

Aquí está el enlace de descarga acostumbrado, algunos ejemplos de código para usar vfpconnection.fll, y la documentación de la librería:
Descarga de VFPConnection FLL (90 KB aprox.)

Código de ejemplo de VFPConnection:

SET LIBRARY TO (LOCFILE("vfpconnection.fll","FLL"))
? FTPGet("FTP://myusername:mypassword@mysite.com/mydir/myfile.zip", "C:\myfile.zip", "MyCommand()")
? HTTPGet("http://www.mysite.com/mypage.htm", "C:\mypage.htm", "MyCommand()")
? FILEGet("File://C:\MyFile.txt", "C:\MyFileCopy.txt", "MyCommand()")
? FTPPut("C:\myfile.zip", "FTP://myusername:mypassword@mysite.com/mydir/myfile.zip", "MyCommand()")
? HTTPPut("C:\mypage.htm", "http://www.mysite.com/mypage.htm", "MyCommand()")
? FilePut("C:\myfile.zip", "File://C:\myfile.zip", "MyCommand()")
m.lcString1 = FTPToStr("FTP://myusername:mypassword@mysite.com/mydir/myfile.txt", "MyCommand()")
m.lcString2 = HTTPToStr("http://www.mysite.com/mypage.htm", "MyCommand()")
SET LIBRARY TO

FUNCTION MyCommand()
  ? nConnectTotalBytes
  ? nConnectBytesSoFar
ENDFUNC


Documentación de VFPConnection (Gracias Germán!!!)

Function FTPGet()

Sintaxis: FTPGet(cSourceURL, cDestination[, cCallbackFunction)

Parámetros:
cSourceURL - La URL para el archivo que desea descargar.
cDestination - La ruta (directorio) completa y el nombre de archivo donde desea guardar el archivo fuente.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Lógico - regresa .T. si tiene éxito o .F. si la operación ha fallado.

Comentarios:
Esta función le da la capacidad para descargar un archivo desde un sitio FTP.
Para especificar el puerto o información de ingreso, simplemente debe incluirla en cSourceURL, como por ejemplo:

"FTP://myusername:mypassword@myftpsite.com:21/mydir/myfile.zip".

Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


Function HTTPGet()

Sintaxis: HTTPGet(cSourceURL, cDestination[, cCallbackFunction)

Parámetros:
cSourceURL - La URL para el archivo que desea descargar.
cDestination - La ruta (directorio) completo y nombre de archivo donde desea guardar el archivo fuente.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Lógico - regresa .T. si tiene éxito o .F. si la operación ha fallado.

Comentarios:
Esta función le da la habilidad para descargar un archivo desde un sitio web.

Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


Function FILEGet()

Sintaxis: FILEGet(cSourceURL, cDestination[, cCallbackFunction])

Parámetros:
cSourceURL - La ruta (directorio) completo y nombre del archivo que desea copiar.
cDestination - La ruta (directorio) completo y nombre de archivo donde desea guardar el archivo fuente.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Lógico - regresa .T. si tiene éxito o .F. si la operación ha fallado.

Comentarios:
Esta función le da la capacidad para copiar un archivo desde una ubicación local o remota..

El formato para el parámetro cSourceURL es ligeramente diferente a lo que podría esperarse. Para copiar el archivo C:\MyDir\MyFile.zip debe especificar cSourceURL como "FILE:// C:\MyDir\MyFile.zip".

Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


Function FTPPut()

Sintaxis: FTPGet(cSource, cDestinationURL[, cCallbackFunction)

Parámetros:
cSource - La ruta (directorio) completo y nombre del archivo que desea subir.
cDestinationURL - La URL a donde debe subirse el archivo especificado en cSource.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Lógico - regresa .T. si tiene éxito o .F. si la operación ha fallado.

Comentarios:
Esta función le da la habilidad para subir un archivo a un sitio FTP.
Para especificar el puerto o información de ingreso, simplemente debe incluirla en cDestinationURL, como por ejemplo:

"FTP://myusername:mypassword@myftpsite.com:21/mydir/myfile.zip".

Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


Function HTTPPut()

Sintaxis: HTTPGet(cSource, cDestinationURL[, cCallbackFunction)

Parámetros:
cSource - La ruta (directorio) completo y nombre del archivo que desea subir.
cDestinationURL - La URL a donde debe subirse el archivo especificado en cSource.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Lógico - regresa .T. si tiene éxito o .F. si la operación ha fallado.

Comentarios:
Esta función le da la habilidad subir un archivo a un sitio Web.

Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


Function FILEPut()

Sintaxis: FILEPut(cSource, cDestinationURL[, cCallbackFunction])

Parámetros:
cSource - La ruta (directorio) completo y nombre del archivo que desea copiar.
cDestinationURL - La ruta (directorio) completo y nombre de archivo donde desea copiar el archivo especificado en cSource.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Lógico - regresa .T. si tiene éxito o .F. si la operación ha fallado.

Comentarios:
Esta función le da la capacidad para copiar un archivo desde una ubicación local o remota..

El formato para el parámetro cDestinationURL es ligeramente diferente a lo que podría esperarse. Para copiar el archivo C:\MyDir\MyFile.zip se debe especificar cDestinationURL como "FILE:// C:\MyDir\MyFile.zip".

Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


Function FTPToStr()

Sintaxis: FTPToStr(cSourceURL[, cCallbackFunction)

Parámetros:
cSourceURL - La ruta (directorio) completo y nombre del archivo que desea retornar como una cadena de texto.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Caracter - el contenido del archivo especificado por cSourceURL

Comentarios:
Esta función le da la habilidad para recuperar una archivo desde un sitio FTP como una cadena de texto.

Para especificar el puerto o información de ingreso, simplemente debe incluirla en cSourceURL, como por ejemplo:

"FTP://myusername:mypassword@myftpsite.com:21/mydir/myfile.zip".
Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


Function HTTPToStr()

Sintaxis: HTTPToStr(cSourceURL[, cCallbackFunction)

Parámetros:
cSourceURL - La ruta (directorio) completo y nombre del archivo que desea copiar.
cCallbackFunction - Una cadena opcional que indica una función, procedimiento o método que desea desencadenar cuando ocurra una lectura/escritura, algo como "MyCallback()".

Valor devuelto:
Lógico - regresa .T. si tiene éxito o .F. si la operación ha fallado.

Comentarios:
Esta función le da la capacidad para recuperar una archivo desde un sitio Web como una cadena de texto.

Cuando se llama a la función cCallbackFunction, la FLL crea dos (2) variables  (nConnectTotalBytes y nConnectBytesSoFar) dinámicamente. Estas variables pueden utilizarse con la función, procedimiento o método especificado para determinar el tamaño total en bytes del archivo fuente, así como el progreso total de la operación (Porcentaje = 100 * nConnectBytesSoFar / nConnectTotalBytes).


15 de febrero de 2008

Conocer la IP de la conexión a Internet

Una función para saber la dirección IP de nuestra conexión a Internet.

Con esta función que descarga la página de inicio del sitio www.WhatIsMyIP.org podemos saber con que IP estamos conectados a Internet.

Esta función tiene dos opciones:

Opción 1: Con la nueva libreria My incluida en Sedna para VFP 9.0:
? IpInternet()

FUNCTION IpInternet()
  LOCAL My AS My
  My = NEWOBJECT("My","My.vcx")
  ERASE "IP.txt"
  IF 0 = My.Computer.Network.DownloadFile("http://www.whatismyip.org", "IP.txt")
    RETURN FILETOSTR("IP.txt")
  ELSE
    RETURN ""
  ENDIF
ENDPROC

Opción 2: Con la API de Windows (esta opción es válida para todas las versiones de Visual FoxPro):
? IpInternet()

FUNCTION IpInternet()
  DECLARE LONG URLDownloadToFile IN URLMON.DLL ;
    LONG, STRING, STRING, LONG, LONG
  ERASE "IP.txt"
  IF 0 = URLDownloadToFile(0, "http://www.whatismyip.org", "IP.txt", 0, 0)
    RETURN FILETOSTR("IP.txt")
  ELSE
    RETURN ""
  ENDIF
ENDPROC
En realidad ambas funciones realizan lo mismo, ya que el método My.Computer.Network.DownloadFile() de la clase My, en un "envoltorio" de la función URLDownloadToFile() de la API de Windows.

Opción 3: Con la libreria VFPConnection.FLL de Craig Boyd que la pueden descargar de aquí:
? IpInternet()

FUNCTION IpInternet()
  SET LIBRARY TO (LOCFILE("vfpconnection.fll","FLL")) ADDITIVE
  lcIP = HTTPToStr("http://www.whatismyip.org")
  RELEASE LIBRARY "vfpconnection.fll"
  RETURN lcIP
ENDPROC
La función HTTPToStr() no realiza lectura/escritura de disco, con lo cual se evita que la página descargada quede en la cache.