SOAP y WSDL

En esta época en que Internet está en todas partes puede ser muy importante que nuestras aplicaciones puedan obtener datos que se encuentran disponibles para nosotros allí, en Internet, y para acceder a ellos solamente deberíamos saber como obtenerlos.

Y eso, es muy fácil de conseguir una vez que entendemos los conceptos. Veamos:

  1. Hay computadoras en Internet que actúan como Servidores de WebServices. O sea que el WebService nos lo provee un Servidor al cual le debemos enviar una solicitud/petición y él nos devolverá una respuesta. Es exactamente lo mismo que hacemos cuando usamos Cliente/Servidor con SQL en nuestras aplicaciones.
  2. Cada Servidor puede aceptar muchas solicitudes distintas y debemos decirle cual de esas distintas solicitudes es la que nos interesa. Es el equivalente a una biblioteca de funciones, hay muchas funciones en esa biblioteca ¿cuál de ellas queremos ejecutar?
  3. La solicitud se la enviamos al Servidor usando un formato llamado WSDL (Web Services Description Language) que está basado en XML y que se transmite por HTTP. Entonces, para entenderlo mejor: la solicitud es una URL que finaliza con las letras wsdl (bueno, no necesariamente, pero es el caso que veremos en este artículo)
  4. Si el estado que el Servidor nos retornó es el número 200 eso significa que está todo ok. Cualquier otro número indicará que ocurrió un error.
  5. Si el estado que el Servidor nos retornó es el número 200 entonces en su respuesta tendremos el dato (o los datos, porque pueden ser muchos) que habíamos solicitado.

Entonces lo que debemos hacer es:

  1. Crear la solicitud de ejecución de una función del WebService. A esto se le llama XML REQUEST
  2. Enviar el XML REQUEST al Servidor (o sea que se le envía una URL que termina con las letras wsdl)
  3. Esperar hasta recibir la respuesta del Servidor. A lo recibido se le llama XML RESPONSE
  4. Procesar esa respuesta para extraer el dato (o los datos) que necesitamos

Paso 1. Crear la solicitud de ejecución de una función del WebService

¿Cómo sabemos cuál debe ser el contenido de la solicitud? Evidentemente que cada solicitud será distinta, no es lo mismo preguntarle a un Servidor por la temperatura actual en mi ciudad que preguntarle a otro Servidor cual es la traducción de una palabra inglesa al idioma castellano.

Para responder a esa pregunta tenemos dos alternativas:

  1. Preguntarle al proveedor del WebService o buscar en su página web
  2. Usar un programa que realice esa tarea
  • El autor de este blog prefiere usar un programa porque no solamente le muestra la solicitud que se debe enviar también le permite probarla y verificar su funcionamiento y recibir la respuesta. Hay varios programas que realizan esas tareas y por lo tanto cual elegir es según el gusto de cada uno. El más popular y muy completo y además gratuito se llama SoapUI y será el utilizado en estos artículos sobre WebServices.

Después de descargar e instalar al SoapUI cuando lo ejecutemos veremos algo asi:

Captura 1. La pantalla inicial del SoapUI

A continuación debemos decirle de cual URL queremos obtener el formato de la solicitud que necesitamos:

Captura 2. Para pedirle al SoapUI que nos muestre las solicitudes que hay en esa URL

En la Captura 2. puedes ver lo que debes hacer para que el SoapUI te muestre todas las solicitudes de ese WebService (o sea, todas las funciones que tienes disponibles para usarlas). La cantidad de solicitudes disponibles varía según cada Servidor, podrías tener una, dos, tres, o cualquier otra cantidad.

Captura 3. Los pasos a seguir para obtener la solicitud que te interesa

En la Captura 3. puedes ver lo que debes hacer para ejecutar un WebService usando al programa SoapUI. Esto es muy importante hacer porque así te asegurarás que funciona bien y que obtienes un valor correcto.

Captura 4. Enviando la solicitud del WebService y obteniendo la respuesta

En la Captura 4. puedes ver que debes reemplazar los símbolos de ? por números, luego hacer clic en el icono de ejecutar y en instantes obtendrás la respuesta del Servidor. En este caso la ejecución fue exitosa pero si algo en la solicitud está mal (por ejemplo, pusiste letras en lugar de números) lo que obtendrás será un error y podrás saber que hay un error porque verás <soap:Fault> (bueno, no necesariamente pero es lo usual).

Captura 5. Si hay un error en los parámetros el WebService nos avisa con un <soap:Fault>

NOTA: No todos los Servidores envían la descripción del error en <soap:Fault> pero lo normal es que sí lo hagan.

Paso 2. Enviar el XML REQUEST al Servidor

En los paneles izquierdos de la Captura 4. y de la Captura 5. vemos las solicitudes (XML REQUEST) enviadas al Servidor y en los paneles derechos vemos las respuestas (XML RESPONSE) recibidas.

En ambos casos lo que se envía y lo que se recibe son variables de tipo string. Que desde luego podemos convertirlas a archivos de texto si lo deseamos.

Hasta ahora todo lo hicimos usando al programa SoapUI ¿y cómo podemos hacer lo mismo desde Visual FoxPro? Pues con las siguientes líneas de código:

LOCAL lcURL_WSDL, lcMiSolicitud, lcSuRespuesta, lcRespuestaWS, loHTTP

lcURL_WSDL    = "la URL que me interesa"
lcMiSolicitud = "el string con formato XML que contiene mi solicitud"
lcSuRespuesta = "MiArchivo.XML"     && La respuesta recibida convertida en un archivo .XML

loHTTP = CreateObject("Msxml2.ServerXMLHTTP.6.0")     && conexión por HTTP

WITH loHTTP
  .OPEN("POST", lcURL_WSDL, .F.)
  .setRequestHeader("User-Agent", "Ejecutando un WebService")
  .setRequestHeader("Content-Type", "text/xml;charset=utf-8")
  *--- Se envía la solicitud al Servidor
  .SEND(lcMiSolicitud)
ENDWITH

Paso 3. Esperar hasta recibir la respuesta del Servidor

En esta época las respuestas a las solicitudes de WebServices suele ser rapidísima. Para obtenerla en Visual FoxPro podemos escribir algo como:

*--- Si la propiedad Status es 200 entonces está todo ok, si es cualquier otro número entonces ocurrió algún error
IF loHTTP.STATUS = 200 THEN
  lcRespuestaWS = loHTTP.responseText
  *--- Se genera un archivo con formato XML de la respuesta obtenida
  =StrToFile(StrConv(lcRespuestaWS, 9), lcSuRespuesta)     && 9: Convierte doble-bytes caracteres a UTF-8
ENDIF

NOTA: EL Servidor nos retorna un string pero ese string está en formato XML y por lo tanto podemos guardarlo como un archivo .XML

Paso 4. Procesar esa respuesta para extraer el dato (o los datos) que necesitamos

Recuerda que el WebService nos da como respuesta un string que contiene mucho texto y solamente una pequeña parte de todo ese texto corresponde al valor que estamos necesitando, el resto es basura. Por ejemplo:

Captura 6. De todo ese string solamente queremos obtener el número 14, todo lo demás está sobrando

Entonces, ¿cómo obtenemos el número 14 y desechamos todo lo demás?

Lo más sencillo es usando una función del Visual FoxPro llamada StrExtract() que retorna el texto que se encuentra entre dos delimitadores. Entonces al escribir:

  lcResultado = StrExtract(lcRespuestaWS, "<AddResult>", "</AddResult>")

en la variable lcResultado tendremos lo que buscábamos. O sea, al número 14.

IMPORTANTE: En este ejemplo los delimitadores fueron <AddResult> y </AddResult> pero en otros casos tendrás que usar otros delimitadores. ¿Cuáles delimitadores debes usar? eso lo sabrás mirando el resultado que te devuelve el Servidor de WebServices (en la Captura 4. vemos que texto está antes y después de la respuesta, o sea del número 14, y esos textos son los que debemos usar como delimitadores).

NOTA: No es la única forma de obtener la respuesta que nos envió el Servidor, también podríamos usar una función del Visual FoxPro llamada XMLTOCURSOR() para obtener esa respuesta. Cuando los datos a recibir son muchos esa puede ser la mejor alternativa.

Conclusión

Es muy fácil desde Visual FoxPro consumir WebServices (si se sabe como hacerlo, claro) y en esta época es muy importante para darle un buen valor agregado a nuestras aplicaciones. En este primer artículo de la serie sobre consumir WebServices hemos visto los conceptos generales para tener claras las ideas.

¿Y qué haríamos si queremos consumir WebServices desde Visual FoxPro?

Lo más práctico y lo más eficiente es crear una clase que se encargue de esa tarea.

En el archivo .ZIP que puedes descargar encontrarás a esa clase junto con varios programas de ejemplo. En el siguiente artículo de esta serie se explicará como funcionan la clase y los ejemplos para que todos los conceptos te queden más claros y ya te sea muy fácil consumir WebServices desde Visual FoxPro.

IMPORTANTE: En este artículo y en el siguiente se usan SOAP (Simple Object Access Protocol) y WSDL (Web Services Description Language) para consumir WebServices porque gracias al programa SoapUI es muy fácil obtener la solicitud que debemos enviar al Servidor pero ambos se están usando cada vez menos. La razón es que SOAP es tedioso para analizarlo, es difícil de leer, y ocupa mucho ancho de banda. Por eso es que ahora se prefiere usar ReST con JSON, lo cual será tema de artículos posteriores.

Descarga

Puedes descargar un archivo .ZIP que contiene a una clase para consumir WebServices y varios ejemplos de uso desde:

NOTA: Todos los ejemplos funcionan perfectamente en la fecha de publicación de este artículo pero no puedo asegurar que siempre será así porque todos son gratuitos y los propietarios de los WebServices podrían darlos de baja si se les antoja. Entonces, cuanto antes los pruebes, mucho mejor.

Artículos relacionados

Aplicaciones en la SysTray (1)

Colocar aplicaciones en la barra de tareas del Windows (más conocida como la SysTray) puede llegar a ser muy útil y muy conveniente.

Captura 1. La barra de tareas (SysTray) del Windows

Podemos usarla para que nuestra aplicación esté siempre activa, sin importar lo que haga el usuario, de esa manera podríamos hacer backups automáticos, verificaciones, descargas de archivos desde Internet, envíos de e-mails, avisarle que ocurrió algo que requiere de su atención, y un largo etcétera.

Y con Visual FoxPro es extremadamente fácil conseguir que nuestros programas se ubiquen allí. Quienes no lo saben, suponen que es algo muy difícil, pero no, es súper sencillo de lograr.

Lo que necesitaremos es una biblioteca de clases llamada SYSTRAY.VCX que viene incluida con el Visual FoxPro. La encontrarás en la carpeta:

C:\Program Files (x86)\Microsoft Visual FoxPro 9\Samples\Solution\Toledo\

Y lo mejor, es que en esa misma carpeta encontrarás un formulario de ejemplo. Mirando ese formulario podrás leer casi todo lo que necesitarás saber. El nombre del formulario es:

systray_sample.scx

Creando una aplicación que se ubicará en la barra de tareas del Windows

Hay dos formas de usar a la biblioteca de clases SYSTRAY.VCX:

  1. En un programa (.PRG)
  2. En un formulario (.SCX)

Si la queremos usar en un .PRG entonces crearemos un objeto con la función CREATEOBJECT(), en cambio si la queremos usar en un formulario insertaremos un control systray en nuestro formulario.

Entendiendo como funciona el menú en la barra de tareas del Windows

Lo que colocamos en la barra de tareas del Windows es un menú. No es un programa ni un formulario, sino un menú. O sea que en tu formulario podrías tener todos los controles que quieras, pero ninguno de ellos se verá en el menú.

¿Y por qué usar un formulario para colocar al menú en la barra de tareas?

A veces es cuestión de gustos y a veces puede facilitarte tu trabajo, porque tienes en el formulario propiedades o métodos que necesitarás. Pero lo importante a recordar es que puedes usar indistintamente un archivo .PRG o un formulario, ya que con cualquiera de ellos funcionará.

Estableciendo los valores de las propiedades:

Después que creamos el objeto en nuestro archivo .PRG o en nuestro formulario, hay 4 propiedades cuyos valores debemos establecer para que nuestra aplicación se ubique en la barra de tareas del Windows, las 4 propiedades son obligatorias:

  • IconFile
  • MenuText
  • MenuTextIsMPR
  • TipText

IconFile es el nombre de un archivo gráfico de icono. Es el icono que el usuario verá en la barra de tareas.

MenuText puede ser un string conteniendo las opciones o el nombre de un archivo .MPR

MenuTextIsMPR indica si MenuText es un string o si es un archivo .MPR

TipText es el mensaje que verá el usuario cuando mueva su cursor sobre el icono

Listado 1. Un ejemplo donde se establecen esas 4 propiedades:

IconFile      = "MI_ICONO_TAREAS_1.ICO"
MenuText      = "1;Bloc de Notas;2;Calculadora;3;Paint;4;Mensaje;5;Procedure;6;Salir"
MenuTextIsMPR = .F.
TipText       = "Ejemplo de un programa en la barra de tareas del Windows"

Captura 2. Al colocar el mouse sobre el icono, se muestra un texto de ayuda

En la propiedad IconFile se escribió el nombre de un archivo de icono; en la propiedad MenuText se escribieron las opciones que estarán visibles para el usuario, en todos los casos es: un número, seguido de un punto y coma, seguido de un texto, y finalizado con un punto y coma. Los números pueden ser cualesquiera, el Visual FoxPro los usa para identificar a las opciones. En este ejemplo, se usaron los números 1, 2, 3, 4, 5, y 6, pero tú puedes usar cualquier número que se te ocurra. Como las opciones que verá el usuario se escribieron en la propiedad MenuText y no en un archivo .MPR entonces en la propiedad MenuTextIsMPR se colocó .F., y finalmente en la propiedad TipText se escribió el texto que ve el usuario cuando coloca su mouse sobre el icono, tal y como se puede ver en la Captura 2. (El icono de este ejemplo es un ojo)

Mostrando a nuestra aplicación en la barra de tareas del Windows

Captura 3. El menú que ve el usuario cuando hace clic en el icono

Especificando un archivo .MPR que contiene las opciones del menú

En el Listado 1. las opciones se escribieron directamente en un string, pero también podemos escribir las opciones en un archivo .MPR si queremos, y quizás te preguntarás ¿para qué haríamos eso? la respuesta es que en ese caso tu menú podría tener distintos colores y tipos de letras y tamaños de letras y por lo tanto sería más vistoso, llamaría más la atención del usuario.

Captura 4. Un menú con varios colores en las opciones

En la Captura 4. vemos un menú cuyas opciones se muestran en distintos colores. En general no es buena idea exagerar con eso, pero se muestra ahí para que veas algunas de tus posibilidades.

Captura 5. Un menú con colores, pero más sobrio que el anterior

Si comparas la Captura 3., con la Captura 4., y la Captura 5., notarás que tienen varias diferencias: distintos tipos de letras, distintos tamaños de letras, distintos colores.

Es por ese motivo que muchos programadores preferimos usar un archivo .MPR para mostrar las opciones del menú, ya que nos permite ser más creativos. El menú de la Captura 3. parece muy aburrido. Aunque sobre gustos…

¿Cómo ejecutamos la opción que eligió el usuario?

Ya le hemos mostrado al usuario cuales son sus opciones disponibles, él hizo clic sobre una de esas opciones, ¿cómo la ejecutamos?

Bien, eso depende de si su menú está en un archivo .MPR o no lo está. Si no está en un archivo .MPR entonces en el procedure ProcessMenuEvent()  de nuestro objeto systray colocamos los comandos. Algo así:

Listado 2. Ejecutando la opción elegida, cuando no se usa un archivo .MPR

PROCEDURE ProcessMenuEvent     && Aquí se debe procesar la opción elegida por el usuario
LPARAMETERS tnMenuItemID

  DO CASE
    CASE tnMenuItemID = 0     && Salió sin elegir opción, nada se debe hacer entonces
    CASE tnMenuItemID = 1     && Eligió la primera opción
      =ShellExecute(0, "OPEN", "NOTEPAD.EXE", "", "", 1)
    CASE tnMenuItemID = 2     && Eligió la segunda opción
      =ShellExecute(0, "OPEN", "CALC.EXE", "", "", 1)
    CASE tnMenuItemID = 3     && Eligió la tercera opción
      =ShellExecute(0, "OPEN", "MSPAINT.EXE", "", "", 1)
    CASE tnMenuItemID = 4     && Eligió la cuarta opción
      =MessageBox("Un mensaje de bienvenida")
    CASE tnMenuItemID = 5     && Eligió la quinta opción
      DO MI_PROCEDURE_SALUDA
    CASE tnMenuItemID = 6     && Eligió la sexta opción
      This.RemoveIconFromSystray()
      CLEAR EVENTS
    ENDCASE

ENDPROC
*
*

Los números que se encuentran después de la variable tnMenuItemID deben coincidir con los números que escribimos en la propiedad MenuText. Si el valor de la variable tnMenuItemID es cero, eso significa que el usuario salió sin elegir alguna de las opciones.

IMPORTANTE: Para que se termine el procesamiento, se debe escribir el comando CLEAR EVENTS

Como puedes ver, nada hay de raro en el Listado 2., ya que se trata de un simple procedure con una construcción CASE…ENDCASE dentro suyo. Lo único realmente importante que debes recordar es que si quieres darle al usuario la opción de terminar tu aplicación, debes escribir el comando CLEAR EVENTS en alguna de las opciones.

Descarga:

Para que todo lo visto hasta aquí te quede más claro y lo puedas ver funcionando en la vida real, puedes descargar un archivo .ZIP que contiene todo lo que necesitas para mostrar una aplicación en la barra de tareas del Windows cuando no se usa un archivo .MPR y en el siguiente artículo tendrás la posibilidad de descargar un archivo .ZIP que te mostrará como hacer cuando  quieres usar un archivo .MPR

https://www.mediafire.com/file/zx0lidv48koqj2r/SYSTRAY_1.ZIP/file

Conclusión:

Algo que no es muy frecuente que hagan los programadores de Visual FoxPro pero que sin dudas es muy útil es colocar aplicaciones en la barra de tareas del Windows. Quizás una de las razones es que desconocen como hacerlo o piensan que es algo muy difícil y complicado. En realidad, es muy fácil.

En este artículo solamente hemos visto una pincelada de lo que se puede lograr, en siguientes artículos profundizaremos más con este interesante tema.

Artículos relacionados:

Entendiendo a ShellExecute

Aplicaciones en la SysTray (2)

Aplicaciones en la SysTray (3)

Aplicaciones en la SysTray (4)

El índice del blog VFPavanzado

 

De DBF a SQL (27). Entendiendo Cliente/Servidor

Hasta ahora, todo lo que hemos hecho en esta serie de artículos podíamos realizarlos con las tablas .DBF, nativas del Visual FoxPro. Eso está muy bien y podríamos quedarnos allí porque como habrás comprobado si pusiste en práctica los ejemplos con tus propias tablas, hay una muy notoria ganancia en velocidad. Escribes menos y obtienes los resultados mucho más rápidamente. Está muy bueno eso ¿verdad?. Pero no nos  quedaremos allí sino que daremos un gran salto hacia adelante y nos adentraremos en una tecnología muy distinta a la tradicionalmente usada en Visual FoxPro pero que a su vez es extremadamente poderosa. Su nombre es Cliente/Servidor y es más o menos el equivalente a que un jugador de fútbol de un club sudamericano de mediano porte vaya a jugar al Barcelona de España o al Real Madrid o algo así. Es totalmente otro nivel de juego.

Entonces, ¿de qué se trata Cliente/Servidor?

Lo primero que debes saber es que esta es una metodología para acceder a los datos desde varias computadoras, en otras palabras, desde una red de computadoras. Aunque puedes hacer aplicaciones Cliente/Servidor que sean monousuario no fue pensado para eso sino para aplicaciones multiusuario.

En las aplicaciones antiguas (o sea, las tradicionales del VFP) los programas “tocaban” físicamente a los datos, o sea que por un lado estaba el programa, por el otro lado estaban los datos que se guardaban dentro de los archivos y no había algo entre ellos que los separara.

Programa <——–> Archivo de datos

Esto funcionaba, pero con muuuchos problemas potenciales, por ejemplo un usuario estaba modificando un registro que otro usuario también estaba modificando. O un usuario estaba borrando un registro que otro usuario necesitaba en su informe. Eso causaba muchos inconvenientes. Mientras no había algo mejor los usuarios debían conformarse con eso … pero ahora tenemos Cliente/Servidor.

¿Cómo funciona Cliente/Servidor?

En esta metodología los programas jamás “tocan” físicamente a los datos, aunque quieran hacerlo no podrán, es totalmente imposible. En lugar de eso, los programas se comunican con el Cliente, el Cliente le envía una petición al Servidor, y es el Servidor quien procesa esa petición y le envía la respuesta al Cliente y el Cliente se la envía de regreso al programa.

Programa —–> Cliente —–>Servidor

Servidor —–> Cliente —–> Programa

Como ves, el camino es mucho más largo pero también es muchísimo más seguro y confiable. El Cliente es el intermediario. Nada puede hacerse sin él.

Entonces, el Servidor se instala en una computadora. ¿En cuál computadora? En la computadora donde estarán alojadas todas las bases de datos.

El Cliente se instala en todas las demás computadoras, o sea en las computadoras que usarán los usuarios.

¿Y cómo se comunica el Cliente con el Servidor, y viceversa? Para que la comunicación entre ellos sea posible se necesita de una canal de comunicación. Hay muchos canales de comunicación, entre ellos: ADO, JDBC, ODBC, .NET, .PYTHON, etc. El más usado es el ODBC, pero tú puedes optar por cualquier otro, si así lo prefieres.

Cuando un programa necesita algo se lo pide al Cliente y éste se lo pide al Servidor. ¿Qué puede necesitar un programa? Veamos algunos ejemplos:

  • Grabar la venta que corresponde a la Factura Nº 12345, con fecha de hoy, le vendimos a Juan Pérez y el monto de la venta fue de 1.250 dólares
  • Grabar la cobranza que hoy le hicimos a María Benítez, según el Recibo Nº 785 y por un monto de 420 dólares
  • Cambiar el precio de venta de los monitores Toshiba de 21″, el nuevo precio es 100 dólares
  • Ver todas las ventas que hicimos el mes pasado
  • Ver los nombres de todos los vendedores que hoy hicieron ventas por más de 500 dólares

En todos estos casos el programa se lo pide al Cliente, el Cliente se lo pide al Servidor y el Servidor trata de cumplir con el pedido. Si pudo cumplir se lo indica al Cliente enviándole un número 1. Si no pudo cumplir le envía el número -1. Y si aún no terminó de procesar el pedido le envía un 0 (cero) y así el Cliente sabe que el Servidor todavía está trabajando.

Por lo tanto, cuando recibe un 1 el Cliente sabe que su petición tuvo éxito. Y si recibe un -1 el Cliente sabe que su petición falló. Súper fácil ¿verdad?

Si la petición tuvo éxito entonces el Cliente recibe un “cursor”. ¿Y qué es un cursor? ya lo sabes, porque ya lo hemos descrito en artículos anteriores de esta serie. Un cursor es una tabla de memoria, una tabla virtual, una copia del contenido que hay en el Servidor.

Eso significa que si modificas el cursor la Base de Datos ni se entera, su contenido queda exactamente igual a como estaba antes. ¿Por qué? porque el cursor es una copia de lo que está dentro de la Base de Datos (es como fotocopiar un libro, si escribes sobre la fotocopia el libro original continúa igual, allí no verás lo que escribiste en la fotocopia. En Cliente/Servidor siempre trabajas con fotocopias y jamás puedes tocar al libro original).

Resumiendo:

  • El Servidor se instala en una sola computadora (en la que se guardarán todas las bases de datos)
  • El Cliente se instala en muchas computadoras (en las que trabajarán los usuarios)
  • Para que el Cliente y el Servidor puedan comunicarse entre sí se necesita de un canal de comunicación entre ellos (por ejemplo el driver ODBC)
  • El Cliente envía peticiones al Servidor, el Servidor procesa esas peticiones y luego le envía el resultado al Cliente
  • El Cliente sabe que su petición tuvo éxito si recibe como respuesta el número 1. Si la respuesta es -1 entonces la petición falló. Si la respuesta es 0 entonces el Servidor aún continúa procesando los datos
  • Si el Cliente recibió un 1 porque la petición tuvo éxito entonces también recibe un “cursor”, o sea una tabla de memoria, donde se encuentra la respuesta enviada por el Servidor (por ejemplo, los nombres de todos los vendedores que hoy vendieron por un monto superior a 500 dólares)
  • Ni la aplicación (el sistema de contabilidad, de ventas, de tesorería, de cobranzas, etc.) ni el Cliente jamás tocan a los datos, solamente el Servidor puede hacer eso.
  • El Servidor sólo toca a los datos si recibe un pedido del Cliente para hacerlo. Si el Cliente no se lo pide, el Servidor jamás toca a los datos.

Ventajas de usar Cliente/Servidor

Control centralizado: Para poder acceder a una Base de Datos se necesita el nombre de un usuario y su contraseña y los permisos (también llamados derechos o privilegios) correspondientes. Una tabla .DBF puede abrirla cualquiera que conozca aunque sea un poquito de Visual FoxPro. Una tabla SQL no. Solamente podrá abrirla si tiene autorización para hacerlo.

Ocultamiento: Para abrir una tabla .DBF se requiere conocer en cual computadora se encuentra, en cual carpeta de esa computadora, y cual es el nombre de esa tabla .DBF. En buenos motores como Firebird SQL, no es necesario tener toda esa información. Podrías pedir conectarte a MISERVIDOR/MIBASEDATOSCOLEGIO y si previamente el Administrador de la Base de Datos definió a cual computadora se la conoce como MISERVIDOR y a cual Base de Datos de esa computadora se la conoce como MIBASEDATOSCOLEGIO, podrías realizar la conexión sin tener la menor idea del nombre real de la Base de Datos (podría ser DARWIN.FDB, por ejemplo) ni cual es la computadora (podría ser una computadora local, la que tiene el IP 192.168.0.25, por ejemplo; o podría ser una computadora remota, la que tiene el IP 181.120.90.145, por ejemplo).

Seguridad: Como consecuencia directa de los dos puntos anteriores, la seguridad que se puede obtener es muy grande. Si por ejemplo a un usuario solamente se le otorgó el derecho de hacer SELECT a la tabla ALUMNOS, él no podrá causarle un daño a la Base de Datos aunque lo intente y lo reintente. Primero, porque con un SELECT jamás podría causar daño, y segundo porque como ya vimos quizás ni siquiera sabe en cual computadora se encuentra la Base de Datos, ni en cual carpeta, ni cual es el nombre real de esa Base de Datos.

Velocidad: Los Servidores SQL están optimizados para conseguir una muy buena velocidad de respuesta. ¿Visual FoxPro te parece rápido? Si crees eso entonces es seguro que no has probado un buen motor SQL, tal como Firebird. El autor de este blog aún recuerda cuando un proceso que con tablas .DBF se demoraba más de 20 minutos en realizar, con Firebird finalizaba en 5 ó 6 segundos, es otro mundo.

Escalabilidad: Al mejorar el hardware de la computadora donde se encuentra el Servidor se puede conseguir un gran incremento en la velocidad de las respuestas.

Menos fallas: En las tablas .DBF un problema eterno es que pueden corromperse. Un corte de la energía eléctrica o de la conexión con el Servidor pueden dañar a las tablas de datos o a los archivos de índice. En un buen motor SQL (ya lo sabes: como Firebird) tal cosa no puede ocurrir. Aunque se corte la energía eléctrica 100 veces en un día, ninguna tabla de la Base de Datos será dañada.

Backups en cualquier momento: las tablas .DBF no se pueden copiar mientras están siendo usadas, primero debes cerrarlas y luego copiarlas. Con un buen motor SQL (y sí…Firebird) puedes realizar los backups cuando se te ocurra, ningún problema.

Mejor desarrollo de aplicaciones: como ya no debes escribir tanto código y además no debes perder tanto tiempo pensando en los algoritmos correctos, y haciendo un larguísimo proceso de prueba/error entonces puedes concentrarte en lo realmente importante: que tu aplicación se vea mejor, sea más fácil de usar, sea más completa. Esto te posibilitará aumentar las ventas y por lo tanto, ganar más dinero.

Conclusión:

Cambiar de la forma tradicional de programar a Cliente/Servidor no es algo que se puede conseguir de la noche a la mañana pero las ventajas de realizar ese cambio son muy grandes y deberían muy seriamente ser analizadas.

Porque en resumidas cuentas lo que se conseguirá será trabajar menos y obtener mejores resultados.

Artículos relacionados:

De DBF a SQL (1). Introducción

De DBF a SQL (2). Reemplazando comandos

De DBF a SQL (3). Entendiendo a los cursores

De DBF a SQL (4). Ejemplos de usar SELECT con una sola tabla

De DBF a SQL (5). Las funciones agrupadas

De DBF a SQL (6). Agrupando filas

De DBF a SQL (7). Poniéndoles condiciones a los grupos

De DBF a SQL (8). Ordenando las filas

De DBF a SQL (9). Uniendo tablas

De DBF a SQL (10). Obteniendo las primeras filas de una tabla

De DBF a SQL (11). Relacionando una tabla con otra tabla

De DBF a SQL (12). Relacionando una tabla consigo misma

De DBF a SQL (13). Relacionando a varias tablas entre sí

De DBF a SQL (14). Más sobre el uso de DISTINCT

De DBF a SQL (15). Usando subconsultas

De DBF a SQL (16). Más sobre las subconsultas

De DBF a SQL (17). Ejemplos de subconsultas

De DBF a SQL (18). Paginación

De DBF a SQL (19). Hallando los porcentajes sobre un total

De DBF a SQL (20). Los operadores especiales de comparación

De DBF a SQL (21). La técnica de los dos cursores

De DBF a SQL (22). Optimizando el uso del operador de comparación especial IN

De DBF a SQL (23). Usando la función CAST()

De DBF a SQL (24). Buscando texto

De DBF a SQL (25).  Creando tablas y cursores

De DBF a SQL (26). INSERT, UPDATE, y DELETE, con subconsultas

De DBF a SQL (28). Conectándose a un Servidor

De DBF a SQL (29). Las cadenas de conexión ODBC a los motores SQL más populares 

De DBF a SQL (30). Conectándose mediante ADO a las bases de datos

De DBF a SQL (31). Optimizando la escritura de los SELECT

De DBF a SQL (32). Mejorando la estética de los SELECT 

De DBF a SQL (33). Usando la función SQLEXEC()

De DBF a SQL (34). Enviándole parámetros a la función SQLEXEC()

El índice del blog VFPavanzado

 

Registrando archivos OCX y DLL

Muchas veces nuestras aplicaciones necesitan para su correcto funcionamiento algunos archivos OCX o librerías DLL y por lo tanto necesitamos que estén registrados antes de que el usuario ejecute nuestra aplicación.

Si un OCX o un DLL no están registrados en la computadora del usuario, entonces no se los puede utilizar.

En versiones antiguas del sistema operativo Windows la registración era muy sencilla, sin embargo a partir de Windows Vista se necesita tener derechos de Administrador para poder registrar.

Además, la registración también puede fallar. Los motivos más comunes son:

  1. No tenías derecho de Administrador
  2. El archivo que quisiste registrar no existe

Listado 1. Intentando registrar un archivo OCX

Local lnResultado

  lnResultado = REGISTRAR_OCX("E:\SISTEMAS\MSCHRT20.OCX")

  do case
    case lnResultado = 0
      =MessageBox("Registración OK")
    case lnResultado = 1
      =MessageBox("No existe el archivo que quisiste registrar")
    case lnResultado = 2
      =MessageBox("No se pudo registrar, error desconocido")
    otherwise
      =MessageBox("No se pudo registrar, no lo ejecutaste como Administrador")
  endcase

Return
*
*
FUNCTION REGISTRAR_OCX
LParameters tcNombreOCX
Local lnNumError

  lnNumError = 0

  TRY
    DECLARE LONG DllRegisterServer IN (tcNombreOCX)
  CATCH
    lnNumError = 1
  ENDTRY

  IF lnNumError = 0
    TRY
      lnNumError = DLLRegisterServer()
    CATCH
      lnNumError = 2
    ENDTRY
  ENDIF

Return(lnNumError)
*
*

En el Listado 1. se trata de registrar el archivo «E:\SISTEMAS\MSCHRT20.OCX» y luego se muestra un mensaje informando si se realizó con éxito o no.

La función REGISTRAR_OCX() es la que intenta realizar la registración.

NOTA: Aunque la función se llama REGISTRAR_OCX() también sirve para registrar archivos DLL.

Recuerda que para que funcione, debes ejecutar a ese programa con derechos de Administrador. Es por lo tanto una buena idea crear un archivo .EXE que se encargue de registrar a todos los OCX y todas las DLL que nuestra aplicación necesita y ejecutar a ese archivo .EXE como Administrador.

El mejor momento es cuando instalamos nuestra aplicación. Nuestra aplicación normalmente no necesita ser ejecutada con derechos de Administrador pero el programita .EXE que registra a los OCX y a las DLL sí que necesita esos derechos.

Artículos relacionados:

El índice del blog VFPavanzado

 

Como crear una barra de progreso personalizada

Las barras de progreso, también llamadas termómetros por algunas personas, sirven para indicarle al usuario que se está realizando un proceso y cuanto de ese proceso ya se ha completado.

Son muy útiles, porque entonces el usuario sabe que la aplicación está trabajando y que no se quedó «colgada». Si un proceso demora varios minutos y no le indicamos visualmente al usuario que se está ejecutando ese proceso, entonces más de uno creerá que la aplicación «se colgó» y apretará el botón de Reset o las teclas CTRL+ALT+DEL.

El Visual FoxPro trae por defecto una barra de progreso que podemos insertar en nuestros formularios. La tendremos disponible si en el menú principal del Visual FoxPro: Tools | Options | Controls | Microsoft Progress Bar | clic en el checkbox | Ok

Y para insertarla en un formulario: View | Form Controls Toolbar | ActiveX Controls | Microsoft Progress Bar Control

Captura 1. Si haces clic en la imagen la verás más grande

En la Captura 1. se han insertado dos barras de progreso. En la de arriba la propiedad Scrolling = 0 – Standard Scrolling y en la de abajo la propiedad Scrolling = 1 – Smooth Scrolling

Pero eso es prácticamente todo lo que podemos hacer con las barras de progreso, no podemos siquiera cambiarles el color.

Entonces, si queremos otra barra de progreso tenemos dos posibilidades: 1) Buscar una barra de progreso hecha por una tercera persona, 2) Crear nuestra propia barra de progreso.

En este artículo examinaremos esa segunda posibilidad.

Listado 1. Crear la clase que mostrará una barra de progreso

CREATE CLASS PROGRESO OF CONTROLES AS CONTAINER

En el Listado 1. la clase se llama PROGRESO y se encontrará en la biblioteca de clases llamada CONTROLES, pero esos son ejemplos, tú puedes usar cualquier nombre que quieras.

Captura 2. Si haces clic en la imagen la verás más grande

Listado 2. Las propiedades de la clase PROGRESO

BackStyle   = 0 - Transparent
BorderWidth = 0
Width       = 580

A continuación, insertamos dos controles SHAPE.

Listado 3. Las propiedades del control Shape1

BackColor = 255, 255, 192
BackStyle = 1 - Opaque (Default)
Height    = 30
Left      = 0
Top       = 0
Width     = 580

Listado 4. Las propiedades del control Shape2

BackColor = 128, 128, 64
BackStyle = 1 - Opaque (Default)
Height    = 26
Left      = 2
Top       = 2
Width     = 100

Captura 3. Si haces clic en la imagen la verás más grande

Ahora insertamos un control Label, para que nos muestre el porcentaje progresado.

Listado 5. Las propiedades del control Label1

Alignment = 2 - Center
BackStyle = 2 - Transparent
Caption   = 0,00%
FontBold  = .T. - True
FontSize  = 14
ForeColor = 192, 192, 64
Height    = 21
Left      = 2
Top       = 3
Width     = 100

Captura 4. Si haces clic en la imagen la verás más grande

Siguiente paso, es agregarle la propiedad Value a la clase PROGRESO. Para ello, en el menú principal del Visual FoxPro: Class | New Property … | Name: Value | Default/Initial Value: 0 | Clic en Assign Method

Captura 5. Si haces clic en la imagen la verás más grande

Ahora que ya hemos insertado todos los controles que necesitamos y también le hemos agregado una propiedad a nuestra clase, el siguiente paso es escribir los métodos.

Listado 6. El método INIT() de la clase PROGRESO

  with This
    .Shape1.Width   = .Width
    .Shape2.Width   = 0
    .Label1.Caption = ""
    .Label1.Width   = .Shape1.Width - 4
  endwith

Return
*
*

Listado 7. El método VALUE_ASSIGN de la clase PROGRESO

LParameters tnNuevoValor
Local lnAncho

  tnNuevoValor = iif(tnNuevoValor < 0, 0, tnNuevoValor)
  tnNuevoValor = iif(tnNuevoValor > 100, 100, tnNuevoValor)

  with This
    lnAncho         = .Width - 4
    .Value          = tnNuevoValor
    .Label1.Caption = Transform(tnNuevoValor, "999.99") + "%"
    .Shape2.Width   = tnNuevoValor * lnAncho / 100
  endwith

Return
*
*

Finalmente, a nuestra clase PROGRESO le disminuimos el alto, porque ya no necesitamos que sea tan grande.

Listado 8. La propiedad Height de la clase PROGRESO

.Height = 30

Y listo, ya está, ya tenemos nuestra clase barra de progreso.

Captura 6. Si haces clic en la imagen la verás más grande

Para usarla, la insertamos en un formulario.

Captura 7. Si haces clic en la imagen la verás más grande

Y en el método CLICK() del CommandButton escribimos el código, algo como:

Listado 9. El método CLICK() del CommandButton

Local lnCantidadDatos, lnI, lnJ, lnPorcentaje

  lnCantidadDatos = 275    && La cantidad de registros, o de lo que queremos mostrar

  For lnI = 1 to lnCantidadDatos
    lnPorcentaje             = lnI * 100 / lnCantidadDatos
    ThisForm.Progreso1.Value = lnPorcentaje
    For lnJ = 1 to 100000     && Para que no se mueva tan rápido
    EndFor
  EndFor

Return
*
*

Y al ejecutar nuestro formulario, veremos algo como:

Captura 8. Si haces clic en la imagen la verás más grande

Captura 9. Si haces clic en la imagen la verás más grande

Y aquí viene lo interesante, como esta es nuestra barra de progreso entonces podemos hacerle todos los cambios que se nos ocurra, por ejemplo:

Captura 10. Si haces clic en la imagen la verás más grande

Captura 11. Si haces clic en la imagen la verás más grande

En la Captura 10. y en la Captura 11. se sustituyó al control Shape2 por un control IMAGE. Y con esa simple sustitución cambió totalmente la apariencia de nuestra barra de progreso.

Como ves, ya solamente depende de tu creatividad y de tu gusto estético para crear las barras de progreso que puedan atraer a tus usuarios.

Conclusión:

Si la barra de progreso que viene incluida con el Visual FoxPro no nos gusta, tenemos la opción de cambiarla por una barra de progreso propia, personalizada, para que tenga la apariencia que deseamos. Es muy fácil hacer algo así, como pudimos ver en este artículo.

Artículos relacionados:

El índice del blog VFPavanzado

 

Entendiendo a ShellExecute()

La función ShellExecute() es realmente muy útil y por eso es muy importante conocerla bien.

En primer lugar, debemos aclarar que no es una función del Visual FoxPro sino que es una función de la API del Windows. ¿Qué es la API del Windows? Las siglas API son las iniciales de Application Programming Interface o sea la Interfaz para la Programación de Aplicaciones en el sistema operativo Windows. Está compuesta por muchísimas funciones que realizan muchísimas tareas de utilidad para los programadores.

Una de esas funciones es ShellExecute()

¿Para qué sirve ShellExecute()?

Para ejecutar cualquier programa que se encuentre en la computadora del usuario.

Sintaxis de ShellExecute()

DECLARE INTEGER ShellExecute IN shell32.dll ; 
  INTEGER hndWin, ; 
  STRING cAction, ; 
  STRING cFileName, ; 
  STRING cParams, ;  
  STRING cDir, ; 
  INTEGER nShowWin

La función ShellExecute() se encuentra en una DLL (Dynamic Link Library) llamada SHELL32.DLL. Devuelve un número entero. Si ese número es mayor que 32, entonces la función finalizó con éxito. Si es menor que 32, ocurrió algún error.

Los parámetros que puede recibir son:

  • hndWin. Es un número entero que representa al handle del programa que llamó a la función. En Visual FoxPro lo normal es poner cero.
  • cAction. La acción que deseamos ejecutar (mira abajo para más detalles).
  • cFileName. El nombre del archivo o de otro objeto, en el cual la acción será ejecutada.
  • cParams. Si en cFileName pusimos el nombre de un programa ejecutable, estos son los parámetros que ese programa recibiría en la línea de comandos.
  • cDir. Si en cFileName pusimos el nombre de un programa ejecutable, esta es la carpeta (o directorio) por defecto, es decir donde se iniciará.
  • nShowWin. El estado inicial de la ventana (1=normal, 2=minimizada, 3=maximizada).

Ejemplo 1. Abrir un archivo .ZIP

lcAction   = "open"
lcFileName = "E:\BACKUPS\CONTABILIDAD\CONTA2017-10-10.ZIP"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 2. Abrir una planilla EXCEL

lcAction   = "open"
lcFileName = "E:\EXCEL\2017\COMPRAS-ENERO.XLS"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 3. Imprimir un documento WORD

lcAction   = "print"
lcFileName = "E:\WORD\2016\PRESUPUESTOS\PRESUP295.DOC"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 4. Abrir el bloc de notas del Windows

lcAction   = "open"
lcFileName = "NOTEPAD.EXE"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 5. Abrir una página web

lcAction   = "open"
lcFileName = "www.gmail.com"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 6. Abrir una carpeta

lcAction   = "open"
lcFileName = "D:\FOTOGRAFÍAS"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 7. Abrir el Explorador del Windows con la carpeta especificada

lcAction   = "explore"
lcFileName = "D:\IMÁGENES"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 8. Escuchar una canción

lcAction   = "play"
lcFileName = "D:\CANCIONES\INGLÉS\MY WAY.MP3"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Ejemplo 9. Ejecutar como Administrador

lcAction   = "RunAs"
lcFileName = "NOTEPAD.EXE"
=ShellExecute(0, lcAction, lcFileName, "", "", 1)

Errores más comunes:

Como ya sabes, si la función ShellExecute() devuelve un número mayor que 32 eso significa que fue ejecutada con éxito, en cambio un número menor que 32 nos indica que ocurrió un error.

2. Invalid path or filename. No existe el archivo

8. Insuficient memory. No hay suficiente memoria para realizar la acción pedida

11. Invalid EXE file. El archivo no es ejecutable o está corrompido

31. Invalid action. No existe esa acción o ninguna aplicación está asociada con el archivo

Conclusión:

La función ShellExecute() nos puede resultar muy útil cuando queremos ejecutar otros programas que se encuentran en la computadora del usuario. Es muy fácil de usar, tal y como pudimos ver en los ejemplos de arriba.

Si planeas usarla en varios lugares dentro de tu aplicación, entonces lo recomendable es que realices el DECLARE en las primeras líneas de tu programa principal, para así tenerla siempre disponible.

Artículos relacionados:

El índice del blog VFPavanzado