Uso del API de Windows: Introducción a las Ventanas (IV)
27/03/2020
Índice
Después de haber visto en los artículos anteriores los principios básicos sobre las llamadas al API de Windows desde Visual FoxPro y las funciones para el uso de ficheros .INI, vamos a realizar una pequeña introducción a las funciones para el manejo de ventanas.
No nos fijaremos tanto en manejar las ventanas creadas por nosotros en nuestra aplicación, 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.
Toda ventana en MS-Windows tiene un indicador único denominado handle o manejador. Todo en MS-Windows es una ventana, un botón, un campo de edición, o cualquier otro elemento visual de Windows es una ventana y toda ventana tiene un handle.
Muchas funciones que vamos a utilizar requieren como parámetro el handle (manejador) de la ventana de Visual FoxPro o de alguna otra ventana a la que se asociará el comportamiento de esa función. 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 métodos muy sencillos para obtener este handle.
En primer lugar podemos usar la librería FOXTOOLS.FLL para usar la función MainHwnd(). Esta función devuelve el handle de la ventana principal de FoxPro. Sería 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 función FindWindow().
DECLARE ; INTEGER FindWindow ; IN WIN32API ; STRING cClassName, ; STRING cWindNamePara la funcionalidad que deseamos debemos pasar en el primer parámetro un valor 0 (correspondiente a un puntero nulo) y como segundo parámetro el título de la ventana principal de Visual FoxPro. Sería algo así:
nHwnd = FindWindow( 0, _SCREEN.Caption )Esta función, 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 aplicación es la ventana activa, para ello podemos comprobar si la ventana principal de Visual FoxPro es la ventana activa.
Utilizaremos la función 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 pequeño programa de ejemplo que devuelve .T. si la ventana de nuestra aplicación es la ventana activa y .F. si no lo es:
DECLARE ; Integer GetForegroundWindow ; IN WIN32API *** Para darnos tiempo a cambiar WAIT WINDOW ; "Cambie de aplicación 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 == nActiveHwndEs una buena idea construir una función similar a esta para comprobar en cualquier momento si nuestra aplicación corresponde a la ventana activa.
En Windows no todo lo que se puede hacer está bien visto que se haga. Por ejemplo, si una aplicación no esta activa porque estamos trabajando en ese momento en otra aplicación y nos parece la aplicación inactiva para darnos un pequeño aviso sin importancia entonces nos molestamos.
No debemos activar nuestra aplicación continuamente para cualquier aviso que tengamos que dar, pero en algunas ocasiones podemos necesitar activar nuestra aplicación y para ello podemos utilizar SetForegroundWindow(), una función del API de Windows para este fin. Esta función la declaramos como:
DECLARE ; Integer SetForegroundWindow ; IN WIN32API ; Integer nHwndEl programa sería algo así:
WAIT WIND ; "Cambie de aplicación y " + ; "espere unos segundos..." TIMEOUT 5 nFoxHwnd = FindWindow( 0, _SCREEN.Caption ) *** Activarla =SetForegroundWindow( nFoxHwnd )Debemos recordar una vez más que no debemos abusar de esta posibilidad.
Situar nuestra aplicación siempre encima
Habéis visto lo útil que es esa especie de botón con una chincheta en el diseñador 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 versión) es un formulario, pero un formulario bastante especial. En las versiones previas al lanzamiento del producto esta propiedad funcionaba, pero por una medida de diseño se eliminaron todas las propiedades y métodos de _SCREEN que consideraron podían ser 'peligrosas'.
Si de todas formas queremos hacer que nuestra aplicación este 'On Top', es decir, por encima de todas las demás 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 sólo cuando sea realmente importante.
Para realizar esto necesitamos utilizar la función SetWindowPos() del API de Windows. Con esta función se pueden hacer muchísimas cosas con las ventanas, no vamos a explicar todas, pero si tiene curiosidad y puede hojear la documentación sobre el API de Windows comprobará muchos otros usos de esta función.
De momento sólo la usaremos para situar nuestra ventana fija por encima de todas las demás. La función se declara de la siguiente manera:
DECLARE ; Integer SetWindowPos ; IN WIN32API ; Integer nWnd, ; Integer nWndInsertAfter, ; Integer nTop, ; Integer nLeft, ; Integer nHeight, ; Integer nWidth, ; Integer nFlagsAdemás debemos declarar algunas constantes y ya podemos hacer uso de la función:
#define SWP_NOSIZE 1 #define SWP_NOMOVE 2 #define HWND_TOPMOST -1 #define HWND_NOTOPMOST -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. ENDIFLa función SetWindowPos() requiere como primer parámetro el handle de la ventana que queremos modificar, en el segundo la constante para hacer esa ventana siempre visible por encima de las demás, los cuatro siguientes serían la posición y tamaño de la ventana pero en el último parámetro hemos pasado las constantes para hacer que no se tenga en cuenta la posición y tamaño de la ventana y que sólo 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 ningún efecto real sobre la ventana principal de Visual FoxPro.
Para deshacer esta situación por encima de todas las ventanas debemos llamar a la función con el parámetro HWND_NOTOPMOST de la siguiente manera:
SetWindowPos( nFoxHWND, ; HWND_NOTOPMOST, ; 0,0,0,0, ; SWP_NOSIZE + SWP_NOMOVE )Deberíamos también cambiar la propiedad AlwaysOnTop del formulario _SCREEN para mantener la coherencia entre la situación 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 deberíamos hacer ese formulario oculto, pero con _SCREEN esto no funciona. De igual forma el método Hide() no funciona con _SCREEN. Fueron precisamente esta propiedad y este método los que provocaron en versiones previas al lanzamiento del producto que se replantease un buen número 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 código que nos asegure que Visual FoxPro se volverá a hacer visible o bien termine. Si no lo hacemos así tendremos una aplicación activa, consumiendo recursos, y a la que no podemos acceder.
La función que utilizaremos será:
DECLARE ; Integer ShowWindow ; IN WIN32API ; Integer nWnd, ; Integer nCmdShowCon esta función podemos cambiar el estado de una ventana dentro de una amplia gama de posibilidades. Como primer parámetro le tenemos que pasar el handle de la ventana y como segundo parámetro la acción que queremos realizar. Para facilitar el trabajo estas son las acciones soportadas por la función definidas en constantes:
#define SW_HIDE 0 #define SW_SHOWNORMAL 1 #define SW_NORMAL 1 #define SW_SHOWMINIMIZED 2 #define SW_SHOWMAXIMIZED 3 #define SW_MAXIMIZE 3 #define SW_SHOWNOACTIVATE 4 #define SW_SHOW 5 #define SW_MINIMIZE 6 #define SW_SHOWMINNOACTIVE 7 #define SW_SHOWNA 8 #define SW_RESTORE 9 #define SW_SHOWDEFAULT 10 #define SW_MAX 10Para 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 ) ENDCASEOcultar la ventana principal de Visual FoxPro provoca que todas las ventanas hijas, es decir las que están dentro de la ventana de Visual FoxPro, se hagan invisibles. Las ventanas definidas como Desktop seguirán siendo visibles, como un MessageBox().
Utilizar funciones de VFP para manejar la ventana principal
No debemos caer en la tentación 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 función SetWindowPos() podemos cambiar la posición de nuestra ventana, pero es mucho más sencillo y más correcto hacerlo por medio de las propiedades Top, Left, Height y Width de la ventana principal de Visual FoxPro.
También podemos usar la función ShowWindow() para cambiar el estado de la ventana principal de Visual FoxPro entre maximizada, minimizada y normal, pero es más correcto usar la propiedad WindowState del formulario _SCREEN.
Debemos dejar el uso del API a funcionalidades no soportadas directamente por esta versión de Visual FoxPro. Conocer si nuestra aplicación ya está arrancada Todos hemos visto aplicaciones que no pueden arrancarse dos veces. Cuando volvemos a arrancar la aplicación, no se arranca una segunda copia del programa, sino que se vuelve a mostrar la aplicación 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 función GetWindow(), para hacer un recorrido por las distintas ventanas principales que estén visibles en Windows en ese momento.
DECLARE ; Integer GetWindow ; IN WIN32API ; Integer nHwnd, ; Integer nCmdComo parámetros requiere un handle de ventana y en que dirección queremos recorrer las ventanas, para ello hemos definido las siguientes constantes:
#define GW_HWNDFIRST 0 #define GW_HWNDLAST 1 #define GW_HWNDNEXT 2 #define GW_HWNDPREV 3 #define GW_OWNER 4 #define GW_CHILD 5 #define GW_MAX 5Otras dos funciones que vamos a utilizar son GetWindowTextLength() para obtener el tamaño del título de una ventana y GetWindowText() para obtener el título de la ventana.
DECLARE ; Integer GetWindowTextA ; IN WIN32API ; Integer nhWnd, ; String @cString, ; Integer nMaxCount DECLARE ; Integer GetWindowText ; IN WIN32API ; Integer nWndPor medio de estas funciones podemos recorrer las ventanas principales de las aplicaciones activas y si alguna tiene el mismo título que la nuestra mostrar esta y terminar con la ejecución de la nuestra. Sería 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 *** Tamaño del título nLength = GetWindowTextLength( nCurrWnd ) IF nLength > 0 *** Preparar un buffer para el título cTmp = REPLICATE( CHR(0), nLength + 1 ) *** Obtener el título =GetWindowText( nCurrWnd, ; @cTmp, ; nLength + 1 ) *** Si el handle es distinto *** y el título igual IF nFoxHwnd != nCurrWnd ; AND ; _SCREEN.Caption == SUBSTR( cTmp, ; 1, ; nLength ) *** Activar la otra ventana *** y terminar la ejecución =SetForegroundWindow( nCurrWnd ) QUIT ENDIF ENDIF *** Obtener la siguiente ventana nCurrWnd = GetWindow( nCurrWnd, ; GW_HWNDNEXT ) ENDDO
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 más. Debemos, por lo tanto, ser respetuosos con las demás aplicaciones que puedan estar corriendo en Windows.
Espero que esta aproximación a la utilización del API de Windows desde Visual FoxPro haya sido de interés. Esto no se queda aquí y en el próximo artículo 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 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++ 4.x.
- Introducción a Foxtools (Foxtools.fll)
- Introducción (I)
- Funciones para ficheros .INI (II)
- Funciones para ficheros .INI (III)
- Introducción a las Ventanas (IV) --> Usted está aquí.
- Introducción a las Ventanas (V)
- Manejo del Registro (y VI)