Visual FoxPro como cliente en Internet (I): Dial-Up Networking

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

Introducción

Con este artículo empezamos una serie sobre cómo Visual FoxPro puede ser un cliente de los distintos servicios de Internet e intentaremos mostrar cómo podemos contruir una aplicación elegantemente integrada con la red de redes. En los próximos artículos veremos como recibir o enviar ficheros por FTP, como manejar los visualizadores HTML o como comunicar dos aplicaciones por medio de Internet.

Para empezar nos centraremos en como podemos arrancar y manejar un acceso a Internet desde Visual FoxPro, es decir, como podemos arrancar un Dial-Up Networking (Acceso Telefónico a Redes) de Windows 95 y Windows NT para comunicarnos con un proveedor de acceso a Internet.

Una aplicación elegante no presupone que existe una conexión permantente con Internet y pregunta amablemente, si es necesario, qué configuración de conexión a Internet puede utilizar y si puede abrir una conexión con ella. No existe peor aplicación que la que hace que tu modem se dispare a llamar sin saber qué está pasando.

¿Cómo acceder a Internet?

Para acceder desde un puesto Windows 95 o Windows NT a Internet tenemos dos opciones diferentes: acceder directamente desde el puesto por medio de un Dial-Up Networking y un modem o tarjeta RDSI o acceder por medio de la red local y un Router o un Proxy que se conecta a la Internet.

Cuando un Proxy o un Router conectado a nuestra red local detecta que existe un paquete TCP/IP dirigido a una red diferente a la red local encamina ese paquete a la Internet. La conexión del Proxy o Router a Internet puede ser permanente, pero también puede hacer uso de un sistema de Dial-Up por lo que se realizará una conexión con un proveedor de acceso cuando se realice una petición y desconectará cuando se produzca un determinado tiempo de inactividad.

En caso de tener nuestro puesto configurado para realizar el acceso por medio de un Proxy o Router nuestra aplicación Visual FoxPro sólo tendrá que hacer uso de un protocolo Internet como FTP o HTTP configurado para hacer uso del Proxy o Router y los paquetes TCP/IP serán encaminados a la Internet de forma trasparente para nuestra aplicación.

Si por el contrario el acceso a Internet se realiza directamente con Dial-Up Networking desde el puesto Windows 95 o Windows NT por medio de un modem o una tarjeta RDSI, la aplicación requiere que esa conexión se realice antes que sea posible utilizar un servicio por medio de Internet.

Esta conexión con Internet puede establecerla manualmente el usuario, o establecerse automáticamente cuando realizamos una petición TCP/IP a una red externa, pero en mi opinión es más elegante que la aplicación realice la conexión por si misma, si así lo configura el usuario.

Por otra parte, el sistema de Dial-Up Networking, que aquí vamos a describir, no sólo se utiliza para realizar una conexión a Internet. Con este mecanismo podemos conectarnos a otros ordenadores o a otras redes que dispongan de un servidor de Remote Access Service (RAS). Cualquier puesto Windows 95 o Windows NT puede ser un servidor de RAS si dispone de un modem o tarjeta RDSI y nosotros configuramos el servicio, siendo una buena solución para conectarnos a la red de la oficina desde casa.

Veremos cómo se puede manejar el Dial-Up Networking desde Visual FoxPro, ya sea para conectarse a Internet como para conectarse a un puesto o red remota, pero no vamos a explicar como configurar estos servicios. Le recomendamos se dirija a la Knowledge Base de Microsoft donde existen excelente artículos sobre la configuración de Proxys, Routers y Dial-Up Networking.

Arrancar el Dial-Up Networking

La primera aproximación al manejo del Dial-Up Networking es muy simple: arrancar una conexión del Dial-Up Networking por medio de ordenes del sistema operativo.

En Windows 95 y Windows NT utilizaremos técnicas distintas, estos dos sistemas se diferencia bastante en el uso de los del Dial-Up Networking y deberemos ver cada caso por separado.

Para saber en qué sistema operativo nos encontramos podemos hacer uso de la orden OS() de Visual FoxPro.

Windows 95

Si utilizamos Windows 95 podemos utilizar un programa del sistema llamado RunDLL32 que permite hacer una llamada a una función de una DLL. Con este programa llamaremos a la función RnaDial de la librería Rnaui.dll pasando como parámetro el nombre de la conexión del Dial-Up Networing que hayamos configurado con anterioridad.

En Visual FoxPro escribiríamos algo parecido a esto:

RUN /N Rundll Rnaui.dll,RnaDial Conexion1

En este caso estamos llamando a una conexión llamada Conexion1. Debemos escribir el nombre de la función exactamente igual, con mayúsculas y minúsculas para que funcione correctamente.

Windows NT

Si utilizamos Windows NT el sistema es muy similar, pero en este caso llamaremos a un programa del sistema directamente, este programa se llama RASDIAL.EXE. Le pasaremos como parámetro el nombre de la conexión del Dial-Networing que hayamos configurado con anterioridad.

En Visual FoxPro escribiríamos algo parecido a esto:

RUN /N RASDIAL conexion1

Esta orden permite pasar también como parámetros el nombre del usuario y la password y de esta forma evitar tener que guardarlos en la configuración de la conexión.

Evitar que salga el cuadro de diálogo

Cuando llamamos por cualquiera de los medios anteriores aparece un cuadro de diálogo con los datos de la configuración y preguntando si queremos conectar. Para evitar que aparezca ese cuadro de diálogo debemos configurar el Dial-Up Networking.

En Windows NT simplemente debemos guardar la password de la conexión y de esta forma no nos aparecerá ningún cuadro de diálogo.

En Windows 95 es un poco más complicado, pero también es posible. Debemos instalar un versión más moderna del Dial-Up Networking de la que trae por defecto el sistema. Para ello podemos obtener el fichero MSISDN11.EXE del Web de Microsoft que trae el soporte para tarjetas RDSI y la versión actualizada del Dial-Up Networking (no es necesario tener una tarjeta RDSI para instalar este complemento).

Una vez instalada la nueva versión del Dial-Up Networking podemos ir a la opción settings del mismo e indicar que no aparezca el cuadro de diálogo anterior y posterior a la conexión:


 

Si nuestra conexión requiere usuario y password debemos realizar, de forma similar a Windows NT, una conexión donde guardemos esa configuración:


 

El API del RAS

Con el sistema anterior tenemos muchas limitaciones: no podemos saber si ha funcionado correctamente la conexión, no podemos mostrar las distintas configuraciones en un cuadro de dialogo creado por nosotros para que el usuario pueda elegir cual utilizar, no podemos colgar la llamada, etc. Para realizar este tipo de operaciones debemos hacer uso del API del Remote Access Service (RAS).

Todos los ejemplos los hemos construido para Windows 95 y Windows NT 4. El API de RAS está disponible para Windows NT 3.51 y para Windows 3.x, pero las funciones, parámetros y estructuras cambian en algunas ocasiones y sería necesaria una adaptación de los ejemplos que aquí vamos a mostrar.

Obtener las entradas en la configuración del Dial-Up Networking

En primer lugar vamos a ver como podemos obtener una lista con todas las configuraciones del Dial-Up Networking configurados para este equipo. Para ello vamos a utilizar la siguiente función:

DECLARE INTEGER RasEnumEntries ;
	IN RASAPI32.DLL ;
	INTEGER   reserved,;
	STRING    PhoneBox,;
	STRING  @ RasEntryName,;
	INTEGER @ SizeOfRasEntryName,;
	INTEGER @ Entries

Esta función tiene un primer parametro reservado, en el que pondremos un 0. Luego necesita una cadena con la dirección del phonebook. Windows NT 4 dispone de la posibilidad de tener varios un phonebook con posibles conexiones, Windows 95 guarda todas las conexiones en el Registry. Si estamos en Windows 95 o queremos utilizar el phonebook por defecto del sistema utilizaremos una cadena vacia.

Luego necesita una matriz de estructuras de tipo RASENTRYNAME. En este punto tenemos algunas dificultades desde Visual FoxPro, pues no es capaz de generar tipos de datos en forma de estructura y debemos utilizar un buffer del suficiente tamaño como para contener la estructura y luego obtener cada uno de los elementos por su posición dentro de este buffer.

Por último es necesario pasar el tamaño total de la matriz de estructuras y una variable donde almacenará el número de entradas del phonebook que se han incluido dentro de la matriz de estructuras.

Como ejemplo hemos incluido esta función dentro de un programa que requiere una cadena con el nombre de una matriz donde se almacenarán las entradas que se encuentren en la configuración del Dial-Up Networking.

******************************************
***
*** Construye una matriz con todas las
*** entradas del phonebook
***
******************************************
LPARAMETER cArrayReturn

LOCAL cStructRasEntryName
LOCAL cStructRasEntryNames, nSize
LOCAL nEntries, nResult, cRasEntryName

*** Definición de la constantes necesarias
#DEFINE RAS_MAXENTRYNAME 256

*** Declaración de las funciones del API
DECLARE INTEGER RasEnumEntries ;
	IN RASAPI32.DLL ;
	INTEGER   reserved,;
	STRING    PhoneBox,;
	STRING  @ RasEntryName,;
	INTEGER @ SizeOfRasEntryName,;
	INTEGER @ Entries

*** Creación de una estructura tipo
*** RASENTRYNAME
cStructRasEntryName = wordtoc(264) ;
+ REPLICATE( CHR(0), RAS_MAXENTRYNAME )

*** Creación de una matriz con esa 
*** estructura
cArrayRasEntryNames = REPLICATE( ;
cStructRasEntryName, 255 )

*** Variable con el tamaño de la matriz
nSize = LEN( cArrayRasEntryNames )

*** Variable en la que se retorna el número
*** de entradas encontradas
nEntries = 0

*** Llamada a la función para obtener la 
*** información
nResult = RasEnumEntries( 0, "", ;
@cArrayRasEntryNames, ;
@nSize, ;
@nEntries )

*** Dimensión de la matriz como pública
PUBLIC ARRAY &cArrayReturn.[nEntries]

*** Extracción de los datos desde la matriz
*** de estructuras
FOR nCont = 0 TO nEntries - 1

	*** Obtener una Estructura
	cRasEntryName = SUBSTR( ;
cArrayRasEntryNames, ;
264*nCont + 1, 264 )

	*** Asignar el nombre a la matriz
	&cArrayReturn.[nCont+1] = SUBSTR( ;
cRasEntryName, 5, ;
		AT(CHR(0),SUBSTR(cRasEntryName,5))-1)

NEXT

*** Retorno del número de elementos que han 
*** encontrado
RETURN nEntries

Gracias a este programas podemos ya mostrar todas las posibles configuraciones de conexión antes de llamar a la misma, permitiendo que el usuario seleccione cual debemos utilizar.

Realizar una llamada por RAS

Para realizar una llamada directamente desde un programa Visual FoxPro, y no por medio de llamadas a ordenes del sistema operativo, debemos hacer uso principalmente de la función siguiente:

DECLARE Integer RasDial ;
	IN RASAPI32.DLL  ;
	Integer   RasDialExtensions, ;
	Integer   Phonebook, ;
	String  @ RasDialParams, ;
	Integer   NotifierType, ;
	Integer   Notifier, ;
	Integer @ hRasConn

Esta función utiliza como primer parámetro una estructura tipo RASDIALEXTENSIONS con información extra que sólo es utilizada desde Windows NT. Como nosotros no vamos a pasar esa estructura hemos definido el dato como Integer y no como String y de esta forma pasar un 0 como valor. Su segundo parámetro es un puntero a una cadena con la ruta de un phonebook de Windows NT. Como nosotros vamos ha utilizar el phonebook por defecto o las entradas en el registro de Windows 95 lo hemos definido de tipo Integer y pasaremos un 0.

El siguiente parámetro es muy importante, es una estructura RASDIALPARAMS con los parametros de la conexión que queremos realizar. Para poder construir esta estructura podemos utilizar la función GetEntryDialParams a la que pasamos una estructura sólo con el nombre de la configuración que queremos utilizar y el recoge el resto de los datos.

Los dos parámetros siguientes se utilizan para un seguimiento asíncrono del estado de la conexión. Como desde Visual FoxPro es bastante complejo la recepción de mensajes del sistema nos centraremos en realizar conexiones donde la función no devuelva el control hasta que se realice todo el proceso.

Por último le pasaremos una variable por referencia para que introduzca en ella el manejador de la conexión establecida.

Como ejemplo hemos creado una función que recibe el nombre de la entrada en el phonebook que queremos utilizar, realiza la llamada y comprueba que ha sido satisfactoría, devolviendonos el manejador de la misma.

******************************************
***
*** Realiza una llamada y devuelve el 
*** manejador a la conexión
***
******************************************
LPARAMETER cConexion

*** Declaración de las funciones del API DECLARE Integer RasDial ;
	IN RASAPI32.DLL  ;
	Integer   RasDialExtensions, ;
	Integer   Phonebook, ;
	String  @ RasDialParams, ;
	Integer   NotifierType, ;
	Integer   Notifier, ;
	Integer @ hRasConn 

DECLARE Integer RasHangUp ;
	IN RASAPI32.DLL ;
	Integer   hRasConn

DECLARE Integer RasGetEntryDialParams ;
	IN RASAPI32.DLL ;
	String  @ PhoneDirectory, ;
	String  @ RasDialParams, ;
	Integer @ IsPassword 

DECLARE Integer RasGetErrorString ;
   IN RASAPI32.DLL ;
   Integer   ErrorValue, ;
   String  @ MessageBuffer, ;
   Integer   BufferLen

LOCAL cPhonebook, cRasDialParams, nResult
LOCAL nRasConn

*** Elminar blancos en el parámetro pasado
cConexion = ALLTRIM( cConexion )

*** Preparar la estructura para los
*** parametros de conexión
cRasDialParams = wordtoc(1052) ;
+ cConexion ;
+ REPLICATE( CHR(0), ;
1045 - LEN( cConexion ) )
cPhonebook = chr(0)
nPasswords = 0

*** Obtener los parametros de conexión
nResult = RasGetEntryDialParams( ;
@cPhonebook, ;
@cRasDialParams, ;
@nPasswords )

*** Preparar variable para el retorno 
*** del manejador
nRasConn = 0

*** Realizar la conexión
nResult = RasDial( 0, 0, ;
@cRasDialParams, 0, 0, @nRasConn )

*** Comprobar si hubo error y mostrar 
*** mensaje si lo hubo
IF nResult # 0

	PRIVATE cMensaje
	cMensaje = SPACE(256)

	IF RasGetErrorString( nResult, ;
@cMensaje, LEN(cMensaje) ) # 0

		ERROR "Error no determinado en " ;
+ "Acceso telefónico a redes. (" ;
			+ LTRIM( STR( nResult ) ) + ")"
	ELSE

		ERROR "Acceso telefónico a redes: (";
			+ LTRIM( STR( nResult ) ) + ") " ;
+ ALLTRIM( cMensaje ) 

	ENDIF
	
	*** Colgar por si quedó inestable 
*** la conexión
	RasHangUp( nRasConn )
	
	*** Retorno con error -2
	RETURN -2

ENDIF

*** Retorno del manejador de la conexión
RETURN nRasConn

Debemos hacer notar que se está utilizando un programa llamado wordtoc para convertir un número a un tipo de dato DWORD capaz de introducirse en el buffer que utilizamos como sustituto de la estructura C original. En otros ejemplos utilizaremos el programa ctoword para poder obtener un número desde el buffer. Este es el código fuente de ambos programas:

*** WORDTOC
*** Combierte un número en un buffer
PROCEDURE WORDTOC
LPARAMETER nNumber
RETURN CHR(BITAND(255,nNumber)) ;
	+ CHR(BITAND(65280,nNumber)%255) ; 
	+ CHR(BITAND(16711680,nNumber)%255) ;
	+ CHR(BITAND(4278190080,nNumber)%255)

*** CTOWORD
*** Combierte un buffer en un número
PROCEDURE CTOWORD
LPARAMETER cBuffer
RETURN ASC(SUBSTR(cBuffer,1,1)) ;
	+ ASC(SUBSTR(cBuffer,2,1))*256 ;
	+ ASC(SUBSTR(cBuffer,3,1))*65536 ;
	+ ASC(SUBSTR(cBuffer,4,1))*16777216 

Cerrar una conexión RAS

Una aplicación elegante no deja sus conexiones abiertas una vez que no le son necesarias o cuando la aplicación se cierra. Lo mejor es preguntarle al usuario si desea que cerremos la conexión o bien desea mantenerla abierta. Para cerrar una conexión abierta con RasDial utilizaremos la siguiente función:

DECLARE Integer RasHangUp ;
	IN RASAPI32.DLL ;
	Integer   hRasConn

Esta función requiere el manejador de la conexión y colgará la línea. Es necesario esperar algunos segundos antes de volver a utilizar la línea, pues es habitual que ésta no se encuentre disponible inmediantemente despues de ser colgada.

Como ejemplo hemos realizado un pequeño programa que corta la conexión de la que pasemos el manejador, esperando 3 segundos antes de devolver el control.

******************************************
***
*** Cuelga una conexión realizada y de la 
*** que dispongamos un manejador
***
******************************************
LPARAMETER nRasConn

*** Declaración de las funciones del API DECLARE Integer RasHangUp ;
	IN RASAPI32.DLL ;
	Integer   hRasConn

DECLARE Integer Sleep ;
	IN Win32API ;
	Integer   Milliseconds

LOCAL nResult

*** Realizar el cierre de la línea
nResult = RasHangUp( nRasConn )

*** Realizar una pausa de tres segundos
*** para asegurar el colgado
	=Sleep(3000)
ENDIF

*** Retornar lo devuelto por la función
RETURN nResult

Obtener las conexiones ya establecidas

Antes de abrir una conexión por Dial-Up Networking es necesario saber si ya existía una conexión con esa configuración y de esta forma evitar intentar realizar dos veces la misma conexión. Para saber qué conexiones están abiertas se utiliza la siguiente función del API de RAS:

DECLARE INTEGER RasEnumConnections ;
	IN RASAPI32.DLL ;
	STRING  @ RasConnectionsBuffer, ;
	INTEGER @ dwSize, ;
	INTEGER @ nCount

Esta función utiliza una estructura tipo matriz de estructuras RASCONN. Como en los casos anteriores debemos crear un buffer para contener esta matriz de estructuras y luego obtener los datos por la posición de los elementos de la estructura dentro del buffer.

Como ejemplo hemos creado un programa que crea una matriz con el nombre recibido como parámetro e inserta en la misma la información sobre las conexiones activas en ese momento.

******************************************
***
*** Construye una matriz con todas las
*** conexiones RAS abiertas
***
******************************************
LPARAMETER cArrayReturn

*** Declaración de las funciones del API
DECLARE INTEGER RasEnumConnections ;
	IN RASAPI32.DLL ;
	STRING  @ RasConnectionsBuffer, ;
	INTEGER @ dwSize, ;
	INTEGER @ nCount

DECLARE INTEGER RasGetErrorString ;
	IN RASAPI32.DLL;
	INTEGER   ErrorValue, ;
	STRING  @ MessageBuffer, ;
	INTEGER   BufferLen

*** Creación de una estructura tipo RASCONN
cConexion = wordtoc(412) ;
+ REPLICATE( CHR(0), 408 )

*** Creación de una matriz con esa 
*** estructura
cConexiones = REPLICATE( cConexion, 16 )

*** Variable con el tamaño de la matriz
nSize = LEN( cConexiones )

*** Variable en la que se retorna el 
*** número de entradas encontradas
nCount = 0

*** Llamada a la función para obtener 
*** la información
nResult = RasEnumConnections( ;
@cConexiones, ;
@nSize, ;
@nCount )

*** Si existe alguna conexión abierta
IF nCount > 0

	*** Dimensión de la matriz como pública
	PUBLIC ARRAY &cArrayReturn.[nCount,4]

	*** Extracción de los datos desde la
*** matriz de estructuras
	FOR nPos = 0 TO nCount - 1

		*** Obtener una Estructura
		cConexion = SUBSTR( ;
cConexiones, ;
( nPos * 412 ) + 1, ;
412 )

		*** Handle
		&cArrayReturn.[nPos+1,1] = ctoword( ;
SUBSTR(cConexion,5,4))
		*** Nombre
		&cArrayReturn.[nPos+1,2] = STRTRAN( ;
SUBSTR(cConexion,9,257), CHR(0))
		*** Tipo
		&cArrayReturn.[nPos+1,3] = STRTRAN( ;
SUBSTR(cConexion,266,17),CHR(0))
		*** Device
		&cArrayReturn.[nPos+1,4] = STRTRAN( ;
SUBSTR(cConexion,283,129),CHR(0))

	NEXT

ENDIF

*** Retorno del número de elementos que 
**  se han encontrado

RETURN(nCount)

Algunas ideas

El espacio se acaba, pero es interesante mostrar dos cuadros de diálogo que se pueden encontrar en el disco de la revista y que ejemplifican el posible interface de una aplicación cuando esta utiliza Internet. Sólo son un esquema, pero puede servir de base para una aplicación.

Para configurar el sistema de marcado, si es que se utiliza este y no un Proxy o un Router, podemos mostrar un formulario similar a este:


 

Cuando se solicita permiso para realizar una conexión en la que realizará una comprobación de la versión de la aplicación y bajar posibles actualizaciones podría mostrar un mensaje similar a este:


 

Con estos detalles nuestra aplicación puede empezar a utilizar un sistema de Dial-Up Networking con la mayor facilidad.

El API aquí descrito no es más que una pequeña muestra de las posibilidades del RAS, pero de momento es suficiente para nuestros propositos. En los próximos artículos veremos como aprovechar esta conexión por medio de protocolos como FTP y HTTP.

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