Visual FoxPro como cliente en Internet (II): File Transfer Protocol

Por Pablo Almunia
© Copyrights 1997 by FoxPress, All rights reserved
FoxPress, Julio 1997

En el artículo anterior describimos como utilizar el Dial-Up Networking (Acceso Telefónico a Redes) desde Visual FoxPro a fin de establecer una conexión con Internet. Siguiendo con esta línea de artículos veremos como utilizar el File Transfer Protocol (FTP) para enviar y recibir ficheros por Internet desde Visual FoxPro.

La mayoría de las veces cuando pensamos en Internet pensamos en el Word Wide Web, pero uno de los servicios más interesantes para una aplicación es el File Transfer. Por medio de este servicio podemos construir un sistema de actualización automática de nuestro software, recepción de pedidos de nuestros clientes o recogida de informes desde oficinas remotas. Las posibilidades son muchas y no es muy complicado.

Servidores y Clientes FTP

Como la mayoría de los protocolos de Internet el FTP requiere la existencia de un servidor que nos permita recoger (y en algunos casos enviar) ficheros desde un cliente. También, como la mayoría de protocolos de Internet, el servidor FTP sólo actúa bajo las peticiones del cliente y no es posible enviar un fichero si este no ha sido solicitado.

Para nuestro sistema necesitaremos utilizar un servidor FTP. La mayoría de los servidores FTP públicos de Internet sólo permiten conexiones anónimas para extraer ficheros y no permiten recibirlos. Si queremos aprovechar todas las posibilidades deberemos hacernos con un servidor FTP.

Windows NT trae dos servidores FTP, el incorporado dentro de sus servicios TCP/IP y el incluido con el servidor Microsoft Internet Information Server. Existe una buena colección de servidores FTP para Windows NT, pero personalmente me siento bastante satisfecho con el que nos proporciona Microsoft con el IIS.

En el mundo de los programas comerciales, shareware y freeware existen algunos servidores FTP para Windows 95 excelentes. Deberemos pensar en alguno de ellos si queremos utilizar Windows 95 como un servidor FTP más allá de unas pocas peticiones.

Windows NT y Windows 95 disponen de un cliente FTP por medio de órdenes de comando. Este cliente es muy similar al que se dispone en la mayoría de los sistemas UNIX, pero bastante pobre para los usuarios acostumbrados a la interface gráfica, que preferirán hacer uso del Browser de WWW como cliente FTP. Otra posibilidad es utilizar algún programa FTP gráfico shareware y freeware como CuteFTP o WsFTP, programas muy difundidos entre los usuarios de Internet.

Usuario y Password

En FTP, a diferencia de otros protocolos, entre el servidor y el cliente se establece una sesión en la que se permite al cliente, dependiendo de su usuario y password, acceder a unos determinados ficheros y/o directorios, en sólo lectura o en escritura y lectura. No es posible realizar una sesión FTP sin una identificación de usuario. La mayoría de los servidores de Internet permiten el acceso de un usuario llamado anonymous cuya password es simplemente la dirección de correo del que se conecta (en la práctica puede ser cualquier cosa) que permite acceder a la parte pública del servidor.

Deberemos tener clara una cierta política de usuarios si tenemos intención de recibir ficheros en un servidor FTP, pues no sería buena idea dar al usuario anonymous estos privilegios. Si sólo va a servir ficheros es posible utilizar únicamente este usuario genérico.

La orden FTP

Como hemos dicho, Windows 95 y Windows NT disponen de una orden FTP en modo comando que nos permite realizar todas las operaciones del cliente FTP. Para arrancarlo debemos abrir una ventana de Interfaz de Comando (DOS) y escribir simplemente FTP. Nos aparecerá una especie de símbolo del sistema con el texto ftp>. Una vez en este punto podemos escribir ordenes FTP.

ftp> open ftp.server1.es
Connected to ftp.server1.es.
220 server1 Microsoft FTP Service
User (ftp.server1.es:(none)): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password: ………………         
230-Windows NT FTP Service.
230 Anonymous user logged in as anonymous.
ftp> binary
200 Type set to I.
ftp> get parche.exe
200 PORT command successful.
150 Opening BINARY mode data connection for parche.exe(48790 bytes).
226 Transfer complete.
48790 bytes received in 1,43 seconds (34,12 Kbytes/sec)
ftp> quit
221 Bye.

Se pueden observar algunas de las ordenes comprensibles por el cliente FTP para conectarse (OPEN), configurar (BINARY) o recibir los ficheros (GET). Es importante fijarse en la necesidad de configurar la trasferencia como binaria si vamos a trasmitir un fichero que no sea sólo texto.

Desde un programa como FoxPro 2.x o Visual FoxPro podemos utilizar este sistema con un fichero en el que pasamos las operaciones que deseamos realizar y uno de los parámetros que este programa admite en Windows 95 y en Windows NT.

RUN FTP -s:fichero.ftp >resultado.txt

Aquí se puede ver un programa FoxPro que baja el fichero con el índice de la Knowledge Base de FoxPro del servidor FTP de Microsoft:

*** Construir el fichero para ftp
SET ALTERNATE TO indexkb.ftp
SET ALTERNATE ON
SET CONSOLE OFF
? "open ftp.microsoft.com"
? "anonymous"
? "usuario@servidor.es"
? "cd /developr/fox/kb"
? "get index.txt"
? "quit"
SET CONSOLE ON
SET ALTERNATE OFF
SET ALTERNATE TO
*** Llamar al comando ftp
RUN ftp -s:indexkb.ftp >resultado.txt

Al ejecutar este programa se muestra una ventana donde aparecen las distintas órdenes que hemos pasado. Desde luego no es un sistema muy elegante, pero funciona. Podríamos mejorarlo haciendo que la ventana de comandos no se visualizara, leyendo de un fichero el resultado de la orden, etc. De todas formas está es la forma más sencilla, y posiblemente más formativa, de utilizar el FTP.

Cada cliente FTP permite incluir más o menos órdenes, pero las principales están en todos los programas. Para saber más sobre la orden FTP y sus posibilidades se puede consultar el comando por medio del parámetro /? o desde su prompt por medio de la orden HELP.

Microsoft Internet Transfer Protocol ActiveX Control


  La siguiente aproximación a la programación desde Visual FoxPro con FTP es el uso de Controles ActiveX. Existen bastantes controles ActiveX comerciales que proveen de acceso al servicio FTP, entre ellos se pueden citar: Distinct Visual Internet Toolkit, FTP Internet Data Control de Crescent y FTP OCX de Mabry. Microsoft por su parte distribuyó una beta del Internet Control Pack, pero desgraciadamente cedió su distribución a Crescent.

Con el Visual Studio ha aparecido un control denominado Microsoft Internet Transfer Protocol que permite acceder de forma sencilla a los servicios de WinInet para hacer uso de HTTP, FTP y GOPHER. Este control, sin ser excelente, es muy sencillo de utilizar y nos permite un acceso bastante bueno a FTP.

Conexión e Identificación

Una vez que seleccionamos el control y lo incluimos dentro de un formulario de Visual FoxPro podemos empezar por configurar el protocolo que vamos a utilizar. Como hemos dicho, este control sirve para HTTP, FTP y GOPHER. En muchos casos el tipo de URL le indicará el tipo de protocolo que queremos utilizar, pero siempre podemos usar la propiedad Protocol con el valor 2 (icFTP) para indicar que deseamos FTP.

También configuraremos el modo en el que nos conectaremos con Internet con la propiedad AccessType. Si esta propiedad tiene el valor 1 (icDirect) la conexión se realizará directamente por la red, si el valor es 2 (icNamedProxy) la conexión se realizará por Proxy y si el valor es 0 (icUseDefault) cogerá la configuración del Panel de Control. Si la conexión se realiza por Proxy debemos cumplimentar la propiedad Proxy con el nombre o dirección del mismo.

Si las conexiones las realizamos con el usuario anonymous no debemos poner nada en la propiedad UserName, pero si queremos conectarnos a un servidor por medio de otro usuario debemos utilizar las propiedades UserName y Password.

La conexión con el servidor FTP se realiza la primera vez que hagamos una llamada a los métodos OpenURL o Execute. No existe como tal una orden tipo Connect como la que disponen otros Controles ActiveX.

Para desconectarnos destruiremos el control, abriremos otra conexión o bien llamaremos a la orden Execute con el comando CLOSE.

Obtener ficheros de forma síncrona

Para obtener ficheros tenemos dos posibilidades, utilizar un método síncrono (no devuelve control hasta que la transferencia ha terminado) o utilizar un método asíncrono. En el primer caso hacemos uso de la función OpenURL con dos parámetros: el fichero que deseamos obtener y el formato del buffer 0 (icString) como una cadena, 1 (cByteArray) como una matriz de elementos binarios. La función devuelve en un buffer el contenido de ese URL.

PROCEDURE cmdurl.Click
cBuffer = thisForm.oleInet.OpenURL( ;
"ftp://ftp.microsof.com" + ;
"/developr/fox/kb/index.txt", 0 )
	ThisForm.edtResultado.Value = cBuffer
If NOT EMPTY( ;
thisForm.oleInet.ResponseInfo )
		MessageBox( ;
thisForm.oleInet.ResponseInfo )
ENDIF
ENDPROC

Como podemos observar no hemos necesitado configurar el control para poder hacer uso de OpenURL, la configuración básica se ha recogido del formato del primer parámetro de la función, estableciendo que el protocolo era FTP, el puerto era el de por defecto (21), etc.

Si ejecutamos OpenURL sobre un directorio de un servidor FTP recibiremos un fichero en formato HTML con la lista de ficheros y directorios en él contenida. Podemos leer este fichero para obtener la información que pudiéramos necesitar, pero es bastante complicado.

Este sistema es bastante poco efectivo. Necesitamos almacenar todo el fichero en un buffer y no se devuelve el control hasta que termine la transferencia. Está más pensado para el protocolo HTTP que para el FTP que nos ocupa.

Ordenes en forma asíncrona

El Microsoft Internet Transfer Protocol Control dispone de un sistema de ejecución de órdenes FTP de forma totalmente asíncrona. Para ello utilizaremos la orden Execute y los comandos FTP que ésta admite. Por ejemplo:

ThisForm.oleInet.Execute( ;
	"ftp://ftp.microsoft.com", ;
"DIR *", "", "" )

Una vez ejecutado el método Execute recibimos el control del programa y por medio del evento StateChanged sabremos si ha tenido éxito el comando que hemos ejecutado.

Si todo ha funcionado correctamente obtendremos en el parámetro state del evento un 12 y podremos dar por terminada la operación. Algunos comandos aceptados por Execute como LS, DIR, PWD, SIZE, etc. envían su resultado a un buffer del que obtendremos su contenido por medio del método GetChunk dentro del evento StateChanged.

LPARAMETER state
DO CASE
CASE state = 12
	cData = ""
	lDone = .F.
	DO WHILE NOT lDone
		cTmp = ;
THISFORM.OleInet.GetChunk(1024, 0)
		IF LEN( cTmp ) = 0
			lDone = .T.
		ENDIF
		cData = cData + cTmp
	ENDDO
	THISFORM.edtResultado.VALUE = cData
ENDCASE

Si ha fallado, el parámetro state del evento será 11 y por medio de las propiedades ResponseInfo y ResponseCode podremos saber qué ha ocurrido.

Para evitar problemas no deberemos cerrar la form mientras el control esté trabajando en segundo plano. Para comprobar su estado utilizaremos la propiedad StillExecuting que se encontrará a .T. si el control se encuentra realizando alguna orden enviada por nosotros.

PROCEDURE QueryUnload
	IF ThisForm.oleInet.StillExecuting
		IF MessageBox( ;
			"¿Desea cancelar el proceso?", ;
			4 + 16 ) = 6
			ThisForm.oleInet.Object.Cancel()
		ELSE
			NODEFAULT
		ENDIF
	ENDIF
ENDPROC

Podremos cancelar esta ejecución por medio del método Cancel. Como el objeto OleControl de Visual FoxPro dispone ya de un método Cancel deberemos llamar al método desde la referencia al Control ActiveX contenida en la propiedad Object. Esta propiedad de la clase OleControl es muy útil en estos casos donde se producen confusiones con los miembros de clases de Visual FoxPro o cuando queremos utilizar ordenes como AMEMBERS o para el depurador de Visual FoxPro, pues nos muestra todas las propiedades del control. Debo agradecer al soporte técnico de Microsoft Ibérica la resolución del problema del método Cancel en el control que nos ocupa y por añadido el redescubrimiento de las posibilidades de la propiedad Object de la clase OleControl.

Mas allá de este control

El Microsoft Internet Transfer Procotol ActiveX Control no es un control muy sofisticado, pero funciona aceptablemente bien. Durante la elaboración del artículo se produjeron algunos problemas, sobre todo derivados de la no cancelación de procesos en curso, pero aparte de esto, es bastante correcto. En muchos casos deberemos pensar en alguno de los controles ActiveX comerciales que citamos más arriba para obtener toda la potencia y control del protocolo FTP.

También podemos seguir la senda del API de Windows por medio de WinInet. El Microsoft Internet Transfer Procotol ActiveX Control dispone de la propiedad hInternet que nos devuelve un manejador a una conexión ya realizada para ser utilizada por el API WinInet.

WinInet

Dentro de la estrategia de Microsoft en el desarrollo de clientes para Internet ha aparecido un API de bajo nivel para acceder a los servicios HTTP, FTP y GOPHER. Este API es utilizado por Microsoft Internet Explorer, pero está a disposición de cualquier programa Windows que quiera utilizarlo. Al ser este un sistema de más bajo nivel que los vistos anteriormente nos permite un mayor control a costa de una mayor complejidad, pero en mi opinión compensa.

Conexión e Identificación

El API de WinInet dispone de dos funciones básicas para la conexión a un servicio FTP, las ordenes InternetOpen e InternetConnect. Si fuéramos a utilizar un servicio que no necesitara identificación como es HTTP, esta segunda función no sería necesaria.

De estas funciones recogeremos sendos manejadores que necesitaremos en el resto de funciones del API de WinInet. Aquí mostramos el esquema de cualquier programa Visual FoxPro que haga uso de este API:

*** Definición de constantes
#DEFINE INTERNET_OPEN_TYPE_PRECONFIG     0
#define INTERNET_OPEN_TYPE_DIRECT        1
#define INTERNET_OPEN_TYPE_PROXY         3
#DEFINE INTERNET_DEFAULT_FTP_PORT       21
#DEFINE INTERNET_SERVICE_FTP             1
#DEFINE INTERNET_FLAG_PASSIVE     14217728
*** Declaración de funciones del API
DECLARE LONG InternetOpen ;
	IN "wininet.dll" ;
	STRING   lpszAgent, ;
	LONG     dwAccessType, ;
	STRING   lpszProxyName, ;
	STRING   lpszProxyBypass, ;
	LONG     dwFlags
DECLARE LONG InternetConnect ;
	IN "wininet.dll" ;
	LONG     hInternetSession, ;
	STRING   lpszServerName, ;
	LONG     nServerPort, ;
	STRING   lpszUsername, ;
	STRING   lpszPassword, ;
	LONG     dwService, ;
	LONG     dwFlags, ;
	LONG     dwContext
DECLARE INTEGER InternetCloseHandle ;
	IN "wininet.dll" ;
	LONG     hInet
DECLARE LONG GetLastError ;
	IN WIN32API
*** Apertura del API
nInternet = InternetOpen( ;
	"pruebavfp", ;
	INTERNET_OPEN_TYPE_DIRECT, ;
	"", "", 0 )
IF nInternet = 0
	MessageBox( "Error: " ;
		+ LTRIM( STR( GetLastError() ) ) ;
		+ " en InternetOpen.", 16 )
	InternetCloseHandle( nFtp )
	RETURN
ENDIF
*** Conexión con el servicio FTP
nFtp = InternetConnect( ;
	nInternet, ;
	"ftp.microsoft.com", ;
	INTERNET_DEFAULT_FTP_PORT, ;
	"", "", ;
	INTERNET_SERVICE_FTP, ;
	INTERNET_FLAG_PASSIVE, ;
	0 )
IF nFtp = 0
	MessageBox( "Error: " ;
		+ LTRIM( STR( GetLastError() ) ) ;
		+ " en InternetConnect.", 16 )
	RETURN
ENDIF

*!* Insertar aquí código WinInet *!*

*** Cierre de la conexión FTP
InternetCloseHandle( nFtp )
*** Cierre del uso del API
InternetCloseHandle( nInternet )

Como podemos observar, la función InternetOpen requiere un identificador de la aplicación que hace uso de las funciones, el tipo de conexión a Internet (Directa, por Proxy o la preconfigurada en el Panel de Control), si se utiliza Proxy los siguientes parámetros son la dirección del mismo y la dirección de destino y por último se pueden pasar algunas opciones de configuración.

Por su parte la función InternetConnect requiere el manejador obtenido por InternetOpen, la dirección del servidor a que nos queremos conectar, el puerto del servicio (no tiene por qué ser el predeterminado), el usuario y la password (si no se incluye nada serán anonymous e IExxuser@), el servicio al que nos queremos conectar, en nuestro caso FTP y los dos últimos parámetros de configuración.

Una vez establecida una conexión con éxito en el servidor FTP podemos hacer uso del resto de funciones del API de WinInet.

Para concluir deberemos cerrar los manejadores por medio de la orden InternetCloseHandle.

Obtener ficheros

Para obtener un fichero del servidor FTP podemos hacer uso de una sencilla función llamada FTPGetFile. Requiere como parámetros el manejador de la sesión FTP, el nombre del fichero en el servidor, el nombre del nuevo fichero en el cliente, saber si da un error si el fichero ya existe o lo sobreescribe, los atributos del nuevo fichero, el tipo de transmisión que se realiza (binaria o texto) y un parámetro de configuración.

Dentro del ejemplo anterior podríamos incluir este código para obtener el índice de la Knowledge Base relacionado con FoxPro.

*** Definición de constantes
#DEFINE FILE_ATTRIBUTE_NORMAL         128
#DEFINE FTP_TRANSFER_TYPE_ASCII         1
#DEFINE FTP_TRANSFER_TYPE_BINARY        2
*** Declaración de funciones
DECLARE INTEGER FtpGetFile ;
	IN "wininet.dll" ;
	LONG    hFtpSession, ;
	STRING  lpszRemoteFile, ;
	STRING  lpszNewFile, ;
	LONG    fFailIfExist, ;
	LONG    dwFlagsAndAttributes, ;
	LONG    dwFlags,;
	LONG    dwContext
*** Realizar la conexión...
*** Recibir el fichero
WAIT WIND "Recibiendo el fichero" NOWAIT
FtpGetFile( ;
	nFtp, ;
	"/developr/fox/kb/index.txt",	"index.txt", 0, ;
	FILE_ATTRIBUTE_NORMAL, ;
	FTP_TRANSFER_TYPE_ASCII, ;
	0 )
WAIT CLEAR
*** Realizar la desconexión...

Este sistema tiene un problema: mientras se está bajando el fichero no tenemos ningún control del programa y si este proceso es un poco largo puede dar la sensación de que nuestro programa se ha colgado. Para evitarlo podemos utilizar un programa un poco más complejo que haga uso de las funciones FtpOpenFile e InternetReadFile. Por medio de estas dos funciones podemos abrir y leer un fichero residente en un servidor FTP de forma similar a como abriríamos un fichero de un disco local. Veamos un ejemplo (se ha eliminado la parte de la conexión y desconexión que pueden verse en el primer ejemplo de esta sección):

*** Definición de Constantes
#DEFINE GENERIC_READ            2147483648
#DEFINE GENERIC_WRITE           1073741824
#DEFINE FTP_TRANSFER_TYPE_ASCII          1
#DEFINE FTP_TRANSFER_TYPE_BINARY         2
*** Declaración de Funciones
DECLARE LONG FtpOpenFile ;
	IN "wininet.dll" ;
	LONG     hFtpSession, ;
	STRING   lpszFileName, ;
	INTEGER  fdwAccess, ;
	INTEGER  dwFlags, ;
	INTEGER  dwContext
DECLARE LONG InternetReadFile ;
	IN "wininet.dll" ;
	LONG     hFtpSession, ;
	STRING  @lpBuffer, ;
	LONG     dwNumberOfBytesToRead, ;
	LONG    @lpNumberOfBytesRead
*** Realizar la conexión...
*** Abrir el fichero en el servidor
nFichFTP = FtpOpenFile( ;
	nFtp, ;
	"/developr/fox/kb/index.txt", ;
	GENERIC_READ, ;
	FTP_TRANSFER_TYPE_ASCII, ;
	0 )
IF nFichFTP = 0
	MESSAGEBOX( "Error: " ;
		+ LTRIM( STR( GetLastError() ) ) ;
		+ " en FtpOpenFile.", 16 )
	InternetCloseHandle( nFtp )
	InternetCloseHandle( nInternet )
	RETURN
ENDIF
*** Abir el fichero en el cliente
nFich = FCREATE( "index.txt" )
*** Contruir las variables necesarias
nTama = 0
nLen = 1
*** Bucle de lectura
DO WHILE nLen # 0
	cBuffer = REPLICATE( CHR(0), 2048 )
	*** Leer del fichero en el servidor
	InternetReadFile( ;
		nFichFTP, ;
		@cBuffer, ;
		LEN( cBuffer ), ;
		@nLen ) 
	*** Escribir el fichero en el cliente
	FWRITE( ;
		nFich, ;
		SUBSTR( cBuffer, 1, nLen ) )
	*** Aumentar el tamaño total
	nTama = nTama + nLen
	WAIT WIND "Recibidos " ;
		+ LTRIM( STR( nTama ) ) NOWAIT
ENDDO
WAIT CLEAR
*** Cerrar el fichero local
FCLOSE( nFich )
*** Cerrar el fichero en el servidor
InternetCloseHandle( nFichFTP )
*** Realizar la desconexión...

FtpOpenFile requiere un manejador de FTP, el nombre del fichero en el servidor, incluir si es para escritura o para lectura, el tipo de transferencia y un parámetro de configuración. Por su parte InternetReadFile requiere el manejador obtenido de FtpOpenFile, un buffer por referencia donde dejar el contenido leído, el tamaño en bytes a leer y una variable por referencia donde dejar el tamaño de lo leído.

Mientras se produce la lectura del fichero podemos notificar su progreso, permitir su cancelación, etc. Este es, sin duda, el sistema que más control nos permite.

Enviar ficheros

Al igual que se pueden obtener ficheros, se pueden enviar al servidor FTP. Para ello será necesario identificarse con un usuario distinto a anonymous a fin de obtener permisos de escritura y a su vez mantener una correcta política de seguridad. Una vez conectados podemos hacer uso de la siguiente función:

DECLARE INTEGER FtpPutFile ;
	IN "wininet.dll" ;
	LONG     hFtpSession, ;
	STRING   lpszLocalFile, ;
	STRING   lpszNewRemoteFile, ;
	LONG     dwFlags,;
	LONG     dwContext

Esta función requiere el manejador de la sesión FTP, el nombre del fichero local, el nombre del fichero en el servidor, el tipo de transmisión y un parámetro de configuración. Como FtpGetFile, esta función no retorna hasta haber terminado de realizar la transmisión.

Si queremos tener un mayor control deberemos hacer uso de la función FtpOpenFile, con el parámetro GENERIC_WRITE, y de la función:

DECLARE LONG InternetWriteFile ;
	IN "wininet.dll" ;
	LONG     hFtpSession, ;
	STRING   lpBuffer, ;
	LONG     dwNumberOfBytesToWrite, ;
	LONG    @lpNumberOfBytesWrite

Esta función, muy similar a InternetReadFile, requiere el manejador del fichero abierto en el servidor, el buffer con el contenido a escribir, el número de caracteres de este buffer y una variable por referencia para retornar el número de bytes realmente escritos.

En el disco que acompaña a la revista se pueden obtener ejemplo del uso de estas funciones.

Obtener la lista de ficheros

En la orden FTP podemos hacer uso de los comandos dir o ls para ver los ficheros del servidor. Los clientes FTP gráficos suelen mostrar el árbol de directorios del servidor y los ficheros que contienen. Desde el API de WinInet también podemos obtener información sobre los directorios y fichero que contiene el servidor.

Para ello vamos a utilizar las funciones FtpFindFirstFile e InternetFindNextFile del API de WinInet. Estas dos funciones nos permiten recorrer un determinado directorio del servidor FTP obteniendo en una estructura tipo WIN32_FIND_DATA la información sobre los ficheros ahí contenidos.

Como siempre que nos encontramos estructuras en funciones contenidas en DLLs que queremos llamar desde Visual FoxPro necesitamos realizar un trabajo extra: construir un buffer donde almacenar la estructura y luego ir sacando del mismo los distintos elementos por medio de su posición.

En los casos en los que el elemento de la estructura es un número deberemos utilizar unos programas complementarios, escritos ya por nosotros en el artículo anterior, y que denominamos CToWord y CToInt.

En este caso debemos trabajar también con las estructuras FILETIME que se contienen dentro de la estructura WIN32_FIND_DATA. Para ello vamos a utilizar la función FileTimeToSystemTime del API de Windows que nos permite transformar la estructura FILETIME en una estructura SYSTEMTIME más sencilla de transformar.

Este es el ejemplo de obtención del directorio donde parte la Knowledge Base de FoxPro en el servidor FTP de Microsoft (se ha eliminado la parte de la conexión y desconexión que pueden verse en el primer ejemplo de esta sección):

*** Definición de constantes
#DEFINE FILE_ATTRIBUTE_NORMAL          128
#DEFINE FILE_ATTRIBUTE_DIRECTORY        16
#DEFINE INTERNET_FLAG_RAW_DATA   1073741824
*** Declaración de Funciones
DECLARE Integer InternetFindNextFile ;
	IN "wininet.dll" ;
	Long     hFind, ;
	String  @lpFindFileData
DECLARE Integer FtpFindFirstFile ;
	IN "wininet.dll" ;
	Long     hFtpSession, ;
	String   lpszSearchFile, ;
	String  @lpFindFileData, ;
	Long     dwFlags, ;
	Long     dwContext
Declare Integer FtpSetCurrentDirectory ;
	IN "wininet.dll" ;
	Long     hFtpSession, ;
	String   lpszDirectory
DECLARE LONG FileTimeToSystemTime ;
	IN WIN32API ;
	STRING    lpFileTime, ;
	STRING   @lpSystemTime
*** Realizar la conexión...
*** Crear un cursor 
CREATE CURSOR ftpserver (;
	nombre C(254), ;
	Tipo C(10), ;
	Tama I,;
	Fecha T )
*** Modificar la configuración de la fecha
cOldDate = SET("DATE")
SET DATE TO DMY
*** Ir al directorio de la KB de FoxPro
FtpSetCurrentDirectory( nFtp, ;
"/developr/fox/kb/" )
*** Empezar el recorrido de busqueda
cFindData = REPLICATE( CHR(0), 320  )
nFind = FtpFindFirstFile( ;
	nFtp, ;
	NULL, ;
	@cFindData, ;
	INTERNET_FLAG_RAW_DATA, ;
	0 )
nSeguir = 1
DO WHILE nSeguir # 0
	*** Obtener el nombre de la matriz
	cNombre = SUBSTR( cFindData, 45, ;
		AT(CHR(0), SUBSTR(cFindData,45)) -1 )
	*** Directorio o Fichero
	IF CToWord( SUBSTR( cFindData, 1, 4 ) );
= FILE_ATTRIBUTE_DIRECTORY
		cTipo = "Directorio"
	ELSE
		cTipo = "Fichero"
		*** Tamaño del fichero
		nTama = CToWord( ;
SUBSTR( cFindData, 33, 4 ) )
	ENDIF
	*** Cambiar el formato de Fecha y Hora
	cSystemTime = REPLICATE( CHR(0), 16 )
	FileTimeToSystemTime( ;
		SUBSTR( cFindData, 21, 8 ), ;
		@cSystemTime )
	*** Obtener cada uno de los elementos
	cDia = PADL( CToInt( SUBSTR( ;
		cSystemTime, 7, 2 ) ), 2, "0" )
	cMes = PADL( CToInt( SUBSTR( ;
		cSystemTime, 3, 2 ) ), 2, "0" )
	cAnno = STR( CToInt( SUBSTR( ;
		cSystemTime, 1, 2 ) ), 4 )
	cHora = PADL( CToInt( SUBSTR( ;
		cSystemTime, 9, 2 ) ), 2, "0" )
	cMinuto = PADL( CToInt( SUBSTR( ;
		cSystemTime, 4, 2 ) ), 2, "0" )
	tFechaHora = CTOT( ;
		cDia+"/"+cMes+"/"+cAnno+" " ;
		+cHora+":"+cMinuto )
	*** Insertar el dato en el cursor
	INSERT INTO ftpserver ;
		VALUES ( cNombre, ;
			cTipo, ;
			nTama, ;
			tFechaHora )
	*** Continuar con la búsqueda
	cFindData = REPLICATE( CHR(0), 320 )
	nSeguir = InternetFindNextFile( ;
		nFind, ;
		@cFindData )
ENDDO
*** Realizar la desconexión...
*** Restaurar la configuración de la fecha
SET DATE TO (cOldDate)
*** Mostrar el cursor obtenido
GO TOP
BROWSE NOWAIT

Se ha construido un cursor con la información obtenida por medio de estas funciones.

Conclusión

Las funciones de WinInet son muchísimas más de las pocas aquí descritas. No hemos podido ver las funciones de gestión de errores, configuración avanzada, etc.

Sea cual sea el sistema que utilicemos el File Transfer Protocol es un servicio ideal como complemento de nuestras aplicaciones y como base de sofisticados sistemas de distribución.

Pablo Almuniapertenece al grupo de Consultoría y Control de Calidad de la Unidad de Informática de MAPFRE. Se puede entrar en contacto con él por Email en palmun@compuserve.com