Uso del API de Windows: Introducción (I)

27/03/2020

 

Introducción

Con este artículo empezamos una serie que nos acercará a las posibilidades que nos ofrece Visual FoxPro (VFP) para llamar a funciones contenidas en librerías de enlace dinámico (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 ilustrarán 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 programación es finito, es decir, esta limitado. Pero en muchos casos es posible ampliar la funcionalidad de un sistema por medio de librerías 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 librerías FLLs existen desde la versión 2.5 para Windows y son librerías escritas en C especialmente para FoxPro. Estas librerías, una vez cargadas, ofrecen un conjunto de nuevas funciones para ser usadas desde nuestros programas.

Las FLLs están 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 código de la librería.

Sin duda la librería más conocida de este tipo es FOXTOOLS.FLL, que es distribuida con FoxPro y que nos ofrece un buen número de funciones.

Por otra parte, Windows dispone de un estándar para las librerías de enlace dinámico, 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 librerías comerciales en formato de DLL. Windows, por su parte, ofrece una buena colección librerías DLLs, de hecho, todo el API de Windows o complementos como el ODBC, MAPI, etc., están contenidos en este tipo de librerías.

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 versión de los famosos VBX y en breve se van a popularizar muchísimo.

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 librerías, pero con orientación a objeto. Ejemplo de este tipo de controles son los MSCOMM32.OCX y MSMAPI32.OCX de VFP.

En el futuro el API de Windows serán métodos 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 código de una DLL y es necesario cargar la librería FOXTOOLS.FLL para registrar y llamar a las funciones de las librerías de enlace dinámico.

La librería 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 sólo dos: RegFn() y CallFn().

  • Con RegFn registramos una función contenida en una DLL. Debemos pasar el nombre de la función (con las mayúsculas y minúsculas correctamente situadas), los tipos de los datos que la función recibe como parámetros, el tipo de dato que se va a devolver y por último el nombre de la DLL, según este esquema:
  NumFuncion = RegFn( NombreFuncion,  ;
                      TiposArgumentos,;
                      TipoRetorno,    ;
                      NombreDLL )
  • Con la función CallFn podemos llamar a las funciones registradas con RegFn. En este caso debemos pasar el número de función que nos retornó RegFn y los parámetros que hemos registrado. Se nos retornará el valor que devuelva la función de la DLL, que también debemos haber registrado.
  Retorno = CallFN( NumFuncion, ;
                    Arg1,       ;
                    Arg2, .... )

 

FoxTools en Visual FoxPro 3.0: 16 y 32 bits

En la versión 3.0 también podemos hacer uso de la librería FoxTools, pero debemos tener en cuenta las diferencias entre las librerías construidas en 32 bits y en 16 bits.

Para llamar a librerías de 16 bits desde aplicaciones de 32 bits como Visual FoxPro es necesario utilizar un mecanismo llamado Universal Thunk y este mecanismo sólo 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 excepción de las llamadas al API de Windows 3.1, pues en este caso la conversión entre 16 y 32 bits la realiza Win32s).

También podemos usar FoxTools para registrar librerías de 32 bits por medio de la función RegFn32(). La función 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 librerías 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 más corto para llamar a funciones de las DLLs de 32 bits desde Visual FoxPro, existe otro mucho mejor...

 

Declaración de funciones en Visual FoxPro

En Visual FoxPro podemos llamar a funciones de las DLLs de 32 bits sin necesidad de la librería FoxTools por medio de la orden DECLARE. Con ella podemos registrar las funciones de las DLLs sin necesidad de ninguna librería externa.

  DECLARE [cTipoRetorno] NombreFuncion
    IN NombreLibreria [AS NombreAlias]
    [cTipoParam1 [@] NombreParam1,
    cTipoParam2 [@] NombreParam2, ...]

Debemos indicar el nombre de la función distinguiendo entre mayúsculas y minúsculas. Si este nombre no es válido en VFP o no nos gusta podemos darle un ALIAS, es decir, el nombre con el que vamos a llamar a la función registrada dentro de VFP.

También debemos indicar el tipo del valor de retorno y los tipos de los parámetros. 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 algún parámetro debe ser pasado por referencia, indicaremos este hecho con un @ después del tipo del parámetro. También podemos dar un nombre a los parámetros, pero Visual FoxPro no lo requiere y no tiene más utilidad que darnos alguna ayuda para recordar el contenido de los parámetros.

Con la cláusula IN damos el nombre de la DLL que deseamos utilizar. Si en vez del nombre de la librería indicamos WIN32API la función declarada es buscada en las librerías 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 función, entonces la librería busca la función automáticamente en las librerías 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 librería o utilizamos la orden DECLARE con el modificador IN WIN32API y no el nombre de una DLL, entonces se busca la función en las librerías 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 librería Win32s, e indicamos WIN32API, entonces se buscará la función en el fichero W32SCOMB.DLL, la librería de conversión del API de Win32 a Win16.

En todos los casos, una vez registrada la función ya podemos hacer uso sin más 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 difícil pasar el código a FoxPro 2.x usando la librería 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 número que indica los atributos que tiene un fichero del que se ha pasado su nombre como parámetro. Para saber realmente que atributos tiene podemos hacer uso de estas simples definiciones:

   #define _A_RDONLY    1
 	#define _A_HIDDEN    2
 	#define _A_SYSTEM    4
 	#define _A_SUBDIR   16
 	#define _A_ARCH     32
 	#define _A_NORMAL  128 

A partir de aquí podemos utilizar una nueva función 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 sólo lectura..."
 	ENDIF
 	IF BITAND( nAttrib, _A_HIDDEN ) != 0
 	  WAIT WIND "El de sólo lectura..."
 	ENDIF
 	...

Si por algún motivo la función 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 función.

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 número 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 función no tiene éxito devuelve un 0. Si todo ha sido correcto el retorno es de un 1.

 

Función de VFP o función del API de Windows

En muchas ocasiones nos encontraremos que ya existe una función de VFP para hacer lo que queremos hacer por medio de una función del API de Windows. En este caso podemos obtener los atributos de un fichero por medio de la función ADIR() de VFP y no necesitamos llamar a GetFileAttributes() del API de Windows. En VFP no existe una función para dar atributos a un fichero y es necesario utilizar el API, una librería 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 función en VFP entonces llamar a las funciones del API, si no existe tampoco en el API de Windows entonces crear una librería y si no hay más remedio llamar a una orden del MS-DOS. Pero cada uno deberá decidir que quiere hacer.

Vamos a ver un pequeño ejemplo del uso de estos API de Windows y vamos a ver como hacer uso de la función 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 función 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 función 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 librerías que usemos, por medio de funciones propias, que permiten independizar nuestro código 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 función llamada SetAttri a la que se le pasa como primer parámetro el nombre del fichero o una máscara 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

 

Conclusión

Espero que esta primera aproximación a la utilización del API de Windows desde Visual FoxPro haya sido de interés. De momento sólo hemos visto como registrar y llamar a estas funciones y hemos descrito dos funciones muy simples, pero en el próximo artículo estudiaremos otras funciones muy útiles.

Si no puede esperara hasta recibir el próximo número de la revista puede ver el fichero de ayuda sobre el API de Win32 de la versión profesional en CD-ROM de Visual FoxPro o bien la magnífica información y ejemplos contenidos en los CD-ROM del Microsoft Developer Network (MSDN) o los manuales de Visual C++ 2.0.




error: Contenido protegido