You are on page 1of 70

Introduccin a las capacidades Cliente-Servidor

Visual FoxPro 3.0 es un gran salto en el desarrollo de aplicaciones xBase. Dentro de todas las novedades que trae esta nueva versin est la ampliacin de sus capacidades en el desarrollo de aplicaciones Cliente-Servidor. Ahora es mucho ms sencillo realizar conexiones a bases de datos remotas por medio de ODBC, pues Visual FoxPro nos va a permitir no slo realizar llamadas explcitas a funciones de ODBC, sino crear vistas locales sobre tablas remotas, lo que supone que podemos trabajar con tablas de SQL-Server u Oracle prcticamente como si fueran tablas del propio Visual FoxPro. FoxPro 2.5/2.6 ya incorporaba la posibilidad de realizar aplicaciones con acceso a ficheros de otros formatos y bases de datos remotas por medio de ODBC, pero ahora esta posibilidad se potencia y simplifica, aportando nuevas y ms fciles formas de desarrollar en este entorno. Cuando aparezca el producto definitivo, ahora en fase de betatest, las nuevas capacidades en el desarrollo Cliente-Servidor sern una de las mejoras ms importantes del producto junto a la introduccin de los diccionarios o bases de datos (DBC), la programacin orientada a objeto y los nuevos diseadores de pantallas. ODBC ODBC se ha convertido de hecho en un estndar en el acceso a datos, tanto locales en otros formatos, como remotos en servidores SQL. Visual FoxPro utiliza toda la potencia de ODBC 2.0 integrando en su versin estndar todas las funcionalidades para el acceso a estos drivers. El producto definitivo incluir un conjunto de drivers ODBC 2.0 para leer ficheros Access, dBase, Btrieve, Excel, Paradox y TXT, as como para conectarse a servidores SQL Server y Oracle. No son los ms rpidos y mejores drivers que se pueden encontrar, existe un mercado con ms de 150 fabricantes de drivers ODBC que ofrecen, en muchos casos, drivers ms optimizados y que permiten el acceso a ms de 50 tipos diferentes de formatos y servidores. Pero, al fin y al cabo Microsoft slo nos regala unos pocos drivers para que empecemos a trabajar. Integracin de las capacidades Cliente-Servidor Hasta ahora, si queramos incluir capacidades de conexin con bases de datos remotas, debamos adquirir FoxPro Versin Profesional, que incluye el Conectivity Kit, y cargar la librera FPSQL.FLL. Con esto obtenamos una serie de funciones para acceder por medio de ODBC a bases de datos de otros formatos y a bases de datos de servidores SQL. Con Visual FoxPro 3.0 la posibilidad de interactuar con ODBC est implcita al producto en su Versin Estndar, es decir, se incorporan al lenguaje de Visual FoxPro todas las funciones necesarias de estos drivers, sin necesidad de adquirir o cargar ningn otro producto adicional. Adems se ha incluido toda una serie de diseadores y asistentes para hacer ms fcil e intuitivo el trabajo con Visual FoxPro y ODBC. Este conjunto de herramientas nos van a permitir de forma interactiva definir y realizar conexiones a servidores SQL sin necesidad de teclear largas listas de comandos.

Esta integracin de las capacidades Cliente-Servidor en Visual FoxPro, facilitar, sin duda, la extensin del desarrollo de aplicaciones en modelo Cliente-Servidor a un mayor nmero de usuarios. Funciones SQL : El SQL Pass-through Con este trmino nos referimos a las funciones para la ejecucin explcita de rdenes SQL por medio de los drivers ODBC, similares a las que obtenamos con el Connectivity Kit de la Versin Profesional de FoxPro. En Visual FoxPro estas funciones comienzan con las letras "SQL", a diferencia de la versin anterior que comenzaban con "DB", y son las que se sealan en el cuadro 1. Con todas estas funciones somos capaces de realizar llamadas explcitas a los drivers de ODBC sin que estos drivers realicen la interpretacin del SQL, dejando a los servidores de bases de datos realizar esta labor. Para ejemplificar este sistema de trabajo vamos a ver un caso muy simple, pero no elemental : la obtencin de los datos de tres tablas residentes en Oracle y la actualizacin de algunos de estos datos. Las sentencias SQL pueden ser mucho ms sencillas o mucho ms complejas, en esta ocasin hemos querido presentar un caso intermedio.

**************************************** * Programa de ejemplo * * con SQL pass-through * * * * Conexin al servidor Oracle con un * * Data Source de ODBC * **************************************** nConexion = SQLCONNECT ; ( "ORADESA", "PALMUN", "PASSWORD" ) IF nConexion < 1 ERROR "Conexin no realizada." ENDIF **************************************** * Ejecucin de una consulta al servidor* **************************************** IF 0 < SQLEXEC( nConexion, ; "SELECT USUARIOS.NOMBRE,"+; "USUARIOS.APELLIDOS,"+; "APLICACIONES.APLICACION,"+; "APLICACIONES.VERSION,"+; "APLICACIONES.COMENTARIO "+; "FROM USUARIOS, APLICACIONES, ; "USER_APLI "+; "WHERE ; "USUARIOS.NOMBRE_CORTO = USER_APLI.NOMBRE_CORTO"+; "AND USER_APLI.ORDEN = APLICACIONES.ORDEN",; "user_apli" ) Else ERROR "Ejecucin SQL incorrecta."

ENDIF ****************************************** * Si se quiere, se pueden visualizar * * en un Browse * ****************************************** BROWSE NOWAIT ****************************************** * Se modifican los datos en Oracle * ****************************************** IF 0 < SQLEXEC( nConexion, ; "UPDATE USUARIOS SET"+ ; "USUARIOS.NOMBRE = 'PEDRO'"+; "WHERE USUARIO.NOMBRE = 'JORGE '" ) Else ERROR "Ejecucin SQL incorrecta." ENDIF *********************************** * Desconexin del Servidor * *********************************** =SQLDISCONNECT( nConexion ) *********** * Fin * ***********
Como se puede ver, el esquema de desarrollo es simple, por lo menos existir un conexin a una fuente de datos de ODBC por medio de SQLCONNECT, una o varias ejecuciones de sentencias SQL por medio de SQLEXEC y un cierre de la conexin con SQLDISCONNECT. Este esquema se puede perfeccionar y completar definiendo el modo de realizar las transacciones, definiendo ejecuciones asncronas o batch, etc.. Pero en definitiva el sistema es simple y las posibilidades son muchsimas. SQLCANCEL SQLCOLUMS SQLCOMMIT SQLCONNECT SQLDISCONNECT SQLEXEC SQLGETPROP SQLMORERESULT SQLROLLBACK SQLSETPROP SQLSTRINGCONNECT SQLTABLES Cancela una sentencia SQL que se est ejecutando Consulta los nombres de las columnas disponibles Confirma los cambios realizados Realiza una conexin Realiza una desconexin Ejecuta una sentencia SQL Obtiene valores de la configuracin Obtiene ms datos de una consulta anterior Deshace los cambios realizados Cambia valores de la configuracin Predefine la cadena de conexin Consulta los nombres de las tablas disponibles

Definicin de conexiones: CREATE CONNECTION

Una de las posibilidades incorporadas a Visual FoxPro es la de utilizacin de los servicios de ODBC a travs del entorno y no slo por medio de comandos y funciones. Ejemplo de esto es la posibilidad de crear conexiones predefinidas por medio del Diseador de Conexiones. Para ejecutarlo deberemos tener abierta una base de datos (DBC) y teclear CREATE CONNECTION.

Por medio de este diseador podemos definir todas las caractersticas de una conexin a bases de datos por medio de ODBC de forma sencilla y sin necesidad de utilizar las funciones SQLSETPROP y SQLGETPROP para su configuracin. Esta conexin se almacena dentro de la base de datos (.DBC) y est disponible siempre que abramos la base de datos en cuestin. Creacin de Vistas sobre tablas remotas: CREATE SQL VIEW Otra posibilidad de trabajo diferente al uso de SQL pass-through es la utilizacin de Vistas Remotas. Las vistas estn definidas en las bases de datos (.DBC), junto a las conexiones, tablas y procedimientos almacenados y pueden referirse tanto a datos locales (DBF) como a datos remotos (obtenidos por medio de ODBC). Para definir una vista y sus propiedades podemos utilizar una serie de ordenes SQL o utilizar el entorno de desarrollo. La orden bsica para crear vistas es CREATE SQL VIEW. Con ella podremos crear una vista sobre tablas remotas. El caso ms sencillo es realizar la vista sobre todos las columnas de una tabla, para ello la sentencia es simplemente :

CREATE SQL VIEW usuarios ; REMOTE CONNECTION oradesa ; SHARE AS SELECT * FROM ; USUARIOS
En ese instante la vista queda incoporada en la base de datos (DBC) que tengamos abierta en ese momento. De esta forma podemos vincular una vista remota de Visual FoxPro con cada tabla del servidor SQL sin apenas complicacin y haciendo totalmente transparente el uso de las mismas.

La vista no tiene por qu restringirse a una sla tabla, es muy posible que deseemos ver como una vista de Visual FoxPro varias tablas remotas. Siguiendo con el ejemplo anterior podemos definir la vista sobre los datos de esas tres tablas como se muestra en este ejemplo.

CREATE SQL VIEW User_apli REMOTE CONNECTION oradesa SHARE AS SELECT USUARIOS.NOMBRE, ; USUARIOS.APELLIDOS, ; APLICACIONES.APLICACION, ; APLICACIONES.VERSION, ; APLICACIONES.COMENTARIO ; FROM ; WHERE USUARIOS.NOMBRE_CORTO = USER_APLI.NOMBRE_CORTO ; AND USER_APLI.ORDEN = APLICACIONES.ORDEN
De esta forma estamos definiendo una vista sobre algunos de los datos de tres tablas unidas por medio de una "Join". Podemos complicar la definicin de la vista tanto como lo deseemos, sin apenas esfuerzo por nuestra parte. Creacin de Vistas sobre tablas remotas: DISEADOR DE VISTAS La otra posibilidad es utilizar el Diseador de Vistas. Para ello deberemos abrir un base de datos con la sentencia OPEN DATABASE y una vez abierta la modificaremos con MODIFY DATABASE, nos aparecer el diseador de bases de datos y seleccionaremos en el men 'Database' la 'New Remote View' o pulsaremos el icono correspondiente de la barra de herramientas. Se nos pedir que seleccionemos conexin ODBC almacenada en la base de datos o alguna fuente de datos predefinida :

USUARIOS, APLICACIONES, USER_APLI

Una vez seleccionada la conexin ODBC se nos abrir el Diseador de Vistas, muy similar al generador de consultas. En este diseador seleccionaremos las tablas remotas sobre las que deseamos hacer la vista. En este caso hemos seleccionado las tres tablas relacionadas que venimos utilizando, a fin de obtener la vista con datos de las tres. La definicin puede llegar a ser muy sencilla -todos los datos de una sla tabla- o muy compleja -pudiendo definir multitud de parmetros, selecciones, rdenes, agrupacin, campos a visualizar y criterios de actualizacin.

Una vez cerrado el diseador y dado un nombre a la vista, la definicin de la misma ser almacenada en la base de datos. Siempre que tengamos abierta esta base de datos, podremos hacer uso de la vista. Trabajo sobre vistas remotas: Como tablas Fox

Pero, cmo trabajamos con las vistas remotas? Cuando abrimos una bases de datos que tiene definida una vista podemos utilizarla como si se tratara de una tabla normal. Por ejemplo, podremos abrirla con USE, utilizar sus datos en programas, hacer modificaciones con APPEND y REPLACE o con SQL, realizar bsquedas, etc.. Para mostrar la transparencia en el uso de una vista he aqu el aspecto de una vista remota desde un BROWSE despus de haber hecho un simple USE :

OPEN DATABASE Data1 USE user_list BROWSE

Todas las operaciones que realizamos sobre la vista, como aadir un registro, o modificar un dato, se corresponden a operaciones sobre las tablas originales, de este modo si realizamos una modificacin en los datos de la vista, estamos modificando automticamente los datos en las tablas remotas y si alguien realiza un cambio en un valor en la tabla remota, nosotros observaremos ese cambio en la vista. Podemos no darnos cuenta de la importancia de este hecho, podemos trabajar con datos remotos como si fueran datos de Visual FoxPro, manteniendo una total integridad entre la vista remota que maneja Visual FoxPro y los datos del servidor SQL, sin que sea necesario por nuestra parte ningn cdigo especfico para ello, simplemente definiendo los datos como una vista remota. Un ejemplo. Sobre la vista que hemos definido anteriormente vamos a modificar algunos valores y esta modificacin ser realizada automticamente en las base de datos remota.

******************************************** * * Programa de ejemplo con vistas * ******************************************* *************************** * Abre la Base de Datos * ***************************

OPEN DATABASE Data1 ******************************************* * Abre la vista sobre tablas de Oracle * ******************************************* USE User_apli ******************************************* * Si se quiere, se pueden visualizar en * * un Browse * ******************************************* BROWSE NOWAIT ****************************************** * Se modifican los datos en Oracle * ****************************************** REPLACE Nombre WITH "PEDRO" FOR Nombre == "JORGE " *********** * Fin * ***********
Las posibilidades son inmensas, podemos relacionar tablas locales con vistas remotas, realizar todo tipo de operaciones sobre los datos remotos, realizar bsquedas, modificaciones, etc. Es prcticamente indiferente trabajar sobre tablas locales que sobre vistas remotas, es decir, gracias a la capacidad de Visual FoxPro de definir vistas remotas podremos trabajar en un entorno Cliente-Servidor de forma transparente. Asistente para Upsizing Gracias a la enorme similitud en el trabajo con tablas locales y con vistas remotas, es posible realizar las labores de desarrollo sobre vistas o tablas locales y en un momento dado, cercano a la finalizacin del proyecto, realizar una migracin de las estructuras de datos y relaciones a un Servidor SQL tipo SQL-Server u ORACLE. Esto nos permite desarrollar sin necesidad de disponer de un servidor SQL, siempre costoso. Para ayudar en esta labor de migracin desde las tablas y vistas locales a tablas en un servidor SQL existe el "Upsizing Wizard" (o asistente de 'upsizing'), que nos facilita el traslado del diseo de nuestra base de datos desde Visual FoxPro a un servidor de Bases de Datos. Si bien es posible que en la versin definitiva de Visual FoxPro el asistente tenga algunas modificaciones sobre la versin beta actual, sta es una de las pantallas del mismo :

Su uso es muy sencillo, siguiendo sus indicaciones seremos capaces de trasladar nuestro modelo de datos local a un modelo Cliente-Servidor. Es decir, una vez realizado este upsizing nuestra aplicacin pasar a trabajar sobre un diseo Cliente-Servidor, en principio, sin ms modificaciones, aun cuando debe de tenerse en cuenta algunos aspectos sobre el nuevo diseo y ajustar los parmetros de las vistas remotas a fin de que sea un sistema efectivo. Conclusin : Una de las mejores herramientas para el modelo ClienteServidor Visual FoxPro est llamado a convertirse en una de las mejores herramientas para el desarrollo de aplicaciones en entornos Cliente-Servidor. Conjuga un sistema de desarrollo muy optimizado por medio de "forms", un lenguaje xBase potente y extendido, una programacin orientada a objeto sencilla y efectiva, una velocidad en el manejo de datos locales inmejorable y unas capacidades de acceso a datos remotos sorprendentemente fciles. Por lo tanto, la revolucin que sin duda supone la aparicin de Visual FoxPro nos acercar a todos un poco ms al interesante mundo del desarrollo de aplicaciones en entornos Cliente-Servidor.

Introduccin a la POO en Visual FoxPro : Conceptos bsicos (I)


Con este artculo empezamos una pequea serie que nos acercar a la Programacin Orientada a Objeto (POO) en Visual FoxPro. En este, el primero, haremos una descripcin de los conceptos bsicos sobre los que se sustenta la POO a fin de introducirnos en esta forma de programar. Si Vd. es nuevo en esto de la POO es posible que en algunos momentos se sienta perdido. No se preocupe, es normal. La orientacin a objeto tiene un gran nmero

de conceptos interrelacionados, por ello cuando describimos uno de forma separada parece carecer de sentido. No dude en releer el texto una y otra vez, de esa forma conseguir tener una visin de conjunto. Si por el contrario ya lleva algn tiempo aplicando estas tcnicas lo que le interesa realmente es la implementacin que de ests tcnicas de programacin hace Visual Foxpro (VFP), en parte podr ver algunas de sus caractersticas en este artculo, pero e s realmente en los prximos donde se desarrollar toda la potencia de VFP. Hemos intentado en todo momento ser rigurosos, pero espero que disculpe que en algunas ocasiones se haga una descripcin poco formal de las caractersticas de la POO a fin de facilitar la compresin. Si ya conoce algunas de las caractersticas de VFP sabr que es posible realizar POO casi sin darse cuenta, pero en este artculo haremos hincapi en los entresijos de este tipo de programacin desde la codificacin ms tradicional, a fin de saber perfectamente lo que estamos haciendo en cada momento. En prximos artculos describiremos la POO cuando utilizamos Form Designer, el generador de pantallas, o Class Designer, la herramienta visual para definicin de clases, as como el manejo de herramientas de apoyo como el Class Browser. No es tan difcil Est de moda hablar de POO, y no es nuevo este concepto, pero es ahora cuando se est generalizando su uso. Esta moda a provocado alguna confusin, muchas herramientas han dicho estar orientadas a objeto sin serlo, otras han realizado implementaciones muy extraas, y algunas, como VFP, han sabido mantener un equilibrio y una buena implementacin, por lo que son verdaderamente recomendables para desarrollar con este nuevo sistema de programacin. Quizs uno de los problemas a la hora de afrontar por primera vez la POO, estriba en el temor que puede producir tantos y tantos nuevos conceptos que se ciernen sobre nosotros. Lo cierto es que la POO no es tan fiera como la pintan, pero en la medida que cambia algo nuestra forma de pensar los programas, sus componentes y el modo que se relacionan entre si, requiere un cierto esfuerzo. Los que llevan aos en esto de la programacin recuerdan lo difcil y extrao que fue el paso de la programacin lineal (con goto) a la programacin estructurada (con call), ahora toca el paso de la programacin estructurada a la POO. La POO viene a completar algunas de las lagunas de la programacin estructurada, como esta vino a solucionar algunos de los problemas de la programacin lineal. No es la soluciona a todos nuestros problemas, pero s facilita nuestra labor de programacin y mantenimiento. Objetivos de la POO La POO intenta ser un mejor sistema para el desarrollo de aplicaciones. Como toda tcnica de programacin si se hace mal puede ser desastrosa -pensemos los los que se pueden provocar si aplicamos mal la programacin estructurada- pero es un mejor sistema de desarrollo. Una de las primeras mejoras que obtenemos por el uso de la POO es el permitir afrontar programas ms complejos y de mayor tamao con menos esfuerzo. El hecho de trabajar con pequeos elementos bien definidos, como son los objetos, nos permite aislar cada componente de la aplicacin del resto y de esa forma aprovechar en mayor medida nuestro esfuerzo.

Una vez adiestrados en las nuevas tcnicas de orientacin a objeto obtendremos tambin una mejora considerable en nuestro rendimiento de desarrollo. Las grandes facilidades para el reaprovechamiento del cdigo que nos ofrece la orientacin a objeto harn que desarrollemos con mayor velocidad, pero manteniendo unos buenos niveles de calidad. La reutilizacin de cdigo en la POO nos otorga una gran flexibilidad. La existencia de la herencia permite modificar las caractersticas que necesitemos de una clase de objeto con seguridad de no alterar las especificaciones del mismo y aprovechan do todo el desarrollo realizado en el mismo. El mantenimiento de aplicaciones se ha visto como uno de los grandes problemas de la programacin actual. Con las tcnicas de POO es ms sencillo realizar este mantenimiento. Los objetos son elementos de pequeo tamao, bien definidos, y por lo tanto ms fciles de mantener. Adems la existencia de la herencia nos va a asegurar que la modificacin de algunas caractersticas de la clase no van a afectar a los desarrollos ya terminados. La definicin correcta de los objetos permitir tener una mejor estructuracin de nuestros programas. Con la POO se tiene un acercamiento ms natural a los problemas y por lo tanto los anlisis de aplicaciones orientadas a objeto tienen un acercamiento a la realidad mucho ms completa que con la programacin estructurada. Esto no quiere decir que al principio no cueste un poco realizar este tipo de anlisis, pero una vez adquirida las nuevas tcnicas, es mucho ms sencillo. La POO es muy fcilmente compresible en entornos grficos, pues el hecho de que estos entornos manejen objetos grficos hace muy recomendable este tipo de programacin. Pero debemos tener claro que es totalmente posible realizar POO en entornos de tipo carcter y es posible programar entornos grficos sin POO. No todo son ventajas. Tampoco podemos decir que todo sean facilidades. Por una parte la POO nos va ha obligar a cambiar nuestra forma de pensar los programas y por lo tanto es necesario un tiempo para que las nuevas tcnicas se nos hagan habituales y automticamente nos salga programar de esta forma. No basta con dominar estas tcnicas para que alcancemos las mejores cotas de productividad, posiblemente sea necesario elaborarnos unas buenas libreras de clases. Para ello es conveniente desarrollar un par de aplicaciones bajo esta tcnicas para darnos cuenta de que es lo que realmente es factible de ser reutilizado y de esa forma organizar nuestras propias libreras de clases. La depuracin de cdigo orientado a objeto es algo ms compleja que la depuracin de cdigo estructurado. Esto no quiere decir que nuestro cdigo va a ser peor o va ha tener ms errores, pero s es cierto que en el caso de producirse un error deberemos recorrer todo el rbol de herencia para encontrarlo, algo que en programacin estructurada no tenemos que hacer. Pero en general, podemos decir que los inconvenientes son realmente menores que las ventajas, y por lo tanto es realmente recomendable el desarrollo con POO. Elementos de la POO

Vamos a hacer un repaso de los distintos elementos que componen la POO, y como cada uno de ellos es implementado en VFP. En algunos momentos es posible que no entienda algunos de los conceptos, no se preocupe, poco a poco intentaremos aclarar todo este entramado de conceptos y sintaxis. Clase y Objeto. Son los dos primeros elementos de la POO. Se puede decir que la clase es la generalizacin de los objetos y los objetos son la concrecin de la clase, aun cuando parezca un galimatas. Se suelen poner ejemplos bastante filosficos para describir la relacin entre el objeto y la clase (variando segn sea el autor idealista o empirista). Podemos decir que existen un objeto Pedro, otro Antonio y otro Luisa. Todos estos objetos tienen elementos en comn y por ello decimos que son de la clase persona. De la misma forma otros dicen que tenemos una idea clara de lo que es ser una persona y luego somos capaces de distinguir hombres concretos. Para poner otro tipo de ejemplo podemos observar los botones de un entorno grfico. Todos sabemos como es un botn en un entorno grfico, si bien cada botn es diferente de los dems, todos se parecen y por lo tanto podemos decir que la clase botn es la generalizacin de las propiedades y comportamientos de todos los botones de los entorno grficos. Definir una clase. Las clases son la descripcin de los elementos comunes de los objetos que generalizan. As las clases se definen y pueden ser usadas para crear innumerables objetos de este tipo. Para definir una clase utilizaremos una sencilla sintaxis de VFP. Vamos a empezar definiendo una clase denominada Persona. Es una clase vaca, pero nos sirve para comenzar:

DEFINE CLASS Persona AS CUSTOM ENDDEFINE


Para poder utilizar esta definicin debemos incluirla en un fichero .PRG y cargado con SET PROCEDURE TO o bien incluirla al final de nuestro fichero .PRG. Crear un objeto. Ya podemos crear objetos basado en esta clase, para ello utilizamos la siguiente expresin:

oPersona1 = CREATEOBJECT( "Persona" ) oPersona2 = CREATEOBJECT( "Persona" )


Debemos tener clara la diferencia entre clase y objeto. La clase es una plantilla, donde definimos las caractersticas de cada uno de los objetos. Los objetos que hemos creado, oPersona1 y oPersona2, comparten la plantilla que con la que se han creado, la clase Persona, pero son diferentes entre si. Borrar un objeto. Los objetos se asemejan a las variables en cuanto a que pueden declararse como LOCAL, PRIVATE o PUBLIC. Esta es la nica similitud que tienen las variables y los objetos, pero es una caracterstica muy importante. Por defecto, los objetos son de tipo PRIVATE y por lo tanto existirn mientras se ejecute el programa que los cre. Una vez salgamos de este programa el objeto se borrar automticamente. El objeto puede ser usado en el programa que lo cre y en todos lo programas llamados desde l.

Si declaramos el objeto como LOCAL el objeto persistir hasta la salida del programa que lo cre, pero los programas que sean llamados desde el programa de creacin no podrn hacer uso de este objeto, pues permanece oculto par a ellos, evitando as posibles problemas con los nombres de los objetos. Al declarar un objeto como PUBLIC estamos indicando que permanezca hasta que lo borremos explcitamente o salgamos de VFP. Este objeto podr ser utilizado por cualquier programa desde el momento que es creado hasta que sea borrado. Para borrar un objeto de forma explcita debemos hacer uso del comando RELEASE. Podemos borrar de forma explcita no solo los objetos pblicos, sino tambin los locales y privados. Veamos un ejemplo muy simple de creacin de un objeto pblico y su destruccin :

PUBLIC oPrueba oPrueba = CREATEOBJECT( "Persona" ) ... RELEASE oPrueba


Encapsulacin. Aunque parece un termino extrao, es muy habitual en POO. Hace referencia a la capacidad de los objetos para incluir dentro de si tanto datos como acciones. Las clases de distinguen unas de otras justamente por tener unos datos y acciones que las diferencian. Los objetos de una misma clase se diferencian entre si por tener datos diferentes. Los datos que caracterizan a una clase se denominan propiedades y sus acciones (o programas) se denominan mtodos. Se dice por lo tanto que la clase encapsula mtodos y propiedades, es decir, que agrupa dentro si tanto mtodos como propiedades. Las propiedades y mtodos de una clase se denominan habitualmente propiedades y mtodos miembro. Veamos poco a poco estos nuevos conceptos. Propiedades. Como hemos dicho, las propiedades son los datos que manejan las clases. Estas propiedades se declaran en la definicin de la clase y permanecen en todo momento asociados a los objetos creados bajo esa clase. Para verlo con ms claridad vamos a dar contenido a esta definicin de la clase Persona que iniciamos hace un momento. Para ello debemos estudiar que propiedades posee este tipo de objeto. Podemos decir que todas la personas tienen un nombre, unos apellidos y una fecha de nacimiento. Hay muchas otras propiedades para una clase de este tipo, pero empecemos con estas. La implementacin en VFP se hara de la siguiente manera :

DEFINE CLASS persona AS CUSTOM cNombre = "" cApellidos = "" dFechaNacimiento = {} ENDDEFINE

En nuestra definicin de clase declaramos las propiedades con unos valores iniciales, que pueden ser de cualquiera de los tipos de datos definidos en VFP. Por ejemplo, cNombre y cApellidos los hemos inicializado como una cadena vaca y dFechaNacimiento como una fecha tambin vaca, pero pueden ser numricos, datetime o de cualquier otro tipo. Tambien podramos haber definido las propiedades con cualquier otro tipo de valor por defecto : una texto o una fecha en concreto, de esta forma, al crear un objeto ya tendra este valor la propiedad. Por ejemplo podramos ampliar la definicin de la clase incluyendo una propiedad denominada cEstadoCivil que por defecto fuera la cadena Soltero.

DEFINE CLASS persona AS CUSTOM cNombre = "" cApellidos = "" dFechaNacimiento = {} cEstadoCivil = "Soltero" ENDDEFINE
Una vez creado un objeto, si quedemos dar valores a cada una de sus propiedades haremos uso de operador punto. Para ello pondremos el nombre del objeto, un punto y el nombre de la propiedad :

oPersona1 = CREATEOBJECT( "persona" ) oPersona1.cNombre = "Mara" oPersona1.cApellidos = "Prez Gonzlez" oPersona1.dFechaNacimiento = {20-10-75} oPersona2 = CREATEOBJECT( "persona" ) oPersona2.cNombre = "Pedro" oPersona1.cApellidos = "Jimnez Nieto " oPersona2.dFechaNacimiento = {04-12-69} oPersona2.cEstadoCivil = "Casado"
Si, como en este caso, vamos a dar valores a muchas propiedades de un objeto podemos utilizar la sintaxis abreviada, de la forma siguiente :

WITH oPersona2 .cNombre = "Pedro" .cApellidos = "Jimnez Nieto " .dFechaNacimiento = {04-12-69} .cEstadoCivil = "Casado" ENDWITH
Desde ese momento podemos hacer uso de estas propiedades, usando tambin el operador punto:

WAIT WIND oPersona1.cNombre WAIT WIND oPersona2.cNombre

Como decamos, las propiedades estn asociadas a cada objeto, de esta forma el valor de la propiedad cNombre es diferente entre los objetos oPersona1 y oPersona2, aun cuando en la definicin de la clase hubiramos otorgado un va lor por defecto a esta propiedad. Es importante tener clara esta diferencia. Las propiedades se declaran en la definicin de la clase, pero los valores de las propiedades pueden ser diferentes para cada uno de los objetos de esta clase. Mtodos. El otro elemento caracterstico de una clase son los mtodos. Los mtodos son acciones que pueden realizar los objetos, es decir, son funciones o procedimientos asociados a este tipo objeto. En el caso de las personas podemos decir que pueden nacer, morir, casarse, tener hijos, etc... Para dar un primer ejemplo de esto veamos uno de sus mtodos :

DEFINE CLASS persona AS CUSTOM cNombre = "" cApellidos = "" dFechaNacimiento = {} cEstadoCivil = "Soltero" PROCEDURE Nacer LPARAMETER cNombre, ; cApellidos, ; dFecha This.cNombre = cNombre This.cApellidos = cApellidos This.dFechaNacimiento = dFecha ENDPROC ENDDEFINE
En este mtodo damos valor a tres propiedades del objetos a partir de los parmetros que se nos han pasado. Quizs sea todava poco evidente este cdigo, pero poco a poco iremos entendiendolo. En primer lugar debemos diferenciar entre los parmetros de este mtodo y las propiedades. Los parmetros son variables y se perdern al finalizar la ejecucin del mtodo. Las propiedades, que son la que empiezan con la sintaxis This., permanecen mientras dure la existencia del objeto. Por otra parte, este procedimiento, denominado Nacer, se diferencia de los procedimientos que estamos acostumbrados a escribir en que slo es llamable asociado a un objeto de la clase Persona y no puede ser invocado de forma independiente. Esta es una de las grandes diferencias entre la programacin estructurada y la POO. Mensajes. Cuando llamamos a un mtodo de un objeto se dice que estamos enviando un mensaje al objeto para que realice una determinada accin. As cuando enviamos un mensaje de nacer a un objeto persona estamos ejecutando el mtodo correspondiente:

oPersona1 = CREATEOBJECT( "Persona" )

oPersona1.Nacer( "Mara", ; "Prez Gonzlez", ; {20-10-75} ) ? oPersona1.cNombre ? oPersona1.cApellidos


Operador This. Vemos dentro del cdigo del mtodo Nacer que hemos utilizado una extraa sintaxis, algo como THIS. Decamos al principio que la clase es una plantilla y cuando definimos un mtodo dentro de esta plantill a que es la clase no sabemos cual ser el nombre del objeto que utilizar este mtodo, por eso, cuando vamos a utilizar una propiedad o un mtodo de la clase, debemos anteponer al operador punto el operador This, para indicar que se trataran las propiedades del objeto que recibe el mensaje, es decir, que ha sido invocado, y no para otro. Deciamos que las propiedades mantienen valores diferentes para cada uno de los objetos, pero los mtodos comparten su cdigo entre todos los objetos de una clase. Un mtodo vara en la medida que las propiedades del objeto que lo llama son diferentes, por ello es tan importante el operador This. De esta forma cuando ejecutamos:

oPersona1.Nacer( "Mara", ; "Prez Gonzlez", ; {20-10-75} )


el cdigo del mtodo Nacer asocia el primer parmetro, denominado cNombre, a la propiedad cNombre del objeto que recibe el mensaje. As This.cNombre es una propiedad del objeto y cNombre es una variable que cor responde al primer parmetro del mtodo invocado en el mensaje.

PROCEDURE Nacer LPARAMAMETER cNombre, ; cApellidos, ; cFecha This.cNombre = cNombre ... ENDPROC
Ocultacin. Una de las mejoras que implementa la POO, es la posibilidad de limitar el acceso a determinadas propiedades o mtodos. Con ello conseguimos que la utilizacin del la clase se haga de forma ordenada. Las propiedades o mtodos protegidos slo son utilizables desde los mtodos pertenecientes a esta clase y no pueden usarse directamente por otros programas. Vamos con un ejemplo :

DEFINE CLASS Nivel AS CUSTOM PROTECTED nContador

nContador = 0 PROCEDURE Mas LPARAMETER nCantidad This.nContador = This.nContador ; + nCantidad ENDPROC PROCEDURE Menos LPARAMETER nCantidad This.nContador = This.nContador ; - nCantidad ENDPROC PROCEDURE Ver RETURN This.nContador ENDPROC ENDDEFINE
Esta clase define un interface por medio de los mtodos Mas, Menos y Ver. Si intentamos modificar el valor de nContador de forma directa nos dar un error, pues esta propiedad est protegida.

oTemperatura = CREATEOBJECT( "Nivel" ) oTemperatura.Mas ( 10 ) ? oTemperatura.Ver() oTemperatura.Menos ( 3 ) ? oTemperatura.Ver() * Proboca el Error : * Property NCONTADOR is not found. oTemperatura.nContador = 100
Al igual que podemos proteger propiedades, podemos proteger mtodos. De esta forma podemos definir mtodos que slo sean usado por otros mtodos de la clase y no puedan ser invocados a partir de los objetos de esta clase. Para ello basta colocar la clus ula PROTECTED antes de PROCEDURE. Polimorfismo. Cuando realizamos programacin estructurada debemos tener cuidado de no llamar con el mismo nombre a dos variables o a dos procedimientos, sin embargo en la POO podemos llamar a un mtodo o a una propiedad de una clase de igual forma que un mtodo o propiedad de otra. Esta caracterstica es lo que se denomina polimorfismo. Veamos un ejemplo con propiedades :

DEFINE CLASS ClaseUno AS CUSTOM Dato = 1000 ENDDEFINE DEFINE CLASS ClaseDos AS CUSTOM Dato = "Hola"

ENDDEFINE
Esto, que puede parecer un problema, es muy sencillo de entender si vemos como los utilizamos los dos objetos :

Objeto1 = CREATEOBJECT( "ClaseUno" ) Objeto2 = CREATEOBJECT( "ClaseDos" ) ? Objeto1.Dato ? Objeto2.Dato


Aun cuando en los dos objetos llaman a una propiedad denominada Dato, en el primer caso estamos llamando a una propiedad tipo numrica definida en ClaseUno, y el segundo caso estamos llamado a una propiedad de tipo carcter definida en ClaseDos. Igual que las propiedades, en el caso de los mtodos tambin es posible el polimorfismo. De esta forma podemos definir dos mtodos denominados Imprimir, pero que cada uno hace una cosa bien distinta :

DEFINE CLASS ClaseUno AS CUSTOM PROCEDURE Imprimir ? "Hola esto es una prueba ..." ENDPROC ENDDEFINE DEFINE CLASS ClaseDos AS CUSTOM PROCEDURE Imprimir LIST STATUS ENDPROC ENDDEFINE
En VFP no es posible definir dos mtodos o dos propiedades con igual nombre dentro de una misma clase. Eventos. Existe una serie de mtodos especiales, que normalmente no se ejecutan por ser invocados de forma explcita, como los que hemos definido hasta ahora, sino que por denominarse de una forma determinada son lanzados cuando 'pasa algo', es dec ir, cuando se produce un evento. Estos eventos pueden ser un click, el movimiento del ratn, un pulsacin de tecla, etc.. Los dos primeros eventos que vamos a tratar son los denominados Init y Destroy. El primero se ejecuta cuando se crea un objeto de esta clase y el segundo cuando se destruye. En otros lenguajes orientados a objeto estos mtodos son denomi nados el constructor y el destructor de la clase, pero el VFP son tratados como eventos. Los eventos Init y Destroy los encontramos en absolutamente todos los objetos de VFP. Veamos como hacer uso de esta caracterstica creando dos mtod os con estos nombres :

* Lanzamiento automtico del * mtodo INIT oPrueba = CREATEOBJECT( "Eventos" ) ...

* Lanzamiento automtico del * mtodo DESTROY RELEASE oPrueba DEFINE CLASS Eventos AS CUSTOM PROCEDURE Init ? "Creacin del objeto ..." ENDPROC PROCEDURE Destroy ? "Destruccin del objeto ..." ENDPROC ENDDEFINE
Cada tipo clase tiene unos eventos predefinidos y que no podemos aadir ms. As, por ejemplo, la clase CUSTOM tiene definidos los eventos Init, Destroy y Error, la clase COMMANDBUTTOM los eventos Click, Destroy, DragDrop, DragOver, Error, ErrorMessage, GotFocus, Init, InteractiveChange, KeyPress, LostFocus, Message, MouseDown, MouseMove, MouseUp, RightClick, UIEnable, Valid, When, etc.. La mayora de estos eventos los veremos en prximos artculos de forma detallada. De momento quedmonos con el concepto de evento. Objetos como propiedades. Existe la posibilidad, en algunas clases, de definir objetos como propiedades miembro. Por ejemplo, podemos querer definir una clase matrimonio con dos objetos tipo persona. As definiramos :

DEFINE CLASS Matrimonio AS CUSTOM dFecha = {} ADD OBJECT Conyuge1 AS Persona ADD OBJECT Conyuge2 AS Persona ENDDEFINE
Para poder hacer uso de ellos no tenemos ms que indicar el nombre del objeto miembro. Por ejemplo :

oPareja1 = CREATEOBJECT( "Matrimonio" ) oPareja1.Conyuge1.Nombre = "Mara" oPareja2.Conyuge2.Nombre = "Pedro"


No todas las clases admiten que sean definidos objetos como miembros de la misma, slo las clases denominadas contenedoras, entre la que se encuentra CUSTOM.

Introduccin a la POO en Visual FoxPro : Conceptos bsicos (y II)


Por razones de espacio se interrumpi en el nmero anterior el relato de los elementos y conceptos de la programacin orientada a objeto y su implementacin en Visual FoxPro 3.0. En este artculo continuaremos con esa descripcin. En el artculo anterior intentamos explicar los conceptos de Clase, Objeto, Encapsulacin, Propiedad, Mtodo, Mensaje, Operador This, Ocultacin,

Polimorfismo y Evento. Pero dejamos para este artculo uno de los principios bsicos de la programacin orientada a objeto, la Herencia. Herencia. La herencia es un mecanismo que nos va a permitir reutilizar nuestro cdigo de forma fcil y ordenada. Siempre que hemos definido una clase en los ejemplos anteriores hemos heredado de la clase base CUSTOM. No hemos sido conscien tes de ello, pero siempre hemos puesto la clusula AS CUSTOM, es decir, estabamos heredando de la clase CUSTOM. Para verlo ms claramente vamos a crear una clase llamada Prueba y utilizaremos la orden DISPLAY OBJECT par a ver como esta formado un objeto de esta clase: oPrueba = CREATEOBJECT( "Prueba" ) DISPLAY OBJECTS DEFINE CLASS Prueba AS CUSTOM ENDDEFINE Podremos obtener una salida de DISPLAY OBJECS similar a la figura 1. Object: OPRUEBA Class Tree: PRUEBA CUSTOM Properties: BASECLASS CLASS CLASSLIBRARY COMMENT HEIGHT LEFT NAME PARENTCLASS PICTURE TOP WIDTH Methods and Events: ADDOBJECT INIT C C C C N N C C C N N "Custom" "Prueba" "C:\TMP\prueba.FXP" "" 17 ( 17,00000000) 0 ( 0,00000000) "Prueba1" "Custom" "" 0 ( 0,00000000) 100 ( 100,00000000) ERROR SAVEASCLASS Priv O PRUEBA

DESTROY REMOVEOBJECT

Podemos observar en primer lugar el objeto se llama OPRUEBA y es de tipo PRUEBA. En la seccin Class Tree vemos que esta clase tienen un rbol de herencia compuesto de PRUEBA y CUSTOM. As mismo vemos un buen n mero de propiedades y algunos mtodos que nosotros no hemos escrito y esto se debe ha que al haber definido la clase PRUEBA como una clase heredada de la clase CUSTOM hemos heredado las propiedades y mtodos de esta clase padre. En VFP siempre es necesario crear una clase heredando de alguna otra. De esta forma, cualquier clase que creemos tendr siempre algunas propiedades y algunos mtodos que toda clase de VFP posee. La clase CUSTOM slo posee los mtodos y propiedade s que siempre podremos encontrar en todo objeto, todas las dems clases predefinidas en VFP (un total de 33) tiene muchas ms propiedades y mtodos que los descritos aqu, pero siempre tienen estos mtodos y propiedades bsicos.

Sigamos con los ejemplos de la clase Persona. Definimos en su momento esta clase, pero ahora nos damos cuenta que debemos hacer una ampliacin. En la aplicacin necesitamos tratar tambin un caso concreto de persona, los contribuyentes, pero sabe mos que no todas las personas son contribuyentes, pero todos los contribuyentes son personas. Tras un cierto anlisis concluimos que es necesaria la herencia para incluyendo nuevos datos y mtodos de la clase Contribuyente pero manteniendo la integridad y funcionalidad de la clase Persona. Veamos como se hace esta herencia: * Clase original DEFINE CLASS persona AS CUSTOM cNombre = "" cApellidos = "" dFechaNacimiento = {} cEstadoCivil = "Soltero" PROCEDURE Nacer LPARAMETER cNombre, ; cApellidos, ; dFecha This.cNombre = cNombre This.cApellidos = cApellidos This.dFechaNacimiento = dFecha ENDPROC ENDDEFINE * Clase nueva DEFINE CLASS Contribuyente AS Persona cNIF = "" nBaseImponibe = 0 cRegimenSS = "" ENDDEFINE Cuando definamos un objeto de la clase Contribuyente, podremos hacer uso tanto de las propiedades definidas en su clase como en las definidas en las clases superiores. As podremos decir : oContr = CREATEOBJECT( "Contribuyente" ) * Propiedades definidas * en la clase Persona oContr.cNombre = "Juan" oContr.cApellidos = "Lpez Garrido" * Propiedad definida * en la clase Contribuyente oContr.cRegimenSS = "Autonomo" Como todo Contribuyente es tambin una Persona, tiene todos los mtodos y propiedades de la clase Persona. La herencia nos va a facilitar enormemente el mantenimiento del cdigo. Por una parte podemos heredar en vez de modificar la clase base y de esa forma preservar la naturaleza de los objetos sin necesidad de modificar cientos de programas. Por otra aparte, cualquier modificacin que realicemos en una clase, se ve reflejada automticamente en todas las clases que hereden de ella. Por ejemplo, si aadimos una nueva propiedad a la clase Persona o modificamos alguno de sus mtodos, esa

modificacin tambin se ve reflejada en el comportamiento de los objetos Contribuyente, pues tambin son del tipo Persona. Identificar Clases y Herencias. La programacin orientada a objeto exige de nuevas tcnicas de anlisis orientada objeto. Cuando nos enfrentamos a un programa realizado bajo estas tcnicas la mayores dificultades las tenemos en identificar la clases correctamente y en definir las relaciones entre las distintas clases. No es fcil al principio, pero en poco tiempo la definicin de clases y las herencias entre ellas ser un trabajo tan sencillo como ahora identificar cuando debemos hacer una subrutina. Sobreescribir mtodos o propiedades. En algunos casos, en la clase hija, queremos modificar el comportamiento de algn mtodo o el contenido de alguna propiedad de la clase padre. Este hecho se denomina sobreescribir. Con l somos capaces de modifi car los miembros de una clase padre sin afectar al cdigo de la misma. En el siguiente caso la clase Persona tiene un mtodo denominado Imprimir y la clase Contribuyente va a sobreescribir este mtodo con el suyo propio. DEFINE CLASS persona AS CUSTOM cNombre = "" cApellidos = "" dFechaNacimiento = {} cEstadoCivil = "Soltero" PROCEDURE Imprimir ? "Nombre : " ; + This.cNombre ; + " " ; + This.cApellidos ? "Fecha de nacimiento : " ; + DTOC( This.cFechaNacimiento ) ? "Estado civil : " ; + This.cEstadoCivil ENDPROC ENDDEFINE * Clase nueva DEFINE CLASS Contribuyente AS Persona cNIF = "" nBaseImponible = 0 cRegimenSS = "" PROCEDURE Imprimir ? "Nombre : " ; + This.cNombre ; + " " ; + This.cApellidos ? "Fecha de nacimiento : " ; + DTOC( This.dFechaNacimiento ) ? "Estado civil : " ; + This.cEstadoCivil ? "NIF : " ; + This.cNIF ? "Base Imponible : " ; + STR( This.nBaseImponible )

? "Regimen de la S.S. : " ; + This.cRegimenSS ENDPROC ENDDEFINE Cuando llamemos al mtodo imprimir, depender de la clase de objeto que utilicemos se llamar a un mtodo o a otro. oPrue1 = CREATEOBJECT( "Persona" ) oPrue1.cNombre = "Juan" oPrue1.cApellidos = "Lpez Garrido" oPrue2 = CREATEOBJECT( "Contribuyente" ) oPrue2.cNombre = "Pedro" oPrue2.cApellidos = "Gomz Iriarte" oPrue1.Imprimir()&& De persona oPrue2.Imprimir()&& De contribuyente Cuando escribamos un mtodo denominado Init o Destroy lo que estamos haciendo es sobreescribir el mtodo por defecto para este evento. De igual forma podemos sobre escribir cualquier otro mtodo o evento de una clase. Recordemos lo que decamos antes sobre el Polimorfismo, este es otro ejemplo de esta caracterstica, tenemos dos mtodos con el mismo nombre en clases diferentes, pero en este caso, las clases se heredan una de otra, sobreescribiendo este mtodo. El Operador ::. Si es observador se habr percatado que en ejemplo anterior estamos duplicando parte del cdigo del mtodo Imprimir de la clase Persona en el mtodo imprimir la clase Contribuyente, esto no parece muy acertado para ayudar al mantenimiento del cdigo. En algunos casos queremos sobreescribir totalmente el mtodo de la clase padre, pero en otros casos lo que deseamos en slo incluir nuevas prestaciones, pero manteniendo el cdigo anterior. Para estos casos se ha creado el operador :: u operador de resolu cin de alcance. Con el podemos hacer referencia al mtodo de una clase superior aun cuando este mtodo se hubiera sobreescrito. Para hacer uso de este operador debemos indicar el nombre de la clase padre, el operador ::, y el nombre del mtodo. Haciendo uso de este operador podramos haber escrito la clase Contribuyente de la siguiente forma : DEFINE CLASS Contribuyente AS Persona cNIF = "" nBaseImponible = 0 cRegimenSS = "" PROCEDURE Imprimir * Llamada al procedimiento * original de la clase Persona Persona::Imprimir() && OPERADOR :: * Resto de impresin ? "Estado civil : " ; + This.cEstadoCivil ? "NIF : " ;

+ This.cNIF ? "Base Imponible : " ; + STR( This.nBaseImponible ) ? "Regimen de la S.S. : " ; + This.cRegimenSS ENDPROC ENDDEFINE Esta caracterstica nos asegura un buen mantenimiento de nuestras clases, pues cualquier modificacin en el mtodo de la clase padre, se ve automticamente reflejado en el mtodo de la clase hija, aunque este sobre escrto el mtodo. Conclusiones Han sido muchos conceptos seguidos y es posible que se sienta un poco aturdido con tantas palabras. No se preocupe, como decamos al principio, la POO es un conjunto de conceptos interrelacionados que difcilmente se entiende unos sin los otros. Poco a po co ir comprendiendo su significado y concluir que no estn difcil como algunos quieren hacer creer. En los siguientes artculos haremos referencia a los conceptos aqu esbozados y esperemos que se vaya encontrando ms fcil su compresin a medida que avancemos. En este artculo solo hemos creado clases partiendo de CUSTOM y siempre han sido clases muy poco prcticas, pero prometemos realizar algunas clases que si merecen la pena ser utilizadas. Tambin describiremos las herramientas que VFP nos da para una hacer ms fcil la POO, las clases que nos facilita y el modo de trabajar con ellas. La POO est aqu y no deberamos ignorarla por ms tiempo. Posiblemente no es necesario este tipo de programacin, pero es realmente muy recomendable, es seguro que no soluciona todos los problemas, pero es mucho ms sencillo el desarrollo, tendremos que esforzarnos un poco al principio, pero nuestro esfuerzo se ver sobradamente recompensado. En definitiva, la Programacin Orientada a Objeto es una mejor forma de programar.

Uso del API de Windows : Introduccin (I)


Con este artculo empezamos una serie que nos acercar a las posibilidades que nos ofrece Visual FoxPro (VFP) para llamar a funciones contenidas en libreras de enlace dinmico (DLLs) y por lo tanto a la posibilidad de llamar a las funciones del Application Program Inteface (API) de Windows. No pretendemos dar un curso sobre el API de Windows, sino orientar sobre las posibilidades que tenemos al alcance de nuestra mano para ampliar VFP. Para ello describiremos algunas funciones tiles y sencillas que ilustrarn estas posibilidades, pero no pretendemos ser exhaustivos en las descripciones del API de Windows. Pero vamos por partes, empecemos por ver como podemos traspasar las barreras de Visual FoxPro. Distintas formas de ampliar VFP

Todo lenguaje o entorno de programacin es finito, es decir, esta limitado. Pero en muchos casos es posible ampliar la funcionalidad de un sistema por medio de libreras u otros complementos. En el caso de Visual FoxPro podemos hacer uso de FLLs, DLLs y OCXs para ampliar el entorno, sin entrar en las posibilidades del DDE y del OLE... Las libreras FLLs existen desde la versin 2.5 para Windows y son libreras escritas en C especialmente para FoxPro. Estas libreras, una vez cargadas, ofrecen un conjunto de nuevas funciones para ser usadas desde nuestros programas. Las FLLs estn especialmente pensadas para programas escritos en C que tengan que hacer uso de las capacidades internas de FoxPro. En realidad son DLLs, pero tienen la posibilidad manejar bases de datos, crear variables de FoxPro o llamar a procedimientos escritos en FoxPro desde el cdigo de la librera. Sin duda la librera ms conocida de este tipo es FOXTOOLS.FLL, que es distribuida con FoxPro y que nos ofrece un buen nmero de funciones. Por otra parte, Windows dispone de un estndar para las libreras de enlace dinmico, las DLLs. No son desarrolladas para un tipo especial de lenguaje , aun cuando nacieron para ser llamadas desde C, y dan nuevas funciones a los programas que las cargan. Pueden ser escritas en C, C++, Basic, Delphi, etc.. Existen muchas libreras comerciales en formato de DLL. Windows, por su parte, ofrece una buena coleccin libreras DLLs, de hecho, todo el API de Windows o complementos como el ODBC, MAPI, etc., estn contenidos en este tipo de libreras. Por ltimo, los OCXs son objetos que haciendo uso del las capacidades del OLE permiten ampliar los elementos que podemos incluir en nuestras Forms. Son la nueva versin de los famosos VBX y en breve se van a popularizar muchsimo. Algunos de estos objetos son fundamentalmente visuales como los MSOUTL32.OCX y PICCLP32.OCX incluidos en VFP. Otros OCXs son no visuales y funcionan de forma similar a las libreras, pero con orientacin a objeto. Ejemplo de este tipo de controles son los MSCOMM32.OCX y MSMAPI32.OCX de VFP. En el futuro el API de Windows sern mtodos de los objetos OLE del sistema, pero de momento son DLLs con una inmensa serie de funciones. En nuestro caso nos vamos a centrar estas funciones. FoxPro 2.x En FoxPro 2.x para Windows no es posible hacer uso directo del cdigo de una DLL y es necesario cargar la librera FOXTOOLS.FLL para registrar y llamar a las funciones de las libreras de enlace dinmico. La librera FoxTools se carga con la sencilla orden SET LIBRARY TO FOXTOOLS.FLL. Si utilizamos la orden DISPLAY STATUS veremos que son muchas las funciones contenidas en FoxTools, pero las que nos interesan ahora son slo dos : RegFn() y CallFn(). Con RegFn registramos una funcin contenida en una DLL. Debemos pasar el nombre de la funcin (con las maysculas y minsculas correctamente situadas),

los tipos de los datos que la funcin recibe como parmetros, el tipo de dato que se va a devolver y por ltimo el nombre de la DLL, segn este esquema : NumFuncion = RegFn( NombreFuncion, ; TiposArgumentos,; TipoRetorno, ; NombreDLL ) Con la funcin CallFn podemos llamar a las funciones registradas con RegFn. En este caso debemos pasar el nmero de funcin que nos retorn RegFn y los parmetros que hemos registrado. Se nos retornar el valor que devuelva la funcin de la DLL, que tambin debemos haber registrado. Retorno = CallFN( NumFuncion, ; Arg1, ; Arg2, .... ) FoxTools en Visual FoxPro 3.0 : 16 y 32 bits En la versin 3.0 tambin podemos hacer uso de la librera FoxTools, pero debemos tener en cuenta las diferencias entre las libreras construidas en 32 bits y en 16 bits. Para llamar a libreras de 16 bits desde aplicaciones de 32 bits como Visual FoxPro es necesario utilizar un mecanismo llamado Universal Thunk y este mecanismo slo lo podemos utilizar por medio de FoxTools. Por lo tanto, para llamar desde Visual FoxPro a funciones contenidas en DLLs de 16 bits debemos usar FoxTools (a excepcin de las llamadas al API de Windows 3.1, pues en este caso la conversin entre 16 y 32 bits la realiza Win32s). Tambin podemos usar FoxTools para registrar libreras de 32 bits por medio de la funcin RegFn32(). La funcin RegFn32 es igual que RegFn, pero siempre llama a funciones de 32 bits, independientemente de la plataforma en la que nos encontremos. Si queremos llamar a libreras de 16 bits siempre deberemos usar RegFn. En los dos casos las llamadas a las funciones registradas se hace con CallFn(). Con todo, el uso de FoxTools no es el camino ms corto para llamar a funciones de las DLLs de 32 bits desde Visual FoxPro, existe otro mucho mejor... Declaracin de funciones en Visual FoxPro En Visual FoxPro podemos llamar a funciones de las DLLs de 32 bits sin necesidad de la librera FoxTools por medio de la orden DECLARE. Con ella podemos registrar las funciones de las DLLs sin necesidad de ninguna librera externa. DECLARE [cTipoRetorno] NombreFuncion IN NombreLibreria [AS NombreAlias] [cTipoParam1 [@] NombreParam1, cTipoParam2 [@] NombreParam2, ...] Debemos indicar el nombre de la funcin distinguiendo entre maysculas y minsculas. Si este nombre no es vlido en VFP o no nos gusta podemos darle un ALIAS, es decir, el nombre con el que vamos a llamar a la funcin registrada dentro de VFP.

Tambin debemos indicar el tipo del valor de retorno y los tipos de los parmetros. Los posibles tipos de datos son : SHORT - Entero de 16 bits INTEGER - Entero de 32 bits SINGLE - Coma flotante de 32 bits DOUBLE - Coma flotante de 64 bits STRING - Cadena de caracteres Si algn parmetro debe ser pasado por referencia, indicaremos este hecho con un @ despus del tipo del parmetro. Tambin podemos dar un nombre a los parmetros, pero Visual FoxPro no lo requiere y no tiene ms utilidad que darnos alguna ayuda para recordar el contenido de los parmetros. Con la clusula IN damos el nombre de la DLL que deseamos utilizar. Si en vez del nombre de la librera indicamos WIN32API la funcin declarada es buscada en las libreras del sistema. Por medio de DISPLAY STATUS o LIST STATUS se muestran los nombres de las funciones registradas. Con CLEAR ALL o CLEAR DLLS se eliminan de la memoria las funciones registradas. Entrando en materia : llamar al API de Windows Gracias a los distintos medios que tenemos para llamar a las funciones contenidas en las DLLs podemos llamar a las funciones del API de Windows. Para ello debemos registrar o declarar las funciones del API de Windows. Si en utilizamos la FoxTools sobre plataformas Win16 y no incluimos el nombre de la DLL que contiene la funcin, entonces la librera busca la funcin automticamente en las libreras USER.EXE, KRNL386.EXE y GDI.EXE. Estos ficheros son las DLLs que contienen el API de Win16. Si utilizamos RegCall32 de FoxTools y no indicamos el nombre de la librera o utilizamos la orden DECLARE con el modificador IN WIN32API y no el nombre de una DLL, entonces se busca la funcin en las libreras KERNEL32.DLL, GDI32.DLL, USER32.DLL, ADVAPI32.DLL y MPR.DLL, los ficheros que contienen el API del Win32. En el caso de estar utilizando Visual FoxPro sobre Windows 3.1 con la librera Win32s, e indicamos WIN32API, entonces se buscar la funcin en el fichero W32SCOMB.DLL, la librera de conversin del API de Win32 a Win16. En todos los casos, una vez registrada la funcin ya podemos hacer uso sin ms de ella. Un ejemplo : Atributos de Fichero Para abrir boca vamos a ver dos funciones muy simples del API de Windows para obtener y modificar los atributos de un fichero. Todos los ejemplos se van a codificar para Visual FoxPro, pero no es muy difcil pasar el cdigo a FoxPro 2.x usando la librera FoxTools. Las funciones que nos interesan se declaran como sigue :

*** Obtiene los atributos de un fichero *** DECLARE INTEGER GetFileAttributes ; IN WIN32API ; STRING cFileName *** Cambia los atributos de un fichero *** DECLARE INTEGER SetFileAttributes ; IN WIN32API ; STRING cFileName, ; INTEGER nFileAttributes Con GetFileAttributes se obtiene un nmero que indica los atributos que tiene un fichero del que se ha pasado su nombre como parmetro. Para saber realmente que atributos tiene podemos hacer uso de estas simples definiciones : #define #define #define #define #define #define _A_RDONLY _A_HIDDEN _A_SYSTEM _A_SUBDIR _A_ARCH _A_NORMAL 1 2 4 16 32 128

A partir de aqu podemos utilizar una nueva funcin de Visual FoxPro BITAND, que hace un AND binario para saber si contiene uno de estos valores : nAttrib=GetFileAttributes("c:\command.com") IF nAttrib == -1 ERROR ENDIF IF BITAND( nAttrib, _A_RDONLY ) != 0 WAIT WIND "El de slo lectura..." ENDIF IF BITAND( nAttrib, _A_HIDDEN ) != 0 WAIT WIND "El de slo lectura..." ENDIF ... Si por algn motivo la funcin no tiene xito devuelve un -1. Debemos comprobar siempre que el retorno ha sido distinto de -1 antes de comprobar el valor de retorno de la funcin. Para dar unos nuevos atributos a un fichero haremos uso de SetFileAttributes pasando el nombre del fichero y los atributos que deseamos que tenga el fichero como un nmero compuesto de las sumas de los atributos (si no incluimos un atributo significa que se lo quitamos) : =SetFileAttributes( "c:\command.com", ; _A_RDONLY + ; _A_ARCH ) En este caso si la funcin no tiene xito devuelve un 0. Si todo ha sido correcto el retorno es de un 1. Funcin de VFP o funcin del API de Windows En muchas ocasiones nos encontraremos que ya existe una funcin de VFP para hacer lo que queremos hacer por medio de una funcin del API de Windows. En

este caso podemos obtener los atributos de un fichero por medio de la funcin ADIR() de VFP y no necesitamos llamar a GetFileAttributes() del API de Windows. En VFP no existe una funcin para dar atributos a un fichero y es necesario utilizar el API, una librera externa o hacer una llamada a la orden ATTRIB del MS-DOS. Personalmente prefiero utilizar primero las funciones de FoxPro y si no hay una funcin en VFP entonces llamar a las funciones del API, si no existe tampoco en el API de Windows entonces crear una librera y si no hay ms remedio llamar a una orden del MS-DOS. Pero cada uno deber decidir que quiere hacer. Vamos a ver un pequeo ejemplo del uso de estos API de Windows y vamos a ver como hacer uso de la funcin ADIR() : *** Cargar las definiciones necesarias *** #INCLUDE attrib.h *** Cargar las declaraciones del API *** DO attrib *** Crear un fichero para el ejemplo *** LIST STATUS TO FILE ver.txt NOCONSOLE *** Cambiar los atributos del fichero *** IF SetFileAttributes( "ver.txt", ; _A_RDONLY + ; _A_HIDDEN + ; _A_SYSTEM + ; _A_ARCH ) == 1 WAIT WIND "Cambiados los atributos" ELSE WAIT WIND "No se han podido cambiar" ENDIF *** Obtener los atributos del archivo *** *** con la funcin del WIN32API *** nAttrib = GetFileAttributes( "ver.txt" ) *** Mostrar los atributos *** IF nAttrib # -1 ? "Atributos con WIN32API : " IF BITAND( nAttrib, _A_RDONLY ) != 0 ?? "R" ENDIF IF BITAND( nAttrib, _A_ARCH ) != 0 ?? "A" ENDIF IF BITAND( nAttrib, _A_SYSTEM ) != 0 ?? "S" ENDIF IF BITAND( nAttrib, _A_HIDDEN ) != 0 ?? "H" ENDIF IF BITAND( nAttrib, _A_SUBDIR ) != 0 ?? "D" ENDIF IF BITAND( nAttrib, _A_NORMAL ) != 0 ?? "N" ENDIF *** Error *** ELSE

WAIT WIND "Error con GetFileAttributes" ENDIF *** Obtener los atributos del archivo *** *** con la funcin del VFP *** nFichs = ADIR( aFicheros, ; "ver.txt", ; "AHRSD" ) *** Mostrar los atributos *** IF nFichs > 0 ? "Atributos con ADIR : " ?? aFicheros[1,5] *** Error *** ELSE WAIT WIND "Error con ADIR" ENDIF Como se puede observar, el resultado de GetFileAttributes y el de ADIR() son el mismo, con la ventaja de que ADIR() puede dar el resultado de varios ficheros. Cubrir el API con funciones propias Un buen sistema es cubrir (o encapsular) las funciones del API de Windows o de las libreras que usemos, por medio de funciones propias, que permiten independizar nuestro cdigo del acceso directo a las mimas y hacer funciones de mayor nivel y por lo tanto con mayor funcionalidad. Para dar un ejemplo de esto vamos a hacer una funcin llamada SetAttri a la que se le pasa como primer parmetro el nombre del fichero o una mscara y como segundo una cadena con los atributos que deseamos que tenga el fichero (tal y como se devuelven por ADIR) : PROCEDURE SETATTRI LPARAMETER cFicheros, cAtributos *** Cargar las definiciones necesarias *** #INCLUDE attrib.h *** Cargar las declaraciones del API *** DO avttrib *** Incializar variables como locales *** LOCAL nCambiados, nAtrib, nFichs LOCAL ARRAY aEncontrados(1,1) nCambiados = 0 *** Obtener los ficheros *** nFichs = ADIR( aEncontrados, ; cFicheros, ; "AHRS" ) *** Tratar cada uno de los ficheros *** FOR nCont = 1 TO nFichs *** Cargar el atributo *** nAtrib = 0 IF "R" $ UPPER( cAtributos) nAtrib = nAtrib + _A_RDONLY ENDIF IF "A" $ UPPER( cAtributos) nAtrib = nAtrib + _A_ARCH

ENDIF IF "S" $ UPPER( cAtributos) nAtrib = nAtrib + _A_SYSTEM ENDIF IF "H" $ UPPER( cAtributos) nAtrib = nAtrib + _A_HIDDEN ENDIF IF "D" $ UPPER( cAtributos) nAtrib = nAtrib + _A_SUBDIR ENDIF IF "N" $ UPPER( cAtributos) nAtrib = nAtrib + _A_NORMAL ENDIF *** Cambiar los atributos *** IF SetFileAttributes( ; aEncontrados[nCont,1], ; nAtrib ) == 1 nCambiados = nCambiados + 1 ENDIF NEXT RETURN nCambiados Conclusin Espero que esta primera aproximacin a la utilizacin del API de Windows desde Visual FoxPro haya sido de inters. De momento slo hemos visto como registrar y llamar a estas funciones y hemos descrito dos funciones muy simples, pero en el prximo artculo estudiaremos otras funciones muy tiles. Si no puede esperara hasta recibir el prximo nmero de la revista puede ver el fichero de ayuda sobre el API de Win32 de la versin profesional en CD-ROM de Visual FoxPro o bien la magnfica informacin y ejemplos contenidos en los CD-ROM del Microsoft Developer Network (MSDN) o los manuales de Visual C++ 2.0.

Uso del API de Windows : Funciones para ficheros .INI (II)


Tal y como prometimos en el artculo anterior, vamos a entrar a fondo en algunas funciones realmente tiles del API de Windows para ser usadas desde Visual FoxPro (VFP). En este caso vamos a referirnos al conjunto de funciones que nos permiten crear y consultar los ficheros de configuracin de Windows, los ficheros .INI. Donde guardar nuestra configuracin? Antes o despus todos nos hacemos esta pregunta, queremos guardar informacin sobre la posicin de una ventana, el ltimo usuario que entr en la aplicacin, guardar las distintas opciones, etc.. Las respuestas pueden ser muy variadas, pero en principio existen tres mtodos : Bases de Datos : con este sistema guardamos nuestra configuracin en una tabla creada por nosotros con este fin. En ella almacenamos toda la informacin que necesitamos para configurar la aplicacin, con las ventajas que da el uso de un tabla de FoxPro. El problema es que no podemos editarla con facilidad. Si necesitamos ver o modificar el contenido del fichero de configuracin debemos o bien ejecutar el VFP y

abrir la tabla o bien hacer alguna pantalla especial de mantenimiento para la misma. Un buen ejemplo de este tipo de configuracin es el fichero de recursos de Visual FoxPro, normalmente llamado FOXUSER.DBF y donde FoxPro guarda gran parte de los datos sobre su propia configuracin. [Nota : nosotros podemos tambin utilizar el fichero de recursos para nuestros intereses abrindolo con USE AGAIN e introduciendo en l lo que queramos. Este fichero no tiene por qu llamarse FOXUSER, basta con utilizar SET RESOURCE TO para utilizar otro.] Ficheros .INI : el sistema que habitualmente utilizan los programas de Windows para almacenar los datos de su configuracin son los ficheros .INI, normalmente residentes en el directorio donde est instalado Windows. Si hacemos una breve visita a nuestro directorio de Windows podremos encontrar un buen nmero de estos ficheros. En concreto yo he encontrado 97 ficheros .INI en el equipo donde escribo estas lneas. Los ficheros .INI son ficheros de texto y, en principio, son muy fciles de modificar con un simple editor. Pero su estructura es en muchas ocasiones excesivamente simple, cuando se hacen muy grandes puede ser algo lento buscar dentro de ellos. Pero sin duda son el sistema ms difundido. El Registro de Configuraciones : la proliferacin de ficheros .INI y su escasa estructuracin hicieron que Microsoft se plantease dar un sistema mucho ms prctico para guardar los datos de configuracin de las aplicaciones. As, partiendo del Registro de Windows 3.1, en Windows NT y Windows 95 se nos ofrece un autntico Registro de Configuraciones. El Registro es una base de datos jerrquica en la que el sistema y las aplicaciones puede, y deben, guardar la informacin necesaria para interactuar con otros programas y para su propia configuracin. Como el sistema de Registro est muy limitado en Windows 3.1, es necesaria la compatibilidad con los ficheros .INI, y Windows 95 y Windows NT siguen soportndolos, duplicando en muchos casos los datos del registro en este tipo de ficheros. En nuestro caso vamos a hacer una aproximacin a los ficheros .INI, en otros artculos estudiaremos el Registro y las funciones para manejarlo. Estructura de los ficheros .INI Como hemos dicho, los ficheros de configuracin de Windows son ficheros de texto con la extensin INI y almacenados, casi siempre, en el directorio donde se encuentre instalado Windows, aunque pueden estar en cualquier otro directorio. La estructura de los ficheros INI se organiza en Secciones. Las secciones estn identificadas con una etiqueta entre corchetes y agrupan informacin relacionada entre si. Dentro de una seccin se incluyen Claves, es decir, lneas de con un identificador, un signo igual y un valor.

Un fichero .INI muy simple podra ser : [Ventana Principal] Tipo de Ventana=1 Abrir=0 Ttulo=Pantalla Principal Nivel=0 [Usuario] Nombre=Pablo Almunia Sanz Como podemos ver los ficheros de configuracin pueden tener varias secciones, los identificadores de seccin o de claves pueden contener espacios o caracteres acentuados, pero cuando accedamos a ellos deberemos dar su nombre exacto, sin importar las diferencias entre maysculas y minsculas. Los valores asociados a las claves siempre se guardan en formato de cadenas de caracteres, aun cuando sean nmeros, valores lgicos, fechas, etc.. WIN.INI y SYSTEM.INI En Windows 3.1 existen dos ficheros .INI que configuran el sistema, son los conocidos WIN.INI y SYSTEM.INI. En estos dos ficheros se guarda casi toda la informacin necesaria para el sistema. WIN.INI es normalmente el fichero de configuracin ms grande de todos los equipos. En principio este fichero est reservado para las configuraciones general de Windows y para las configuraciones que deban ser compartidas por ms de una aplicacin. Pero muchos programas deciden no tener su propio fichero .INI y utilizar este fichero para su configuracin. Nosotros deberemos ser elegantes y utilizar WIN.INI slo para las informacin que deban compartir ms de una aplicacin y no para los datos de configuracin privados. En WIN.INI se almacena informacin muy interesante sobre las fuentes, extensiones de ficheros registrados, internacionalizacin, etc.. Usada con cuidado esta informacin puede ser muy til para nuestra aplicacin. En SYSTEM.INI se definen todos los controladores, la configuracin de la red, etc.. En muchas ocasiones el mejor sistema para saber si est instalado un determinado dispositivo es consultar este fichero, pero no debemos modificarlo si no sabemos realmente lo que estamos haciendo pues es un fichero difcil. Tanto WIN.INI como SYSTEM.INI son soportados por Windows 95 y Windows NT. Aun cuando estos sistemas guardan su informacin en el Registro de Configuraciones, mantienen una copia de los datos del registro dentro de los ficheros .INI, pues saben que muchas aplicaciones acceden directamente a ellos. Antes de lanzarnos a trabajar con SYSTEM.INI y WIN.INI desde VFP recomiendo que nos demos un paseo por la documentacin viendo funciones como AFONT(), APRINTERS(), SYS(), FONTMETRIC(), PTRINFO(), SYSMETRIC(), SET SYSFORMAT, etc. que nos ofrecen la mejor manera de trabajar con la informacin del sistema. [Nota : si desea informacin detallada sobre la estructura de los ficheros de configuracin de Windows puede acudir a los ficheros SYSINI.WRI y WININI.WRI de

vienen en la instalacin de Windows, la amplia documentacin del Windows Resource Kit y a los excelentes CD-ROMs del Microsoft TechNet.] Los ficheros .INI de las aplicaciones En la mayora de los casos las aplicaciones Windows tienen ficheros .INI para su configuracin. La estructura de estos ficheros .INI varia completamente de una aplicacin a otra, dependiendo de lo que los programadores de la misma hayan decidido. Nosotros deberemos analizar la estructura de nuestro fichero de configuracin a fin de que responda a nuestras propias necesidades. No es muy fcil dar recomendaciones a este respecto, vea lo que hacen otras aplicaciones y coja las mejores ideas que observe. En cuanto a acceder a los ficheros .INI de otras aplicaciones debemos tener algunas reservas. No es conveniente trastear desde nuestra aplicacin en los .INI de otras aplicaciones si no se nos ofrece documentacin muy clara al respecto. Muchos programas pueden dejar de funcionar correctamente o podemos interpretar errneamente la informacin en ellos contenida. Pero en algunos casos las aplicaciones dan informacin sobre como interpretar e incluso modificar la informacin de sus ficheros de configuracin y no tenemos porque temer nada. [Nota : si desea informacin sobre FOXPRO.INI consulte el documento de Microsoft Q107830.] Como acceder a ficheros .INI desde VFP Visual FoxPro no dispone ordenes para acceder a los ficheros .INI directamente y debemos utilizar algn otro sistema. Podemos construirnos nosotros mismos algunas funciones a base de abrir los ficheros a bajo nivel con FOPEN(), leer con FREAD() y escribir con FWRITE(). No es muy sencillo, pues debemos reconocer las distintas estructuras de los ficheros .INI y puede ser problemtico en algunas ocasiones. Las ltimas versiones de la librera FOXTOOLS disponen dos funciones, GetProStr y PutProStr para leer y escribir en WIN.INI. En mi opinin son unas funciones bastante limitadas, pero podemos utilizarlas sin problemas. Existe otro sistema, bastante sencillo y muy potente, utilizar el API de Windows. Este API nos proporciona un amplio conjunto de funciones y para leer, escribir y borrar el contenido de cualquier fichero .INI. Vemoslo : Leer cadenas de ficheros .INI El API de Windows nos ofrece una funcin para consultar el valor de una clave dentro de una seccin de un fichero .INI como una cadena de caracteres. Su declaracin es : DECLARE ; INTEGER GetPrivateProfileString ; IN WIN32API; STRING cSeccion, ; STRING cClave, ;

STRING cDefecto, ; STRING @cCadenaRetorno, ; INTEGER nTama, ; STRING cNombreFichero A esta funcin le debemos pasar la seccin que consultamos, la clave que queremos obtener, un valor por defecto si no encuentra esta clave, seccin o fichero, un buffer para la cadena de retorno, el tamao de este buffer y el nombre del fichero INI con extensin (el path no es necesario si se encuentra en el directorio de Windows). La funcin devuelve 0 si existe algn problema o, si todo a funcionado correctamente, el tamao de la cadena incluida en el buffer. Si se almacena en el buffer el valor por defecto, que le hemos indicado para el caso de no encontrar el fichero, la seccin o la clave, se retorna el tamao de esta cadena. En ambos casos no se incluye en el tamao el carcter nulo -CHR(0)- de fin de cadena. Veamos un pequeo ejemplo : *** Cargar las declaraciones *** DO profiles.prg *** Creacin de la variable de retorno *** LOCATE cRetorno, nRet cRetorno = REPLICATE( CHR(0), 255 ) *** Consultar el ratn instalado *** nRet = GetPrivateProfileString( ; "boot.description", ; "mouse.drv", ; "", ; @cRetorno, ; 255, ; "system.ini" ) IF nRet > 0 ? "Ratn instalado : " ?? SUBSTR( cRetorno, ; 1, ; AT( CHR(0), cRetorno ) -1 ) ENDIF Debemos fijarnos en que la cadena que pasamos debe tener un cierto tamao reservado, sino podemos provocar un error. Este tamao puede ser rellenado por cualquier carcter, pero prefiero utilizar el carcter nulo. La cadena retornada est siempre terminada por un carcter nulo -CHR(0)-, que no es necesario en Visual FoxPro, por ello es conveniente hacer siempre un SUBSTR hasta el primer nulo que encontremos. Los ms observadores se habrn percatado que los parmetros de tipo cadena que nosotros pasamos a las funciones del API de Windows no tienen el carcter nulo final, preceptivo en C. No hay ningn problema, Visual FoxPro lo incluye por nosotros. Leer enteros de ficheros .INI Si lo que queremos hacer es obtener un dato que sabemos que es un nmero entero podemos utilizar esta funcin :

DECLARE ; INTEGER GetPrivateProfileInt ; IN WIN32API ; STRING cSeccion, ; STRING cClave, ; INTEGER nValorDefecto, ; STRING cNombreFichero Debemos tener cuidado en el tipo de valor que podemos obtener, pues siempre debe ser numrico y que quepa en un entero de 32 bits. Por ejemplo, un nmero con decimales no puede ser obtenido por este sistema y deber ser ledo como cadena y luego convertido. En esta funcin, como en la de lectura de cadenas, debemos indicar un retorno por defecto si no se encuentra la clave, la seccin o el fichero. Debemos elegir correctamente este valor de retorno a fin de poder controlar los posibles errores o bien siempre retorne un valor vlido. Leer una seccin completa Si preferimos leer una seccin entera y no clave a clave podemos utilizar esta funcin : DECLARE ; INTEGER GetPrivateProfileSection ; IN WIN32API ; STRING cSeccion, ; STRING @cCadenaRetorno, ; INTEGER nTama, ; STRING cNombreFichero La cadena de retorno contendr todas las claves de la seccin, incluido el identificador, el signo igual y el valor, separadas las lneas por caracteres nulos y finalizando la cadena con dos caracteres nulos. Esta funcin es una clara candidata a estar encapsulada en una funcin de mayor nivel que, por ejemplo, ponga todas las lneas de una seccin dentro de un matriz de dos dimensiones, donde la primera columna son las claves y la segunda los valores. Esta funcin puede ser : PROCEDURE GtIniSec LPARAMETER cSeccion, cArray, cNombreFich *** Tamao del buffer (puede cambiarse) *** #define MAX_SECTION 5120 *** Todas la variables locales *** LOCAL cBuffer, cTmp LOCAL nResult, nCont LOCAL nInicioLinea, nFinLinea, nPosIgual *** Cargar las declaraciones *** DO profiles *** Preparar bufferr *** cBuffer = REPLICATE( CHR(0), MAX_SECTION ) *** Obtener todo la seccin *** nResult = GetPrivateProfileSection( ; cSeccion, ; @cBuffer, ; MAX_SECTION, ; cNombreFich )

*** Comprobar si existen errores *** IF nResult == 0 RETURN 0 ENDIF *** Inicializar variables *** nInicioLinea = 1 nFinLinea = 1 nCont = 0 cTmp = "" *** Procesar el resultado *** DO WHILE nFinLinea < nResult *** Aumentar el contador de nulos *** nCont = nCont + 1 *** Posicin del prximo nulo *** nFinLinea = AT( CHR(0), cBuffer, nCont ) *** Cadena de una lnea *** cTmp = SUBSTR( cBuffer, ; nInicioLinea, ; nFinLinea - nInicioLinea ) *** Redimensionar el array *** PUBLIC ARRAY &cArray.[nCont,2] *** Posicin del igual *** nPosIgual = AT( '=', cTmp ) *** Cargar la clave *** &cArray.[nCont,1] = ; SUBSTR( cTmp, 1, nPosIgual - 1 ) *** Cargar el valor si existe *** IF nPosIgual < LEN( cTmp ) &cArray.[nCont,2] = ; SUBSTR( cTmp, nPosIgual + 1 ) ELSE &cArray.[nCont,2] = "" ENDIF *** El incio de la siguiente lnea *** *** es el fin de esta *** nInicioLinea = nFinLinea + 1 ENDDO *** Retornar el nmero de lneas *** RETURN nCont Para utilizar esta funcin, que sin duda es mejorable, debemos pasar el nombre de la seccin, el nombre de la matriz y el nombre del fichero, algo como esto : ? ASECTION( "windows", "aVer", "win.ini" ) DISPLAY MEMORY LIKE aVer Recomiendo sin reservas el encapsular las llamadas del API de Windows dentro de funciones escritas por nosotros mismos, de esta forma, no slo damos una mayor funcionalidad a nuestros programas, sino que aislamos nuestro cdigo antes posibles cambios en el API. Siguiente Artculo

En este artculo slo nos ha dado espacio para ver las funciones de lectura de ficheros .INI. En la prxima entrega veremos las funciones de escritura y un ejemplo completo del uso de estas funciones.

Uso del API de Windows : Funciones para ficheros .INI (III)


En el artculo anterior vimos las funciones del API de Windows para leer ficheros .INI, ahora vamos a dar un repaso a la funciones de escritura y completaremos las funciones que manejan ficheros de configuracin. Escribir cadenas en ficheros .INI La funcin de escritura de ficheros .INI ms utilizada es, sin duda, la de escritura de cadenas de caracteres. Esta funcin se declara en Visual FoxPro as : DECLARE ; INTEGER WritePrivateProfileString ; IN WIN32API ; STRING cSeccion, ; STRING cClave, ; STRING cCadena, ; STRING cNombreFichero A la funcin le pasamos la seccin, la clave, el valor y el nombre del fichero. La cadena del valor no tiene que pasarse por referencia, es decir, no pasamos un buffer, sino simplemente el valor como una cadena. Si la funcin por algn motivo no ha podido escribir dentro del fichero devuelve un 0, si todo ha ido correctamente devuelve un 1. Escribir enteros en ficheros .INI No existe una funcin especfica para la escritura del enteros dentro de los ficheros .INI, aunque exista una funcin para su lectura. Por lo tanto debemos convertir los valores numricos en cadenas antes de escribir por medio de GetPrivateProfileString. Es una buena idea construir una funcin para encapsular esta transformacin. Esta funcin podra llamarse WrIniInt y sera similar a esta : PROCEDURE WrIniInt LPARAMETER cSeccion, cClave, ; nValor, cNombreFichero RETURN GetPrivateProfileString( ; cSeccion, ; cClave, ; LTRIM( STR( nValor ) ), ; cNombreFichero ) Es una funcin muy sencilla, pero puede ser de utilidad si tenemos que escribir muchos enteros en los ficheros de configuracin.

De igual forma, si tenemos guardar muchos nmeros con decimales, fechas o cualquier otro tipo de dato podemos crear nuestras propias funciones para hacer ms sencilla y rpida la escritura y lectura de ficheros .INI. Escribir secciones en ficheros .INI Al igual que podemos leer toda una seccin podemos escribirla. La nica dificultad es que debemos pasar una cadena con un formato algo especial : debe contener todas las claves, cada una compuesta por del identificador, el signo igual y el valor, separadas entre si con un carcter nulo. Para escribir la seccin utilizaremos la funcin : DECLARE ; INTEGER WritePrivateProfileSection ; IN WIN32API ; STRING cSeccion, ; STRING cCadena, ; STRING cNombreFichero Al igual que en el caso de lectura de secciones, esta funcin es una clara candidata a ser recubierta por una funcin escrita por nosotros que, por ejemplo, reciba la seccin, una matriz por referencia y el nombre del fichero, construya el buffer necesario y llame a la funcin del API de Windows. Podra ser algo similar a esta : PROCEDURE WrIniSec LPARAMETER cSeccion, aDatos, cNombreFichero LOCAL nTama, cCadena, nCont *** Obtener el tamao del array *** nTama = ALEN( aDatos, 1 ) *** Inicializar la cadena *** cCadena = '' *** Transformar en la cadena *** FOR nCont = 1 TO nTama cCadena = cCadena + ; aDatos[nCont,1] + ; '=' + ; aDatos[nCont,2] + ; CHR(0) NEXT *** Escribir y retornar *** RETURN WritePrivateProfileSection( ; cSeccion, ; cTmp, ; cNombreFichero ) Tngase en cuenta que la matriz debe pasarse por referencia, es decir con un @ antes del nombre, sera algo as : DIMENSION aInf[2,2] aInf[1,1] = "Clave 1" aInf[1,2] = "1000" aInf[2,1] = "Clave 2" aInf[2,2] = "2000" ? WrIniSec( "Escritura 1", ; @aInf, ; "Ejemplos.ini" )

Cada uno puede crear su propia funcin, admitiendo los parmetros que ms le interese. Crear un fichero, una seccin o una clave No existen funciones especiales para crear ficheros .INI. Si utilizamos una funcin de escritura sobre un fichero que no existe entonces la funcin lo crea automticamente. As mismo si la seccin o la clave que pasamos a la funcin de escritura no existe esta lo crea. Debemos, por lo tanto, tener cuidado con los nombres que pasamos como parmetros en la funciones de escritura, pues un pequeo error crear un nuevo fichero, una nueva seccin o una nueva clave. Dejar una clave vaca Si queremos eliminar el valor de una clave, pero no queremos borrar la clave como tal, debemos pasar una cadena nula a la funcin de escritura de cadenas. Un ejemplo puede ser : =GetPrivateProfileString( ; "Seccin", ; "Clave", ; "", ; "ejemplo.ini" ) En este caso dejaremos una lnea con clave, un igual y sin ningn valor. [Seccin] Clave= Borrar de un fichero .INI Si lo que queremos hacer es borrar completamente la clave debemos pasar a la funcin de escritura de cadenas un cero en el parmetro del valor : =GetPrivateProfileString( ; cSeccion, ; cClave, ; 0, ; cNombreFichero ) Para eliminar toda una seccin debemos utilizar la funcin de escritura de secciones pasando como parmetro de los valores un cero : =WritePrivateProfileSection( ; cSeccion, ; 0, ; cNombreFichero ) Con este mtodo se borra tanto el contenido de la seccin, es decir, todas sus claves, como el identificador de las seccin dentro del fichero de configuracin. Borrar el fichero

Aunque borremos todas las secciones no borramos el fichero .INI. Por lo tanto, si queremos eliminar el fichero debemos hacer uso de las funciones de borrar de ficheros de Visual FoxPro, pues en este caso no existe una funcin especial del API de Windows para borrar este tipo de ficheros. Antes de lanzarnos a borrar nuestro fichero de configuracin debemos tener presente que el fichero de configuracin podemos instalarlo en el directorio de Windows o en cualquier otro directorio que nosotros elijamos. Pero si decidimos situarlo en el directorio de instalacin de Windows debemos saber que este directorio no tiene por que ser C:\WINDOWS (yo utilizo siempre C:\WIN). Para saber donde esta instalado Windows existen varios mtodos. Quizs el ms sencillo es consultar con la funcin GETENV() de FoxPro una variable del sistema llamada WINDIR y que contiene este directorio. Pero si queremos seguir haciendo uso del API de Windows podemos utilizar la siguiente declaracin : DECLARE ; INTEGER GetWindowsDirectory ; IN WIN32API ; STRING @cBuffer, ; INTEGER nTama Esta funcin espera un buffer como primer parmetro y el tamao del mismo como segundo parmetro. El buffer es conveniente que sea al menos de 255 caracteres (por si alguien es muy retorcido en eso de instalar Windows). Sea como fuere, una vez conocido el directorio donde est instalado Windows podemos utilizar la orden DELETE FILE de FoxPro para borrar nuestro fichero .INI (nunca borrar los ficheros de otras aplicaciones o del sistema, puede ser muy desagradable el resultado). Funciones del API especiales para WIN.INI El API de Windows ofrece una serie de funciones para acceder directamente al fichero WIN.INI, sin necesidad de indicar el nombre del fichero, tal y como hacamos hasta ahora. Estas funciones se comportan exactamente igual que las que hemos visto hasta ahora, pero slo acceden al fichero WIN.INI. La declaracin de estas funciones es : DECLARE ; INTEGER GetProfileInt ; IN WIN32API ; STRING cSeccion, ; STRING cClave, ; INTEGER nValorDefecto DECLARE ; INTEGER GetProfileString ; IN WIN32API ; STRING cSeccion, ; STRING cClave, ; STRING cValorDefecto, ; STRING @cCadenaRetorno, ; INTEGER nTama DECLARE ; INTEGER GetProfileSection ;

IN WIN32API ; STRING cSeccion, ; STRING @cCadenaRetorno, ; INTEGER nTama DECLARE ; INTEGER WriteProfileString ; IN WIN32API ; STRING cSeccion, ; STRING cClave, ; STRING cCadena DECLARE ; INTEGER WriteProfileSection ; IN WIN32API ; STRING cSeccion, ; STRING cCadena No existen funciones especficas para SYSTEM.INI, necesitando acceder a este fichero como si fuera un "PrivateProfile". Tambin es posible acceder al fichero WIN.INI por medio de las funciones "PrivateProfile" y no con las funciones especficas. Ejemplo : Guardar la posicin de una ventana Despus de dar un repaso a las funciones del API de Windows para manejar los ficheros .INI vamos a describir un ejemplo completo, donde se van a almacenar algunos datos sobre la ltima posicin de una ventana. Es una buena costumbre que las aplicaciones abran algunos tipos de ventanas en la ltima posicin donde el usuario las dej. A todos nos ha pasado que despus de configurar a nuestro gusto la situacin de las ventanas de una aplicacin, al volver a arrancarla las ventanas estn en su posicin inicial y no donde las dejamos nosotros, debiendo volver a situarlas. Para evitar este feo efecto podemos incluir unas pocas lneas dentro de nuestras pantallas a fin de obtener los datos de la posicin de la ventana cuando se cierra y las guardarlos en un fichero .INI. Cuando la ventana se vuelve a crear la podemos situar en la ltima posicin conocida. Un ejemplo de esta tcnica es el siguiente programa : *** Cargar la Form y mostrarla *** PUBLIC oEjemplo2 oEjemplo2 = CREATEOBJECT( "Ejemplo2" ) oEjemplo2.Show() RETURN *** Form del ejemplo en forma de clase *** DEFINE CLASS Ejemplo2 AS form BackColor = RGB(192,192,192) Caption = "Ejemplo 2 sobre INIs" *** Botn para cerrar *** ADD OBJECT cmdAceptar ; AS COMMANDBUTTON WITH ; Top = 6, ; Left = 6, ; Height = 29, ; Width = 94, ; Caption = "Aceptar" *** Evento al crear la form ***

PROCEDURE Load *** Cargar las declaraciones *** DO profiles *** Obtener los valores con *** *** los que se cerr la ltima vez *** WITH This .Top = GetPrivateProfileInt( ; "Ejemplo 2", ; "Top", ; .Top, ; "ejemplos.ini" ) .Left = GetPrivateProfileInt( ; "Ejemplo 2", ; "Left", ; .Left, ; "ejemplos.ini" ) .Width = GetPrivateProfileInt( ; "Ejemplo 2", ; "Width", ; .Width, ; "ejemplos.ini" ) .Height = GetPrivateProfileInt( ; "Ejemplo 2", ; "Height", ; .Height, ; "ejemplos.ini" ) ENDWITH ENDPROC *** Evento al destruir la form *** PROCEDURE Unload *** Cargar las declaraciones *** *** pueden haberse descargado *** DO profiles *** Si est minimizado o maximizado *** *** restaurar al tamao normal *** This.WindowState = 0 *** Guardar los valores de *** *** posicin y tamao *** WITH This = WritePrivateProfileString( "Ejemplo 2", ; "Top", ; LTRIM( STR( .Top ) ), ; "ejemplos.ini" ) = WritePrivateProfileString( "Ejemplo 2", ; "Left", ; LTRIM( STR( .Left ) ), ; "ejemplos.ini" ) = WritePrivateProfileString( "Ejemplo 2", ; "Width", ; LTRIM( STR( .Width ), ); "ejemplos.ini" ) = WritePrivateProfileString( "Ejemplo 2", ; "Height", ; RTRIM( STR( .Height ) ), "ejemplos.ini" )

; ;

ENDWITH ENDPROC *** Mtodo para cerrar la form *** PROCEDURE cmdAceptar.Click ThisForm.Release() ENDPROC ENDDEFINE En el disco de la revista se puede encontrar este ejemplo en formato .SCX adems de como cdigo. Debemos fijarnos que los valores por defecto de la funcin GetPrivateProfileInt para cuando no se encuentra el fichero, la seccin o la clave son la posicin actual de la ventana, de esta manera nunca se da un error. El fichero EJEMPLOS.INI que utilizamos en este programa no existe la primera vez que ejecutamos la form, pero al cerrar, la funcin WritePrivateProfileString crea el fichero, la seccin y las claves al no encontrarlas. En nuestra aplicacin podemos incluir el fichero .INI dentro de la instalacin. [Nota : si quiere mantener limpio su directorio Windows, borr el fichero EJEMPLOS.INI despus de las pruebas.] Dos comentarios Primero. Los nombres de las funciones del API de Windows suelen ser bastante extensos, por ejemplo, WritePrivateProfileSection tiene 26 letras. Si uno no quiere utilizar estos nombres tan largos puede hacer uso de la clusula ALIAS de la orden DECLARE. De esta forma podemos declarar esta funcin con otro nombre y llamarla de forma ms sencilla : DECLARE ; INTEGER WritePrivateProfileSection ; IN WIN32API ; ALIAS WPPS ; STRING cSeccion, ; STRING cCadena, ; STRING cNombreFichero ? WPPS( "Seccin", Buffer, "ejemplos.ini" ) Yo personalmente me he acostumbrado a los nombres del API de Windows, pero es cuestin de preferencias. Segundo. Es muy recomendable encapsular todas las funciones del API de Windows en funciones de mayor nivel. En el caso concreto de las funciones que manejan ficheros .INI es todava ms importante, pues como dijimos, estos ficheros sern sustituidos paulatinamente por el Registro de Configuraciones. Si nosotros construimos un grupo de funciones de alto nivel para guardar nuestras configuraciones, las aplicaciones no tienen por que verse afectadas por el cambio, slo estas funciones debern ser modificadas cambiando sus llamadas a las funciones que manejan .INIs por las que manejan el Registro de Configuraciones o cualquier otro sistema. Conclusin

Espero que esta aproximacin a la utilizacin del API de Windows desde Visual FoxPro haya sido de inters. Esto no se queda aqu y en el prximo artculo estudiaremos otras tiles funciones. Si no puede esperar y desea avanzar en el conocimiento del API de Windows puede ver el fichero de ayuda sobre el API de Win32 de la versin profesional en CD-ROM de Visual FoxPro o bien la magnfica informacin y ejemplos contenidos en los CDROM del Microsoft Developer Network (MSDN) o los manuales de Visual C++ 2.0 o 4.0.

Uso del API de Windows : Introduccin a las Ventanas (IV)


Despus de haber visto en los artculos anteriores los principios bsicos sobre las llamadas al API de Windows desde Visual FoxPro y las funciones para el uso de ficheros .INI, vamos a realizar una pequea introduccin a las funciones para el manejo de ventanas. No nos fijaremos tanto en manejar las ventanas creadas por nosotros en nuestra aplicacin, y que podemos manejar perfectamente desde Visual FoxPro, sino que nos centraremos en la ventana o formulario _SCREEN, es decir, en la ventana principal de Visual FoxPro. Handle de ventana Toda ventana en MS-Windows tiene un indicador nico denominado handle o manejador. Todo en MS-Windows es una ventana, un botn, un campo de edicin, o cualquier otro elemento visual de Windows es una ventana y toda ventana tiene un handle. Muchas funciones que vamos a utilizar requieren como parmetro el handle (manejador) de la ventana de Visual FoxPro o de alguna otra ventana a la que se asociar el comportamiento de esa funcin. Pero, como obtenemos el handle de una ventana de Visual FoxPro? En principio no tenemos un sistema directo para el acceso al handle de la ventana principal de Visual FoxPro, pero hay dos mtodos muy sencillos para obtener este handle. En primer lugar podemos usar la librera FOXTOOLS.FLL para usar la funcin MainHwnd(). Esta funcin devuelve el handle de la ventana principal de FoxPro. Sera algo como esto : SET LIBRARY TO FOXTOOLS.FLL ADDITIVE nHwnd = MainHwnd() Si no queremos utilizar FOXTOOLS.FLL podemos hacer uso de las funciones del API de Windows para obtener el handle de la ventana principal de Visual FoxPro. Para ello debemos declarar la funcin FindWindow(). DECLARE ; INTEGER FindWindow ; IN WIN32API ; STRING cClassName, ; STRING cWindName

Para la funcionalidad que deseamos debemos pasar en el primer parmetro un valor 0 (correspondiente a un puntero nulo) y como segundo parmetro el ttulo de la ventana principal de Visual FoxPro. Sera algo as : nHwnd = FindWindow( 0, _SCREEN.Caption ) Esta funcin, aunque tiene algunos problemas, ser utilizada desde ahora en los ejemplos para obtener el handle de la ventana principal de Visual FoxPro. Saber si la ventana de Visual FoxPro es la ventana activa Algunas veces queremos saber si nuestra aplicacin es la ventana activa, para ello podemos comprobar si la ventana principal de Visual FoxPro es la ventana activa. Utilizaremos la funcin GetForegroundWindow() del API de Windows que devuelve el handle de la ventana activa, junto con FindWindow() para obtener el handle de la ventana de Visual FoxPro. Aqu tenemos un pequeo programa de ejemplo que devuelve .T. si la ventana de nuestra aplicacin es la ventana activa y .F. si no lo es : DECLARE ; Integer GetForegroundWindow ; IN WIN32API *** Para darnos tiempo a cambiar WAIT WINDOW ; "Cambie de aplicacin y " + ; "espere unos segundos..." TIMEOUT 5 *** Obtener el handle de la ventana activa nActiveHwnd = GetForegroundWindow() *** Obtener el handle de _SCREEN nFoxHwnd = FindWindow( 0, _SCREEN.Caption ) *** Comparar y devolver el resultado ? nFoxHwnd == nActiveHwnd Es una buena idea construir una funcin similar a esta para comprobar en cualquier momento si nuestra aplicacin corresponde a la ventana activa. Activar nuestra aplicacin En Windows no todo lo que se puede hacer est bien visto que se haga. Por ejemplo, si una aplicacin no esta activa porque estamos trabajando en ese momento en otra aplicacin y nos parece la aplicacin inactiva para darnos un pequeo aviso sin importancia entonces nos molestamos. No debemos activar nuestra aplicacin continuamente para cualquier aviso que tengamos que dar, pero en algunas ocasiones podemos necesitar activar nuestra aplicacin y para ello podemos utilizar SetForegroundWindow(), una funcin del API de Windows para este fin. Esta funcin la declaramos como : DECLARE ; Integer SetForegroundWindow ;

IN WIN32API ; Integer nHwnd El programa sera algo as : WAIT WIND ; "Cambie de aplicacin y " + ; "espere unos segundos..." TIMEOUT 5 nFoxHwnd = FindWindow( 0, _SCREEN.Caption ) *** Activarla =SetForegroundWindow( nFoxHwnd ) Debemos recordar una vez ms que no debemos abusar de esta posibilidad. Situar nuestra aplicacin siempre encima Habis visto lo til que es esa especie de botn con una chincheta en el diseador de formularios para dejar por encima del resto la ventana de propiedades. Si alguno ha buscado en los manuales habr encontrado la propiedad AlwayOnTop para fijar este tipo de comportamiento. Pero si alguien ha intentado utilizar esta propiedad con el formulario _SCREEN, es decir, con la ventana principal de Visual FoxPro, habr comprobado que no hace nada. Esto se debe a que la pantalla principal de Visual FoxPro (en esta versin) es un formulario, pero un formulario bastante especial. En las versiones previas al lanzamiento del producto esta propiedad funcionaba, pero por una medida de diseo se eliminaron todas las propiedades y mtodos de _SCREEN que consideraron podan ser 'peligrosas'. Si de todas formas queremos hacer que nuestra aplicacin este 'On Top', es decir, por encima de todas las dems aplicaciones, podemos echar mano al API de Windows. En este caso, como en el anterior, debemos advertir que aun cuando sea posible, no es un comportamiento 'elegante' y debemos utilizarlo slo cuando sea realmente importante. Para realizar esto necesitamos utilizar la funcin SetWindowPos() del API de Windows. Con esta funcin se pueden hacer muchsimas cosas con las ventanas, no vamos a explicar todas, pero si tiene curiosidad y puede hojear la documentacin sobre el API de Windows comprobar muchos otros usos de esta funcin. De momento slo la usaremos para situar nuestra ventana fija por encima de todas las dems. La funcin se declara de la siguiente manera : DECLARE ; Integer SetWindowPos ; IN WIN32API ; Integer nWnd, ; Integer nWndInsertAfter, ; Integer nTop, ; Integer nLeft, ; Integer nHeight, ; Integer nWidth, ; Integer nFlags

Adems debemos declarar algunas constantes y ya podemos hacer uso de la funcin : #define #define #define #define SWP_NOSIZE SWP_NOMOVE HWND_TOPMOST HWND_NOTOPMOST 1 2 -1 -2

nFoxHwnd = FindWindow( 0, _SCREEN.Caption ) *** Pasar a primer plano fijo IF 1 = SetWindowPos( nFoxHWND, ; HWND_TOPMOST, ; 0,0,0,0, ; SWP_NOSIZE + ; SWP_NOMOVE ) *** Cambio meramente informativo _SCREEN.AlwaysOnTop = .T. ENDIF La funcin SetWindowPos() requiere como primer parmetro el handle de la ventana que queremos modificar, en el segundo la constante para hacer esa ventana siempre visible por encima de las dems, los cuatro siguientes seran la posicin y tamao de la ventana pero en el ltimo parmetro hemos pasado las constantes para hacer que no se tenga en cuenta la posicin y tamao de la ventana y que slo se tenga en cuenta el situar la ventana en primer plano de forma fija. El cambio que realizamos en la propiedad AlwaysOnTop del formulario _SCREEN es meramente informativo, pues no realiza ningn efecto real sobre la ventana principal de Visual FoxPro. Para deshacer esta situacin por encima de todas las ventanas debemos llamar a la funcin con el parmetro HWND_NOTOPMOST de la siguiente manera : SetWindowPos( nFoxHWND, ; HWND_NOTOPMOST, ; 0,0,0,0, ; SWP_NOSIZE + SWP_NOMOVE ) Deberamos tambin cambiar la propiedad AlwaysOnTop del formulario _SCREEN para mantener la coherencia entre la situacin real de la ventana y esta propiedad. Ocultar la ventana principal de Visual FoxPro Otra propiedad de _SCREEN que no tiene efecto real sobre esta ventana es Visible. Si asignamos el valor .F. a la propiedad Visible deberamos hacer ese formulario oculto, pero con _SCREEN esto no funciona. De igual forma el mtodo Hide() no funciona con _SCREEN. Fueron precisamente esta propiedad y este mtodo los que provocaron en versiones previas al lanzamiento del producto que se replantease un buen nmero de funcionalidades de _SCREEN. De todas formas podemos hacer uso de las funciones del API de Windows para ocultar la ventana principal de Visual FoxPro. Debemos tener cuidado, pues debemos escribir un cdigo que nos asegure que Visual FoxPro se volver a hacer

visible o bien termine. Si no lo hacemos as tendremos una aplicacin activa, consumiendo recursos, y a la que no podemos acceder. La funcin que utilizaremos ser : DECLARE ; Integer ShowWindow ; IN WIN32API ; Integer nWnd, ; Integer nCmdShow Con esta funcin podemos cambiar el estado de una ventana dentro de una amplia gama de posibilidades. Como primer parmetro le tenemos que pasar el handle de la ventana y como segundo parmetro la accin que queremos realizar. Para facilitar el trabajo estas son las acciones soportadas por la funcin definidas en constantes : #define #define #define #define #define #define #define #define #define #define #define #define #define #define SW_HIDE SW_SHOWNORMAL SW_NORMAL SW_SHOWMINIMIZED SW_SHOWMAXIMIZED SW_MAXIMIZE SW_SHOWNOACTIVATE SW_SHOW SW_MINIMIZE SW_SHOWMINNOACTIVE SW_SHOWNA SW_RESTORE SW_SHOWDEFAULT SW_MAX 0 1 1 2 3 3 4 5 6 7 8 9 10 10

Para ocultar la ventana podemos usar un programa similar a este : nFoxHwnd = FindWindow( 0, _SCREEN.Caption ) nOldWindowState = _SCREEN.WindowState *** Oculta la ventana =ShowWindow( nFoxHwnd, SW_HIDE ) *** Mostrar un mensaje =MessageBox( ; "Ahora Visual FoxPro esta invisible..." ) *** Resturar la ventana DO CASE CASE nOldWindowState = 0 =ShowWindow( nFoxHwnd, SW_SHOWNORMAL ) CASE nOldWindowState = 1 =ShowWindow( nFoxHwnd, SW_SHOWMINIMIZED ) CASE nOldWindowState = 2 =ShowWindow( nFoxHwnd, SW_SHOWMAXIMIZED ) ENDCASE Ocultar la ventana principal de Visual FoxPro provoca que todas las ventanas hijas, es decir las que estn dentro de la ventana de Visual FoxPro, se hagan invisibles. Las ventanas definidas como Desktop seguirn siendo visibles, como un MessageBox().

Utilizar funciones de VFP para manejar la ventana principal No debemos caer en la tentacin de utilizar el API de Windows para hacer cosas que perfectamente podemos hacer con Visual FoxPro sin necesidad del acceso directo al API. Puede parecer que siempre es necesario el uso del API, pero Visual FoxPro nos ofrece excelentes mecanismos para controlar su ventana principal. Por medio de la funcin SetWindowPos() podemos cambiar la posicin de nuestra ventana, pero es mucho ms sencillo y ms correcto hacerlo por medio de las propiedades Top, Left, Height y Width de la ventana principal de Visual FoxPro. Tambin podemos usar la funcin ShowWindow() para cambiar el estado de la ventana principal de Visual FoxPro entre maximizada, minimizada y normal, pero es ms correcto usar la propiedad WindowState del formulario _SCREEN. Debemos dejar el uso del API a funcionalidades no soportadas directamente por esta versin de Visual FoxPro. Conocer si nuestra aplicacin ya est arrancada Todos hemos visto aplicaciones que no pueden arrancarse dos veces. Cuando volvemos a arrancar la aplicacin, no se arranca una segunda copia del programa, sino que se vuelve a mostrar la aplicacin que ya estaba arrancada. Existen varias formas de hacer esto, una de ellas es la descrita aqu, no es la mejor, pero explica el uso de algunas funciones del API que son muy interesantes. La primera de ellas es la funcin GetWindow(), para hacer un recorrido por las distintas ventanas principales que estn visibles en Windows en ese momento. DECLARE ; Integer GetWindow ; IN WIN32API ; Integer nHwnd, ; Integer nCmd Como parmetros requiere un handle de ventana y en que direccin queremos recorrer las ventanas, para ello hemos definido las siguientes constantes : #define #define #define #define #define #define #define GW_HWNDFIRST GW_HWNDLAST GW_HWNDNEXT GW_HWNDPREV GW_OWNER GW_CHILD GW_MAX 0 1 2 3 4 5 5

Otras dos funciones que vamos a utilizar son GetWindowTextLength() para obtener el tamao del ttulo de una ventana y GetWindowText() para obtener el ttulo de la ventana. DECLARE ; Integer GetWindowTextA ; IN WIN32API ; Integer nhWnd, ; String @cString, ; Integer nMaxCount

DECLARE ; Integer GetWindowText ; IN WIN32API ; Integer nWnd Por medio de estas funciones podemos recorrer las ventanas principales de las aplicaciones activas y si alguna tiene el mismo ttulo que la nuestra mostrar esta y terminar con la ejecucin de la nuestra. Sera algo as : nFoxHwnd = FindWindow( 0, _SCREEN.Caption ) *** Esta ser la primera ventana nCurrWnd = GetWindow( nFoxHwnd, ; GW_HWNDFIRST ) *** Recorrer todas las ventanas DO WHILE nCurrWnd # 0 *** Tamao del ttulo nLength = GetWindowTextLength( nCurrWnd ) IF nLength > 0 *** Preparar un buffer para el ttulo cTmp = REPLICATE( CHR(0), nLength + 1 ) *** Obtener el ttulo =GetWindowText( nCurrWnd, ; @cTmp, ; nLength + 1 ) *** Si el handle es distinto *** y el ttulo igual IF nFoxHwnd != nCurrWnd ; AND ; _SCREEN.Caption == SUBSTR( cTmp, ; 1, ; nLength ) *** Activar la otra ventana *** y terminar la ejecucin =SetForegroundWindow( nCurrWnd ) QUIT ENDIF ENDIF *** Obtener la siguiente ventana nCurrWnd = GetWindow( nCurrWnd, ; GW_HWNDNEXT ) ENDDO Conclusin No hemos visto todas las posibilidades, pero ya nos podemos hacer una idea de como superar las posibles limitaciones de Visual FoxPro. Aunque parezcamos bastante repetitivos, debemos volver a recordar que aunque se puedan hacer determinadas cosas por medio del API no es muy correcto hacerlas

sin ms. Debemos, por lo tanto, ser respetuosos con las dems aplicaciones que puedan estar corriendo en Windows. Espero que esta aproximacin a la utilizacin del API de Windows desde Visual FoxPro haya sido de inters. Esto no se queda aqu y en el prximo artculo seguiremos profundizando en el manejo de ventanas. Si no puede esperar y desea avanzar en el conocimiento del API de Windows puede ver el fichero de ayuda sobre el API de Win32 de la versin profesional en CD-ROM de Visual FoxPro o bien la magnfica informacin y ejemplos contenidos en los CDROM del Microsoft Developer Network (MSDN) o los manuales de Visual C++ 4.x.

Uso del API de Windows : Introduccin a las Ventanas (V)


Despus de haber visto en los artculos anteriores los principios bsicos sobre las llamadas al API de Windows desde Visual FoxPro (enero) y las funciones para el uso de ficheros .INI (febrero y marzo), vamos a seguir con las funciones para el manejo de ventanas que iniciamos el mes pasado (abril). En este caso nos fijaremos en como manejar las ventanas de otras aplicaciones. Conocer la lista de aplicaciones arrancadas En ocasiones nuestra aplicacin deber actuar en conjuncin con otras aplicaciones dentro de Windows. Para este tipo de interacciones es interesante saber si la otra aplicacin ya est arrancada. Por ejemplo, si vamos a abrir un canal DDE o una conversacin por medio de OLE Automation podemos saber si la otra aplicacin ya est arrancada. Para ello utilizaremos la misma tcnica que describimos el mes pasado para descubrir si nuestra aplicacin ya estaba arrancada, pero en este caso crearemos una matriz con dos columnas, la primera con todos los ttulos de las ventanas de las aplicaciones y las segunda con los handles de las mismas (pues nos sern de utilidad ms adelante). Nosotros deberemos buscar dentro de esta matriz si la aplicacin que buscamos est arrancada. Para usar la funcin utilizaremos un cdigo similar a este : ? aTask( "aVentanas", .F. ) Con esta llamada conseguiremos una matriz pblica con los nombres de todas las ventanas principales. Si pasamos como segundo parmetro una valor verdadero (.T.) obtendremos tambin las ventanas ocultas de Windows. La funcin devuelve el nmero de ventanas encontradas. *** Constantes #define GW_HWNDFIRST #define GW_HWNDNEXT #define GW_OWNER #define GW_CHILD PROCEDURE ATask LPARAMETER cMatriz, lOcultos *** Declaraciones de la funciones del API

0 2 4 5

DECLARE ; Integer GetWindow ; IN WIN32API ; Integer nHwnd, ; Integer nCmd DECLARE ; Integer GetWindowText ; IN WIN32API ; Integer nHwnd, ; String @cString, ; Integer nMaxCount DECLARE ; Integer GetWindowTextLength ; IN WIN32API ; Integer nWnd DECLARE ; Integer IsWindowVisible ; IN WIN32API ; Integer nWnd DECLARE ; Integer GetDesktopWindow ; IN WIN32API *** Declaracin de variables PRIVATE nFoxHwnd, nCont, nCurrWnd PRIVATE nLength, cTmp RELEASE MEMORY &cMatriz PUBLIC (cMatriz) *** Obtencin del handle del DeskTop nHwnd = GetDesktopWindow() nInitHwnd = GetWindow( nHwnd, GW_CHILD ) *** Esta ser la primera ventana nCurrWnd = GetWindow( nInitHwnd, ; GW_HWNDFIRST ) *** Inicializar contador nCont = 0 *** Recorrer todas las ventanas DO WHILE nCurrWnd # 0 *** Comprobar si no tiene padre IF GetWindow(nCurrWnd, GW_OWNER) = 0 *** Si debemos las ventanas ocultas IF IsWindowVisible( nCurrWnd ) = 1 ; OR lOcultos *** Tamao del ttulo nLength=GetWindowTextLength(nCurrWnd) IF nLength > 0 nCont = nCont + 1 *** Obtener el ttulo cTmp=REPLICATE( CHR(0), nLength+1 ) =GetWindowText( nCurrWnd, ; @cTmp, ; nLength + 1 ) *** Insertar un nuevo elemento

DIMENSION &cMatriz.[nCont,2] &cMatriz.[nCont,1] = ; SUBSTR( cTmp, 1, nLength ) &cMatriz.[nCont,2] = ; nCurrWnd ENDIF ENDIF ENDIF *** Obtener la siguiente ventana nCurrWnd = GetWindow( nCurrWnd, ; GW_HWNDNEXT ) ENDDO && (nCurrWnd # 0) *** Retornar el nmero de procesos RETURN nCont Las funcin del API de Window que se utilizan este mdulo que no hemos explicado en artculos anteriores son GetDesktopWindow() y IsWindowVisible(). GetDesktopWindow() obtiene el handle del Escritorio de Windows, lo que nos permitir utilizar GetWindows() a partir de este handle. IsWindowVisible() nos permite comprobar si una determinada ventana es visible o est oculta. Si pasamos como segundo parmetro al mdulo aTask un valor verdadero (.T.) obtendremos tambin las ventanas ocultas. Probablemente nos sorprenderemos al ver las ventanas que andan ocultas por nuestro Windows. Diferencias de aTask con FindWindow Alguien observador podra preguntarse por que no usar directamente la funcin FindWindow() del API de Windows pasando el nombre de la ventana de principal de la aplicacin con la que queremos interactuar en vez del mtodo indirecto de utilizar aTask y luego buscar en la matriz resultante. Podemos usar la funcin FindWindow() sin problemas, pero deberemos pasar exactamente el ttulo de la ventana. Como muchas aplicaciones incluyen en el ttulo el nombre del documento abierto, no es tan sencillo saber exactamente cual es el ttulo de la ventana. Personalmente prefiero utilizar una funcin propia como aTask, pues me permite hacer bsquedas aproximadas dentro de la matriz, y no necesito saber el ttulo exacto de la ventana principal. Esta es una sencilla funcin para buscar una ventana con un ttulo aproximado : PROCEDURE FindHwnd LPARAMETER cTitle *** Llama a aTask.PRG FOR nCont = 1 TO aTask( "aFindHwnd" ) *** Busca dentro de la cadena IF UPPER( cTitle ) ; $ UPPER( aFindHwnd[nCont,1] ) RELEASE MEMORY aFindHwnd RETURN aFindHwnd[nCont,2] ENDIF NEXT

RELEASE MEMORY aFindHwnd RETURN 0 Cada uno deber estudiar en que casos prefiere utilizar un mtodo u otro. Las Ventanas no son Procesos Hemos llamado a la funcin anterior aTask, es decir, Array de Tareas, por similitud a la Lista de Tareas de Windows 3.x. Realmente lo que estamos haciendo es obtener los ttulos de las ventanas principales que se estn ejecutando en Windows. Esto no es exactamente lo mismo que las tareas. No debemos confundir las ventanas principales de las aplicaciones con las tareas (tasks) o los hilos (threads) de las aplicaciones. Muchas tareas de Win32 no disponen de ventanas asociadas. Las funciones del API de Windows que estamos estudiando se dedican al manejo de ventanas y por eso nos hemos preocupado de las tareas como tales, pero en otras ocasiones nos puede interesar utilizar el identificador del proceso o ID de alguno de los threads de una aplicacin. Este tipo de funciones estn fuera del mbito de estos artculos introductorios, pero son realmente interesantes. Como curiosidad decir que Visual FoxPro tienen dos thread, uno para el interface y otro para el interpretador de comandos y el gestor de base de datos. Nosotros podemos crear un nuevo thread en Visual FoxPro, llamando al API de Windows, pero este thread no es utilizado por nuestro programa y no sirve para nada, por ello es mejor no hacerlo. Si tiene inters en profundizar por este camino puede empezar por dos artculos publicados en la desaparecida Revista Microsoft de Programadores (RMP) : Cual es la diferencia entre ventanas y tareas en Windows 3.1? (Febrero de 1994) y Subiendo a 32 bits : Procesos, Theads y gestin de memoria en Chicago ( Diciembre de 1994). En el CD-Rom de Microsoft Developed Network dispone de la versin en ingls de estos artculos. Arrancar otro programa Todos hemos utilizado la orden RUN el smbolo ! de Visual FoxPro para arrancar otro programa. Personalmente prefiero usar RUN a !, pues este ltimo puede confundirse con el operador lgico !, que es igual a un .NOT.. De todas las maneras es realmente muy sencillo. En Windows tenemos la posibilidad de indicar una serie de operadores a esta orden a fin de controlar la forma de ejecutar la otra aplicacin, por ejemplo el Bloc de Notas : *** RUN *** RUN *** RUN *** RUN *** RUN *** Arranca y sigue la /N notepad Arranca activo y a /N1 notepad Arranca activo y a /N2 notepad Arranca activo y a /N3 notepad Arranca inactivo y /N4 notepad Arranca inactivo y ejecucin de FoxPro tamao normal minimizado maximizado a tamao normal a minimizado

RUN /N7 notepad En la mayora de los casos basta con utilizar RUN ! con alguno de estos modificadores, pero si queremos arrancar una aplicacin, por ejemplo, con su ventana principal oculta, debemos hacer uso de la funcin WinExec() del API de Windows. Esta funcin se declara como : DECLARE ; Integer WinExec ; IN WIN32API ; String cCmdLine, ; Integer nCmdShow Como primer parmetro recibe la cadena que indica el programa a ejecutar, como segundo parmetro el modo en el que se desea visualizar esta aplicacin. Los posibles modos de visualizacin son los mismos que describimos el mes pasado cuando hablamos de la funcin ShowWindows() : #define #define #define #define #define #define #define #define #define #define #define #define #define #define SW_HIDE SW_SHOWNORMAL SW_NORMAL SW_SHOWMINIMIZED SW_SHOWMAXIMIZED SW_MAXIMIZE SW_SHOWNOACTIVATE SW_SHOW SW_MINIMIZE SW_SHOWMINNOACTIVE SW_SHOWNA SW_RESTORE SW_SHOWDEFAULT SW_MAX 0 1 1 2 3 3 4 5 6 7 8 9 10 10

La funcin devuelve un nmero por encima de 32 si ha tenido xito, si existe algn problema para arrancar esta aplicacin, devuelve un cdigo de error por debajo de este nmero 32. Para arrancar el Bloc de Notas de forma oculta deberas ejecutar algo como esto : =WinExec( "Notepad", SW_HIDE ) Despus de esto no vemos la aplicacin arrancada, pero si ejecutamos la funcin aTask con el segundo parmetro a .T., tal y como describamos al principio de este artculo, veremos que si se ha arrancado la aplicacin y que podemos hacerla visible. Algunas aplicaciones no podremos hacerlas invisibles, pues entre su cdigo de arranque tienen una modificacin del estado de su ventana, por ejemplo a maximizado, y esto provoca que se hagan siempre visibles. Deberemos probar la aplicacin que queremos arrancar de forma oculta. Para hacer visible la aplicacin utilizaremos la funcin ShowWindow() del API de Windows, pasando el handle de la ventana y como segundo parmetro alguno de los distintos modos de visualizacin o simplemente SW_SHOW.

Si no hacemos visible la otra aplicacin o no la cerramos desde nuestro cdigo, la aplicacin que hemos arrancado quedar de forma permanente, consumiendo recursos, pues el usuario no tiene medio de cancelar esta ejecucin. Por ello debemos ser muy cuidadosos a la hora de aplicar esta tcnica. Primera aproximacin a los mensajes : Cerrar otra aplicacin Cualquier actuacin con otra aplicacin debe realizarse con sumo cuidado. Tal y como decamos en el artculo anterior : aunque se puedan hacer determinadas cosas por medio del API no es muy correcto hacerlas sin ms. Debemos, por lo tanto, ser respetuosos con las dems aplicaciones que puedan estar corriendo en Windows. En este caso, si arrancamos otra aplicacin de forma invisible nosotros somos responsable de cerrarla. Para cerrar otra aplicacin podemos utilizar una nueva tcnica, enviar un mensaje. Windows se basa, en gran medida, en el paso de mensajes desde Windows a las aplicacin, incluso entre las aplicaciones entre si. Todas ventanas de Windows tienen asociadas un programa que gestiona los mensajes de esa ventana. Los mensajes para un programador de Visual FoxPro se puede asemejar a los eventos, es decir, cuando, por ejemplo, se mueve el ratn, Windows enva un mensaje indicando que se ha realizado este movimiento y Visual FoxPro ejecuta el cdigo asociado a este evento. Los mensajes pueden ser de muchos tipos. Basta ver la documentacin del API de Windows y observar que el inmenso nmero de mensajes que se pueden enviar o recibir. Los mensajes los pueden producir el usuarios, Windows (por ejemplo el mensaje de un Timer) u otras aplicaciones. Por ejemplo, para cerrar una ventana, podemos enviar un mensaje WM_CLOSE. Si esta ventana es la ventana principal de una aplicacin, se cierra esta aplicacin. Para enviar este mensaje a otra aplicacin en Win32 debemos usar la funcin PostMessage(). Veamos un pequeo ejemplo : #DEFINE WM_CLOSE 16

DECLARE ; Integer PostMessage ; IN WIN32API ; Integer nWnd, ; Integer nMsg, ; Integer nParam, ; Integer nParam *** Arranca en NotePad WAIT WINDOW ; "Se va a arrancar el Bloc, " + ; "escriba algo en l y espere..." ; TIMEOUT 3 RUN /N NotePad *** Cierra preguntando si ha habido cambios WAIT WINDOW ; "El Block se va ha cerrar..." ;

TIMEOUT 5 *** La funcin FindHwnd es un PRG nHwndNote = FindHwnd( "Bloc de notas" ) =PostMessage( nHwndNote, WM_CLOSE, 0, 0 ) La funcin PostMessage() necesita el handle de la ventana a la que enviamos el mensaje, en cdigo de mensaje, y dos parmetros necesarios para pasar variantes a algunos mensajes. El mensaje WM_CLOSE provoca que la aplicacin se cierre, pero muchas aplicaciones, como el Bloc de Notas, preguntan si deseamos guardar el documento abierto antes de cerrar. Si necesitamos cancelar una aplicacin de forma rotunda, podemos utilizar otro mensaje, el WM_QUIT, que corresponde al nmero 18. No debemos usar sin ms este ltimo mtodo, pues en algunas aplicaciones puede producir la corrupcin de datos y debemos ser muy, muy, cuidadosos con esta posibilidad. No vamos a describir aqu los mensajes que podemos utilizar. Sirvan estos dos como botn de muestra. Si consulta la documentacin sobre el API de Windows podr descubrir todas las posibilidades del envo de mensajes. Los mensajes en VFP Normalmente utilizaremos los mensajes que reciben las ventanas de Visual FoxPro por medio de los eventos de las ventanas. Recordemos que, normalmente los mensajes de Windows corresponden a un evento de Visual FoxPro. As, por ejemplo, el mensaje WM_MOUSEMOVE corresponde con el evento MouseMove de Visual FoxPro. Esto no siempre es tan sencillo. No todos los mensajes de Windows corresponden con un evento de Visual FoxPro, sobre todo los mensajes menos habituales. No debemos preocuparnos por esta limitacin, pero si necesitamos utilizar alguno de estos mensajes no reflejados directamente en VFP o los mensajes enviados por otras aplicaciones, como por ejemplo los enviados por Windows Socket, deberemos usar alguna librera u OCX que nos permita manejarlos. Por ejemplo, el control OCX de comunicaciones que viene con Visual FoxPro (Microsoft Comm Control) enlaza los mensajes producidos en una comunicacin asncrona con una serie de eventos de este control y manejables por Visual FoxPro. Si recuerda lo que decamos en el artculo anterior, todo en Windows en una ventana, un Botn o un Check Box son ventana y por lo tanto si conseguimos en handle de un control, podemos enviar un mensaje a ese control. En Visual FoxPro esto no es totalmente cierto, pues gran parte de los controles de Visual FoxPro son controles especiales y slo son ventanas cuando tienen el foco. Si obtenemos el handle de la ventana de este control cuando tiene el foco, podemos enviar algunos mensajes a la misma. Conclusin Debemos repetir, una vez ms, que no todo lo que se puede hacer con el API de Windows esta bien hecho. Por lo tanto, sean respetuosos con las dems aplicaciones que puedan estar corriendo en Windows y utilicen lo aqu expuesto con cuidado.

No hemos hecho ms que esbozar brevemente algunas de las posibilidades del manejo de ventanas. Le invito a seguir estudiando por su cuenta las funciones y mensajes que nos ofrece para el manejo de ventanas. El prximo artculo cambiaremos te tema y lo dedicaremos a las funciones para el manejo del Registro de Win32. Si no puede esperar y desea avanzar en el conocimiento del API de Windows puede ver el fichero de ayuda sobre el API de Win32 de la versin profesional en CD-ROM de Visual FoxPro o bien la magnfica informacin y ejemplos contenidos en los CDROM del Microsoft Developer Network (MSDN) o los manuales de Visual C++ 4.x.

Uso del API de Windows : Manejo del Registro (y VI)


Ya llegamos al final de esta serie de artculos sobre el API de Windows. Para terminar hemos elegido casi el mismo tema con el que empezamos, la forma de guardar informacin sobre nuestra aplicacin u obtener informacin sobre otras aplicaciones. En los meses de febrero y marzo vimos como hacer esto por medio de ficheros .INI, este mes veremos como utilizar el Registro (registry) de Windows. Que es el Registro de Windows? Es una base de datos con una estructura jerrquica donde las aplicaciones pueden guardar informacin sobre su configuracin y el sistema operativo almacena informacin sobre si mismo. Antes, las aplicaciones y el sistema operativo utilizaba los archivos .INI, pero su proliferacin y su escasa estructuracin hicieron que Microsoft se plantease dar un sistema mucho ms prctico para guardar los datos de configuracin de las aplicaciones. El primer Registro se incluye en Windows 3.1, pero es en Windows NT y Windows 95 cuando se nos ofrece un autntico Registro de Configuraciones. El Registro de Windows 3.1 est muy limitado y es necesaria la utilizacin de los ficheros .INI. Por su parte Windows 95 y Windows NT siguen soportando los ficheros .INI, duplicando en muchos casos los datos del Registro en este tipo de ficheros, pero se basan en el Registro de Configuraciones. Vamos a realizar una aproximacin a las funciones que disponemos para acceder a los Registros de Configuracin de Windows. Estructura del Registro Tal y como hemos dicho, el Registro tiene una estructura jerrquica. Est jerarqua se basada en una serie de claves (keys) que pueden contener valores u otras claves. En Windows 3.1 las claves slo pueden contener un valor, pero en Windows 95 y Windows NT las claves contienen tantos valores como se quiera, identificados cada uno de ellos por una etiqueta. Si quiere ver el Registro de Windows 3.1 debe ejecutar REGEDIT /V. Es importante la inclusin del parmetro, pues nos permite ver el Registro con su estructurar jerrquica. En Windows 95 deber ejecutar REGEDIT y en Windows NT REGEDIT32.

Como vemos los Registros de Windows 3.1, Windows 95 y Windows NT varan en cuanto a su formato, y como veremos ahora, tambin varan en cuanto a su contenido. Claves principales En Windows 3.1 slo se dispone de una entrada principal al Registro, identificada por el nombre HKEY_CLASSES_ROOT. A partir de esta raz cuelgan el rbol con todas las entradas del Registro. Windows 3.1 guarda informacin sobre los ficheros registrados y sobre OLE, pero las aplicaciones pueden hacer uso de l para guardar su configuracin. En Windows 95 y Windows NT el Registro tiene una conjunto de entradas principales colgando de una raz, denominada ROOT. La lista de las entradas de Windows 95 y Windows NT se pueden ver en la figura 1. Win16 y Win32s HKEY_CLASSES_ROOT - ficheros registrados y OLE - ficheros registrados y OLE Win95 HKEY_CLASSES_ROOT - por compatibilidad con Win16 - por compatibilidad con Win16 HKEY_CURRENT_USER - configuracin del usuario actual - configuracin del usuario actual HKEY_LOCAL_MACHINE - configuracin de esta mquina - configuracin de esta mquina HKEY_USERS - configuracin por defecto de usuarios - configuracin por defecto de usuarios HKEY_CURRENT_CONFIG - configuracin actual de esta mquina - configuracin actual de esta mquina HKEY_DYN_DATA - estado dinmico de esta mquina - estado dinmico de esta mquina WinNT HKEY_CLASSES_ROOT - por compatibilidad con Win16 - por compatibilidad con Win16 HKEY_CURRENT_USER - configuracin del usuario actual - configuracin del usuario actual HKEY_LOCAL_MACHINE - configuracin de esta mquina - configuracin de esta mquina HKEY_USERS - configuracin por defecto de usuarios - configuracin por defecto de usuarios Fig. 1 - Entradas del Registro Como podemos ver, existen dos entradas en el Registro que slo estn disponibles en Windows 95: HKEY_CURRENT_CONFIG y HKEY_DYN_DATA. Estas entradas estn especialmente pensadas para obtener informacin sobre dispositivos Plug&Play y para gestionar la conexin y desconexin de dispositivos en porttiles. Debemos saber que algunas entradas del Registro estn `mapeadas', por ejemplo, HKEY_CLASSES_ROOT corresponde internamente a la entrada

HKEY_LOCAL_MACHINE\Software\Classes, pues esta entrada est soportada slo por compatibilidad con Windows 3.1. Si estamos trabajando en Windows 3.1 con un producto Win32s como es Visual FoxPro 3.0, debemos ser consciente de que aun cuando disponemos de parte de las funciones del API de Win32, el Registro es el de Windows 3.1 y por lo tanto las entradas que hagamos o consultemos deben ser compatibles con este sistema operativo. No vamos a describir aqu la interesante informacin que podemos encontrar en las distintas entradas del Registro, pues nos centraremos en las funciones de acceso al Registro. Para obtener informacin sobre la informacin a la que podemos acceder lo mejor es consultar los Resource Kit de Windows 95 y Windows NT. Tipos de informacin Todos los datos que pueden almacenarse en las distintas entradas de este Registro de Windows 3.1 son de tipo carcter y, como hemos dicho, slo puede haber uno por clave. En Windows 95 y Windows NT la informacin almacenada en el Registro pueden ser de los siguientes tipos : *** Tipo no definido #define REG_NONE 0 *** Cadena de caracteres #define REG_SZ 1 *** Binario #define REG_BINARY 3 *** Nmero de 32 bits #define REG_DWORD 4 *** Nmero de 32 bits *** con formato Little-Endian #define REG_DWORD_LITTLE_ENDIAN 4 *** Nmero de 32 bits *** con formato Big-Endian #define REG_DWORD_BIG_ENDIAN 5 **** Enlace simblico de Unicode #define REG_LINK 6 *** Matriz de cadenas #define REG_MULTI_SZ 7 *** Lista de recursos de dispositivo #define REG_RESOURCE_LIST 8 Los tipos ms utilizados son los de cadenas de caracteres y de nmero, pero debemos estar preparados para encontrarnos datos de cualquier tipo si consultamos informacin de entradas del Registro no creadas por nosotros mismos. Donde guardar nuestra configuracin

Debemos pensar en que tipo de informacin queremos guardar y en que plataforma nos encontramos. La informacin relacionada con la instalacin de nuestro programa, por ejemplo, ubicacin de directorios, si estamos en Windows 95 y Windows NT, deben almacenarse dentro de HKEY_LOCAL_MACHINE\Software. Si la configuracin que queremos guardar est relacionada con un determinado usuario, por ejemplo, la posicin donde dej una ventana o su ltima configuracin del men, y seguimos en Windows 95 o Windows NT, deberemos trabajar con HKEY_CURRENT_USER\Software para el usuario actual y HKEY_USERS\.Default\ Software para definir los valores por defecto de todos los usuarios que se puedan crear. Por convencin, dentro de la clave \Software cada empresa inscribe una clave con el nombre de su empresa, de la que cuelgan las claves de cada programa registrado y a partir de aqu la claves con la versin del mismo. Si nuestra empresa se llama XXXX, nuestra aplicacin ZZZZ y est en al versin 1.0 deberemos hacer unas entradas como estas: HKEY_CURRENT_USER\Software\Xxxx\Zzzz\1.0 HKEY_LOCAL_MACHINE\Software\Xxxx\Zzzz\1.0 HKEY_USERS\Software\Xxxx\Zzzz\1.0 Si nos encontramos en Windows 3.1, bien desde FoxPro 2.x o en Visual FoxPro 3.0 con Win32s, deberemos usar siempre HKEY_CLASSES_ROOT. Normalmente, despus de esta clave crearemos una entrada con el nombre del producto y una subclave con su version. Tambin es posible crear una estructura con una las entradas Software\Empresa\Producto \Version, pero es poco habitual usar una entrada tan larga en el Registro de Windows 3.1. Como obtener informacin Las funciones del API de Windows se utilizan siempre en una secuencia similar : 1.se realiza la apertura de la clave, 2.- se opera sobre ella o sus valores y 3.- se cierra. Para abrir una la clave podemos utilizar la siguiente funcin : DECLARE ; Integer RegOpenKeyEx ; IN WIN32API ; Integer nKey, ; String cSubKey, ; Integer nReserved, ; Integer nSamDesired, ; Integer @nResult Para usarla debemos pasar primero el nmero que identifica una clave superior, normalmente una de la claves bsicas que podemos identificar con las definiciones siguientes :

#define #define #define #define #define #define

HKEY_CLASSES_ROOT -2147483648 HKEY_CURRENT_USER -2147483647 HKEY_LOCAL_MACHINE -2147483646 HKEY_USERS -2147483645 HKEY_CURRENT_CONFIG -2147483653 HKEY_DYN_DATA -2147483654

Despus pasamos una cadena con el nombre de la clave que queremos abrir, un 0, una mscara de seguridad y por ltimo una variable por referencia donde se almacenar el nmero de la clave abierta. La mscara de seguridad se parece mucho a los permisos que tenemos cuando abrimos un fichero a bajo nivel. Los tipos de permisos de acceso los podemos ver en las siguiente definiciones : #define #define #define #define #define #define #define #define #define #define KEY_QUERY_VALUE 1 KEY_SET_VALUE 2 KEY_CREATE_SUB_KEY 4 KEY_ENUMERATE_SUB_KEYS 8 KEY_NOTIFY 16 KEY_CREATE_LINK 32 KEY_READ 1+8+16 KEY_WRITE 2+4 KEY_EXECUTE KEY_READ KEY_ALL_ACCESS 1+2+4+8+16+32

Una vez abierta la clave podemos obtener la informacin que nos interesa utilizando la siguiente funcin : DECLARE ; Integer RegQueryValueEx ; IN WIN32API ; Integer nKey, ; String cValueName, ; Integer nReserved, ; Integer @nType, ; String @cData, ; Integer @nSizeData Debemos pasar el nmero que obtenemos con RegOpenKey(), el nombre de la subclave que queremos leer, un 0, una variable por referencia donde se nos devolver el tipo del valor y, tambin por referencia un buffer, y el tamao de este buffer. Una vez que terminemos de operar con la clave debemos cerrarla por medio de la siguiente funcin: DECLARE ; Integer RegCloseKey ; IN WIN32API ; INTEGER nKey Todas las funciones devuelve un 0 si todo a funcionado correctamente u otro valor si existe algn problema.

Ahora ya podemos hacer un pequeo programa de ejemplo. En este programa obtendremos una de la informacin que graba Visual FoxPro sobre su configuracin para el usuario activo. #include "registro.h" DO registro.prg nKey = 0 =RegOpenKeyEx( HKEY_CURRENT_USER, ; "Software\Microsoft\VisualFoxPro\3.0\Options",; 0, ; KEY_READ, ; @nKey ) nType = 0 nSize = 255 cValor = REPLICATE( CHR(0), nSize ) =RegQueryValueEx( nKey, "_BROWSER", 0, @nType, @cValor, @nSize ) =RegCloseKey( nKey ) ? SUBSTR( cValor, 1, nSize-1 ) Como crear y escribir Para crear una entrada en el Registro se utiliza esta funcin : DECLARE ; Integer RegCreateKeyEx ; IN WIN32API ; Integer nKey, ; String cSubKey, ; Integer nReserved, ; String cClass, ; Integer nOptions, ; Integer nDesired, ; String @cSecurityAttributes, ; Integer @nResult, ; Integer @nDisposition Por medio de esta funcin crearemos y/o abriremos una entrada en el Registro. Si ya existe la entrada, esta se abre, si no existe, esta se crea. Para saber cual es la accin realizada podemos ver el valor devuelto en el ltimo parmetro pasado por referencia, que puede tener estos dos valores : #DEFINE REG_CREATED_NEW_KEY 1 #DEFINE REG_OPENED_EXISTING_KEY 2 Para introducir un valor dentro de una clave utilizamos la funcin : DECLARE ; Integer RegSetValueEx ; IN WIN32API ; Integer nKey, ; String cValueName, ; Integer nReserved, ; Integer nType, ;

String cData, ; Integer nSizeData Si el valor existe se reemplaza, pero si no existe se crea otra entrada con este valor. Si se pasa como segundo parmetro una cadena nula, el valor se incluye a nivel de clave. Veamos un pequeo ejemplo que nos mostrar la forma de usar estas dos funciones: #include "registro.h" DO registro.prg nKey = 0 nResult = 0 =RegCreateKeyEx( HKEY_LOCAL_MACHINE, ; "Software\MiEmpresa\MiProducto\2.0", ; 0, ; 0, ; REG_OPTION_NON_VOLATILE, ; KEY_ALL_ACCESS, ; 0,; @nKey, ; @nResult ) IF nResult == REG_OPENED_EXISTING_KEY WAIT WIND "Entrada abierta..." ELSE WAIT WIND "Entrada creada..." ENDIF = RegSetValueEx( nKey, "Config", ; 0, REG_SZ, "creada", 5 ) = RegCloseKey( nKey ) Ejemplo : Guardar la posicin de una ventana Por medio de estas pocas funciones podemos hacer muchas cosas. Para ejemplificarlo vamos a crear una ventana que por medio de los eventos Load y UnLoad obtiene y guarda la posicin de la ventana y de esa forma el usuario siempre la vea donde la dej. Este ejemplo es muy parecido al que dimos en el mes de marzo, pero en ese caso trabajbamos sobre ficheros .INI y ahora sobre el Registro de Configuraciones. #include "registro.h" #define EMPRESA "MiEmpresa" #define PRODUCTO "Ejemplos" #define VERSION "1.0" *** Cargar la Form y mostrarla PUBLIC oEjemplo3 oEjemplo3 = CREATEOBJECT( "Ejemplo3" ) oEjemplo3.Show() RETURN

*** Form del ejemplo en forma de clase DEFINE CLASS Ejemplo3 AS FORM BackColor = RGB(192,192,192) Caption = "Ejemplo sobre Registry" Name = "Ejemplo3" *** Botn para cerrar *** ADD OBJECT cmdAceptar ; AS COMMANDBUTTON WITH ; Top = 6, ; Left = 6, ; Height = 29, ; Width = 94, ; Caption = "Aceptar" *** Evento al crear la form PROCEDURE Load *** Cargar las declaraciones DO Registro *** Obtener los valores con *** los que se cerr la ltima vez nKey = 0 WITH This IF RegOpenKeyEx( HKEY_CURRENT_USER, ; "Software\"+; EMPRESA+"\"+; PRODUCTO+"\"+; VERSION+"\"+; .name, ; 0, ; KEY_ALL_ACCESS, ; @nKey ) == NO_ERROR nType = 0 cValor = REPLICATE( CHR(0), 4 ) =RegQueryValueEx( nKey, "Top", 0, ; @nType, @cValor, 4 ) .Top = CTOWORD(cValor) nValor = 0 =RegQueryValueEx( nKey, "Left", 0, ; @nType, @cValor, 4 ) .Left = CTOWORD(cValor) nValor = 0 =RegQueryValueEx( nKey, "Width",0, ; @nType, @cValor, 4 ) .Width = CTOWORD(cValor) nValor = 0 =RegQueryValueEx( nKey, "Height",0,; @nType, @cValor, 4 ) .Height = CTOWORD(cValor) ENDIF ENDWITH ENDPROC *** Evento al destruir la form PROCEDURE Unload

*** Cargar las declaraciones *** pueden haberse descargado DO registro *** Si est minimizado o maximizado *** restaurar al tamao normal This.WindowState = 0 *** Abrir o crear la clave si no existe nKey = 0 nResult = 0 WITH This IF RegCreateKeyEx(HKEY_CURRENT_USER,; "Software\"+; EMPRESA+"\"+; PRODUCTO+"\"+; VERSION+"\"+; .name, ; 0, ; 0, ; REG_OPTION_NON_VOLATILE, ; KEY_ALL_ACCESS, ; 0,; @nKey, ; @nResult ) == NO_ERROR *** Guardar los valores de *** posicin y tamao =RegSetValueEx( nKey, "Top", 0, ; REG_DWORD, WORDTOC(.Top), 4 ) =RegSetValueEx( nKey, "Left", 0, ; REG_DWORD, WORDTOC(.Left), 4 ) =RegSetValueEx( nKey, "Width", 0,; REG_DWORD, WORDTOC(.Width), 4 ) =RegSetValueEx( nKey, "Height",0,; REG_DWORD, WORDTOC(.Height), 4) ENDIF ENDWITH ENDPROC *** Mtodo para cerrar la form PROCEDURE cmdAceptar.Click ThisForm.Release() ENDPROC ENDDEFINE *** Combierte un nmero 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) *** Combierte un buffer en un nmero 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 Una de las caractersticas a resaltar de este ejemplo, es la creacin de dos funciones, WORDTOC y CTOWORD para poder pasar un nmero en un buffer del tipo cadena de caracteres. Visual FoxPro no puede definir un tipo void o any y los buffers deben ser de tipo String. Diferencias entre los Sistemas Operativos Los programas anteriores slo son vlidos en Windows 95 y Windows NT, si quiere ejecutarlos en Windows 3.1 desde FoxPro 2.x o Visual FoxPro con Win32s se encontrar que no funcionan, por varios motivos. Por una parte hemos trabajado con las configuraciones del Registro propias de Windows NT y Windows 95, recuerde lo que decamos en los apartados sobre las claves principales y sobre donde guardar nuestra configuracin acerca las diferencias de claves entre las distintas versiones de Windows. Si trabajamos sobre Windows 3.1 debemos usar siempre HKEY_CLASSES_ROOT. Por otra parte, no todas las funciones estn disponibles en todas las plataformas. Si observa la figura 2 puede ver que funciones estn disponibles en que versiones de Windows. Funcin RegCloseKey RegConnectRegistry RegCreateKey RegCreateKeyEx RegDeleteKey RegDeleteValue RegEnumKey RegEnumKeyEx RegEnumValue RegFlushKey RegGetKeySecurity RegLoadKey RegNotifyChangeKeyValue RegOpenKey RegOpenKeyEx RegQueryInfoKey RegQueryMultipleValues RegQueryValue RegQueryValueEx RegReplaceKey Win16 Win32s Win95 WinNT X X X X X X

X X

Usar RegCreateKeyEx X X X X X X

X X

Usar RegEnumKeyEx
X X X X X X X X X

X X X

Usar RegOpenKeyEx X X

X X

Usar RegQueryValueEx X X

RegRestoreKey RegSaveKey RegSetKeySecurity RegSetValue RegSetValueEx RegUnLoadKey

X X

X X X

X X

Usar RegSetValueEx X X

Fig. 2 - Funciones del Registro soportadas por Windows En Windows 3.1 utilizaramos la funcin RegCreateKey en vez de RegCreateKeyEx, RegOpenKey en vez de RegOpenKeyEx, RegQueryValue en vez de RegQueryValueEx y RegSetValue en vez de RegSetValueEx. Otra limitacin, que ya comentamos, es que en Windows 3.1 cada clave slo puede tener un valor, por lo que en el ejemplo anterior deberamos hacer que todas las entradas fueran claves. Por ltimo debemos recordar que los datos que puede manejar Windows 3.1 se reducen al tipo carcter. Para saber en que sistema operativo estamos podemos usar la funcin OS(1) de FoxPro y Visual FoxPro y a partir de esta informacin bifurcar nuestro cdigo. Ms posibilidades Como puede ver en la figura 2, las posibilidades del API de Windows sobre el Registro de configuraciones no se limitan a las aqu presentadas. Pero en la mayora de las veces con estas pocas funciones uno puede trabajar sin problemas. Si quiere todas las declaraciones de las funciones para el manejo del Registro de Configuraciones puede verlas en el disco que acompaa a la revista. Sobre su uso puede consultar el fichero de ayuda sobre el API de Win32 de la versin profesional en CD-ROM de Visual FoxPro Encapsular las funciones del API Tal y como hemos venido proponiendo, es muy conveniente cubrir las funciones del API de Windows con funciones propias que aslen nuestro cdigo de llamadas directas. Es muy sencillo construir una funcin para leer del Registro de Configuraciones y otra para escribir en l. Tambin puede hacer una funcin que lea de ficheros .INI o del Registro dependiendo del sistema operativo o utilice unas funciones u otras dependiendo tambin del sistema operativo en el que nos encontremos. Cada uno podr estudiar que tipo de encapsulacin le es ms conveniente para su proyecto. Conclusin

Antes de terminar quisiera aconsejarles prudencia. Debemos tener cuidado con las escrituras en claves del Registro que no sean de nuestra aplicacin. Aun cuando podemos manejar informacin de otras aplicaciones y el propio sistema operativo modificando entradas en el Registro, debemos ser muy cuidadosos a la hora de utilizar este mtodo. Las lectura de las entradas del Registro es casi siempre inofensiva, pero debemos estar seguros de que sabemos interpretar su contenido en todos los sistemas operativos a fin de no provocar confusin. Como hemos dicho, no hemos descrito todas las posibilidades del Registro, sobre todo no hemos descrito las posibilidades de obtener informacin sobre el Sistema Operativo, Usuarios, etc. por este medio. Le invito a seguir estudiando por su cuenta las funciones y entradas que se nos ofrecen en el Registro. Si quiere iniciarse en los secretos del Registro de Windows puede leer el captulo 33 del Resouce Kit de Windows 95 y el captulo 10 del Resouce Kit de Windows NT. Son realmente aclaratorios. Si desea avanzar en el conocimiento del API de Windows puede ver el fichero de ayuda sobre el API de Win32 de la versin profesional en CD-ROM de Visual FoxPro o bien la magnfica informacin y ejemplos contenidos en los CD-ROM del Microsoft Developer Network (MSDN) o los manuales de Visual C++ 4.x. Espero que estos ltimos seis artculos sobre el API de Windows hayan sido de su inters.

You might also like