<-- Capítulo IV

Regresar a la página principal

Capítulo VI -->

Capítulo V

Ventanas (Windows)

En la introducción y en los capítulos ya explicados hemos hablado sobre ventana, pero en este capitulo trabajaremos un poco mas profundo con este concepto. ¿Se recuerdan del código hecho en C?, ese código lo vamos a repetir pero en VB, y explicaremos con un poco mas de detalles su funcionamiento.

Ya sabemos que la mayoría de las cosas que vemos son ventanas, pero hablemos ahora de esas ventanas o formularios que nos sirven para mostrar información. Cuando ustedes arrancan una aplicación esta tiene que correr en una ventana principal (main window), esta ventana principal tiene básicamente la siguiente forma:

A continuación se presentaran temas relacionados con la manipulación de BITMAP (Mapa de bits), las cuales pueden ser de mucha ayuda al desarrollar una aplicación que las contengan.

Cabe mencionar que las medidas que se usan en estas funciones son en unidades logicas, es decir, parámetros como X, Y, Width y Height, vienen especificado en unidades logicas.

Si le dice ”área cliente”, al área donde nosotros podemos dibujar, donde ponemos los controles, etc, en pocas palabras, es nuestro hdc, el área no cliente es aquella en donde Windows es el que se encarga de crearla, como pueden ver nosotros en ningún momento de la creación de la misma especificamos colores para la barra de titulo, en ningún momento pusimos botones de cerrar, maximizar o minimizar, ni le indicamos que pusiera un menú de sistema, por cierto, menú del sistema es el típico menú que se usa, para mover, maximizar, minimizar, etc. Toda aplicación de Windows básicamente tiene lo que mencione, el uso de los Scroll normalmente es opcional y dependerá de la aplicación que estemos desarrollando.

La figura mostrada sería una ventana “básica”, no tiene por que ser así obligatoriamente, las ventanas están compuestas o se crean dependiendo de los “estilos” que usemos, cuando creamos una ventana nosotros configuramos ciertos estilos que pueden cambiar la apariencia de nuestra ventana, estos estilos se presentan a continuación:

Public Const WS_OVERLAPPED = &H0& : Con este estilo se crea una ventana con bordes y barra de títulos o caption, se llama caption a la banda coloreada con el titulo de la ventana.

Public Const WS_POPUP = &H80000000 : Con este estilo, se crea una ventana sin bordes, sin caption, nada prácticamente, lo único que se muestra es el área del cliente.

Public Const WS_CHILD = &H40000000 : Sirve para crear ventanas que son hijas.

Public Const WS_MINIMIZE = &H20000000 : Crea una ventana que inicialmente se encuentra minimizada, este estilo solo se usa con WS_OVERLAPPED.

Public Const WS_VISIBLE = &H10000000 : Crea una ventana que es visible.

Public Const WS_DISABLED = &H8000000 : Crea una ventana que esta desactivada.

Public Const WS_MAXIMIZE = &H1000000 : Crea una ventana con tamaño maximizado.

Public Const WS_CAPTION = &HC00000 : Crea una ventana con la barra de titulo, y activa el borde de la misma

Public Const WS_BORDER = &H800000 : Crea una ventana con borde

Public Const WS_DLGFRAME = &H400000 : Crea una ventana con doble borde pero sin titulo

Public Const WS_VSCROLL = &H200000 : Crea una ventana con una barra de desplazamiento vertical.

Public Const WS_HSCROLL = &H100000 : Crea una ventana con una barra de desplazamiento horizontal.

Public Const WS_SYSMENU = &H80000 : Crea una ventana con el menú del sistema.

Public Const WS_THICKFRAME = &H40000 : Crea una ventana con bordes, los cuales pueden servir para redimensionar la ventana.

Public Const WS_GROUP = &H20000 : Estilo usado cuando trabajamos con grupos.

Public Const WS_TABSTOP = &H10000 : Crea una ventana que puede tomar el foco cuando se presiona la tecla TAB

Public Const WS_MINIMIZEBOX = &H20000 : Crea una ventana con el botón de minimizar

Public Const WS_MAXIMIZEBOX = &H10000 : Crea una ventana con el botón de maximizar

Public Const WS_TILED = WS_OVERLAPPED

Public Const WS_ICONIC = WS_MINIMIZE

Public Const WS_SIZEBOX = WS_THICKFRAME

Public Const WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED Or WS_CAPTION Or WS_SYSMENU Or WS_THICKFRAME Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX)

Public Const WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW

Public Const WS_POPUPWINDOW = (WS_POPUP Or WS_BORDER Or WS_SYSMENU)

Public Const WS_CHILDWINDOW = (WS_CHILD)

Como verán los últimos estilos son iguales o combinaciones de otros estilos. Con estos estilos se pueden crear la ventana que ustedes quieran.

INSTANCIA (Instance handle)

Cuando una aplicación arranca, a este se le asigna una “instancia”, que es un número que usa el sistema operativo para saber que funciones o que bloque de información, son de esa aplicación. ¿Qué quiero decir?. Imaginen que tenemos un programa con 4 funciones, al arrancar el programa estas funciones son colocadas en memoria, pero se puede arrancar el mismo programa de nuevo, es decir, se inicia otra instancia, este otro programa tiene las mismas funciones que el primero, pero Windows sabe como direccionar el llamado de las funciones gracias a la instancia creada.

PADRES E HIJOS (PARENT – CHILD)

Si tenemos un botón en un formulario, este botón es “HIJO” del formulario, o esta contenido en él. Es decir, se podría decir, con son hijos todas aquellas ventanas contenidas en otra, la cual es llamada “Parent” o padre. La ventana principal o “Main Window”, es nuestro padre principal, en él nosotros incluimos otras ventanas que son “hijos”, esto implica que al destruir al padre todas las ventanas hijas que están contenidas en él también son destruidas.

Una pregunta podría ser ¿La ventana principal es hija de alguien?. SI!, del escritorio de Windows o “Desktop Windows”, se puede ver al escritorio de Windows como el padre supremo. ¿Escritorio de Windows, que es eso?. Cuando arrancas Windows, él inicia un programa llamado “explorer.exe”, gracias a ese programa, ustedes ven la barra de inicio (donde esta el botón “inicio”), ven los iconos, y las diferentes ventanas que Windows le proporcionan, si no me creen aprieten CTRL.-ALT-DEL y finalicen el programa “explorer” verán que se desaparece todo.

Esto que es sencillo de entender, deben de tenerlo en claro al momento de crear una ventana ya que en su creación habría que determinar si la ventana es hija de alguien o es una ventana principal.

CLASE (CLASS)

Una clase, es como una plantilla, en esta plantilla podemos encontrar valores u objetos que toma una ventana al momento de su creación. Windows proporciona funciones para crear nuestras propias clases, inclusive cuando creamos una ventana usualmente se crea una clase. Esto es un poco mas complicado, los botones, por ejemplo, Windows los tiene en una clase “button”, es decir, Windows nos da una plantilla con ciertos parámetro ya predefinidos, con esa plantilla nosotros creamos una ventana que tiene la forma de un botón.

La clase se configura a través de una estructura:

Type WNDCLASS

style As Long

lpfnwndproc As Long

cbClsextra As Long

cbWndExtra2 As Long

hInstance As Long

hIcon As Long

hCursor As Long

hbrBackground As Long

lpszMenuName As String

lpszClassName As String

End Type

Style = aquí especificamos el estilo de la clase, OJO estilo de la clase, no estilo de la ventana, en la clase no se configuran los parámetros de cómo va ser la ventana, eso lo veremos mas adelante.

Entre los valores que puede tomar son los siguiente:

Public Const CS_BYTEALIGNCLIENT = &H1000

Public Const CS_BYTEALIGNWINDOW = &H2000

Public Const CS_CLASSDC = &H40

Public Const CS_DBLCLKS = &H8

Public Const CS_GLOBALCLASS = &H4000

Public Const CS_HREDRAW = &H2

Public Const CS_NOCLOSE = &H200

Public Const CS_OWNDC = &H20

Public Const CS_PARENTDC = &H80

Public Const CS_SAVEBITS = &H800

Public Const CS_VREDRAW = &H1

No les voy a mentir, los único que se usar son CS_VREDRAW y CS_HREDRAW, estos estilos, lo que hacen es que cuando la ventana es movida o alterada en su tamaño ya sea horizontal o vertical, la ventana es repintada, es decir, se envía un mensaje WM_PAINT, indicando que la ventana tiene que ser repintada.

Para las otras constantes que en realidad no le he visto uso, pueden documentarse en la ayuda de MSDN.

Lpfnwndproc = Aquí colocamos la dirección de nuestro “oído” o la función que gestiona los mensajes de la ventana, en la documentación oficial el gestor de mensajes lo pueden ver como “Windows Procedure”.

CbClsextra y cbWndExtra2 = tampoco le veo uso a estas dos variables de la estructura, según la documentación lo que hacen es asignar memoria extra. Pero en nuestros ejemplos vamos a poner 0 en ambas.

HInstance = instancia de la aplicación que contiene el gestor de mensajes.

HIcon = Icono que se muestra en la esquina superior izquierda de la ventana.

HCursor = Cursor que se muestra en el momento que el mouse entra en la ventana.

HbrBackground = fondo de la ventana cuando ella es creada.

LpszMenuName = handle del menú que utiliza la ventana.

LpszClassName = nombre de la clase.

Después de configurar como va ser nuestra clase procedemos a registrarla.

RegisterClass

Api: Declare Function RegisterClass Lib "user32" Alias "RegisterClassA" (Class As WNDCLASS) As Long

Esta función se encarga de registrar una clase, pueden ver que como único parámetro se le pasa una variable del tipo WNDCLASS.

Si la función retorna 0 significa que la creación de la clase fallo. En caso de ser exitosa la creación retornara un número distinto de 0.

RegisterClassEx

Api: Declare Function RegisterClassEx Lib "user32" Alias "RegisterClassExA" (pcWndClassEx As WNDCLASSEX) As Integer

Hace exactamente lo mismo que la función API anterior con la diferencia que recibe una estructura del tipo WNDCLASSEX.

Esta estructura es casi igual a WNDCLASS:

Type WNDCLASSEX

cbSize As Long

style As Long

lpfnWndProc As Long

cbClsExtra As Long

cbWndExtra As Long

hInstance As Long

hIcon As Long

hCursor As Long

hbrBackground As Long

lpszMenuName As String

lpszClassName As String

hIconSm As Long

End Type

Tiene solo dos variables nuevas, cbSize que es el tamaño de la estructura, en VB pueden usar la función Len() para ello y en C++ se usa “sizeof”. Y la variable “hIconSm” que no es mas que el icono mostrado en la barra inferior cuando la ventana se minimiza. De resto es exactamente lo mismo.

UnregisterClass

Api: Declare Function UnregisterClass Lib "user32" Alias "UnregisterClassA" (ByVal lpClassName As String, ByVal hInstance As Long) As Long

Esta función lo que hace es eliminar la clase registrada. Como primer parámetro se le pasa el nombre de la clase, y luego la instancia de la aplicación.

Si esto todavía no lo captan esperen ver el ejemplo que se presenta a continuación.

VENTANA NUEVA

Para este proyecto solo es necesario un modulo, es decir, no tiene formulario. En el modulo pongan este código, y configuren en “PROYECTO- Propiedades del proyecto...” la opción de arrancar por el “Sub main”.

Código:

Ver Codigo

Cuando arranquen verán una ventana, con fondo blanco y dos botones. Aunque este ejemplo es igual al escrito en C al inicio del manual, voy a volver a explicar paso a paso el código, para dejar en claro ciertas funciones.

Empecemos en el Sub Main, con este bloque de código:

If Not RegistrarClase(AddressOf GestorMensajes) Then

MsgBox "Falla en la creacion de la clase"

UnregisterClass "mipropiaclase", App.hInstance

Exit Sub

End If

Lo primero que hacemos es llamar a la función RegistrarClase, ya que una ventana NO puede ser creada sin referenciarla a una clase.

Private Function RegistrarClase(FuncionMensajes As Long) As Boolean

Dim clase As WNDCLASS

clase.style = CS_HREDRAW Or CS_VREDRAW

clase.lpfnWndProc = FuncionMensajes

clase.cbClsExtra = 0

clase.cbWndExtra2 = 0

clase.hInstance = App.hInstance

clase.hIcon = LoadIcon(0, IDI_APPLICATION)

clase.hCursor = LoadCursor(0, IDC_ARROW)

clase.hbrBackground = CreateSolidBrush(RGB(255, 255, 255))

clase.lpszMenuName = 0

clase.lpszClassName = "mipropiaclase"

RegistrarClase = (RegisterClass(clase) <> 0)

End Function

Lo primero que hacemos es crear una variable “clase” que sea del tipo WNDCLASS. Luego llenamos los valores, en el parámetro “lpfnWndProc” le asignamos el lugar de memoria en donde se encuentra nuestro “oido” o gestor de mensajes. Los demas parámetro, no deberían de tener problema de entenderlos, ya que el significado de estos parámetro fueron estudiados arriba.

Pero de seguro, algunas personas pueden preguntarse ¿Referenciamos el gestor de mensajes sin crear la ventana?. Recuerden que lo que estamos haciendo es una plantilla, veámoslos con un ejemplo, imaginen un control “X”, ese control “X” posee un gestor de mensajes, el cual se configura en su clase, cada vez que creamos una ventana referenciada a esa clase, el control que se crea , se podría decir, copia el gestor de mensajes que posee la clase, si tenemos 100 controles pues ellos toman 1 gestor de mensaje para cada uno, es decir sería 100 gestores de mensaje.

Imaginen ahora que no existe esto de crear una plantilla o clase, y necesitan crear 100 controles “X”, tendrían que programar “individualmente” 100 gestores de mensajes, esto haría que un control tenga un comportamiento distinto al otro, siendo el mismo control “X”, ahora ven un poco mas claro la utilidad de crear una clase o plantilla antes de crear la ventana.

Esta clase que nosotros creamos la llamamos “mipropiaclase”. Ya registrada la clase podemos entonces crear la ventana:

If Not CrearAplicacion Then

MsgBox "Falla en la creacion de la aplicacion"

UnregisterClass "mipropiaclase", App.hInstance

Exit Sub

End If

Este IF llama a la función CrearAplicacion la cual se muestra a continuación:

Private Function CrearAplicacion() As Boolean

'Tipos de Ventanas Principales

hWnd = CreateWindowEx(0, "mipropiaclase", "Ventana Principal", WS_OVERLAPPEDWINDOW Or WS_HSCROLL Or WS_VSCROLL, 0, 0, 500, 400, 0, 0, App.hInstance, ByVal 0&)

.

.

.

'Ventanas Hijas

hwnd2 = CreateWindowEx(0, "button", "Presioname", BS_PUSHBUTTON Or WS_CHILD, 0, 0, 100, 50, hWnd, 0, App.hInstance, ByVal 0&)

hwnd3 = CreateWindowEx(0, "button", "Salir", BS_PUSHBUTTON Or WS_CHILD, 0, 100, 100, 50, hWnd, 0, App.hInstance, ByVal 0&)

If hWnd = 0 Then

CrearAplicacion = False

Exit Function

End If

ShowWindow hWnd, SW_NORMAL

ShowWindow hwnd2, SW_NORMAL

ShowWindow hwnd3, SW_NORMAL

CrearAplicacion = True

End Function

En esta función es donde procedemos a crear nuestra ventana, para ello expliquemos la siguiente función:

CreateWindowEx

Api: Declare Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long

Esta función sirve para crear “fisicamente” la ventana. Los parámetros son:

DwExStyle: es donde se configuran estilos “extras”, estos valores se pueden combinar usando el operador OR, los valores pueden ser:

Public Const WS_EX_ACCEPTFILES = &H10& : permite que la ventana creada acepte el arrastre o “drap-drop” de archivos.

Public Const WS_EX_APPWINDOW = &H40000& : Fuerza la ventana a ubicarse en el TOP-LEVEL, cuando ella es visible, esto ocurre al presionar la clic en el “TaskBar”, TaskBar en la barra de botones que se muestra en la barra inferior, donde esta el botón de “inicio”.

Public Const WS_EX_CLIENTEDGE = &H200&

Public Const WS_EX_CONTEXTHELP = &H400& : Crea una ventana con un botón de “?” en el área no cliente. Este botón envía un mensaje WM_HELP a la ventana hija que se encuentre en la ventana en el momento de presionar el clic del mouse. Este estilo no puede ser usado con los estilos de ventanas WM_MAXIMIZEBOX y WM_MINIMIZEBOX.

Public Const WS_EX_CONTROLPARENT = &H10000& : Permite a la ventana navegar entre las ventanas hijas por medio de la tecla TAB.

Public Const WS_EX_DLGMODALFRAME = &H1& : Crea una ventana con doble borde

Public Const WS_EX_LEFT = &H& : Crea una ventana que posee una alineación hacia la izquierda, este estilo esta por defecto.

Public Const WS_EX_LEFTSCROLLBAR = &H4000&

Public Const WS_EX_LTRREADING = &H& : Ventana cuyo texto desplegado se representa de izquierda a derecha, este estilo también esta por defecto.

Public Const WS_EX_MDICHILD = &H40& : crea una ventana hija MDI.

Public Const WS_EX_NOPARENTNOTIFY = &H4& : Especifica que la ventana hija no envía el mensaje WM_PARENTNOTIFY cuando esa ventana es creada o destruida.

Public Const WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE Or WS_EX_CLIENTEDGE)

Public Const WS_EX_PALETTEWINDOW = (WS_EX_WINDOWEDGE Or WS_EX_TOOLWINDOW Or WS_EX_TOPMOST)

Public Const WS_EX_RIGHT = &H1000& : Ventana cuya propiedad de alineación es “derecha”

Public Const WS_EX_RIGHTSCROLLBAR = &H& : Si existe una barra de desplazamiento vertical esta se encuentra a la derecha de la ventana. Estilo por defecto.

Public Const WS_EX_RTLREADING = &H2000& : El texto desplegado en la ventana se representa de derecha – izquierda, utilizado para los casos en que se representan texto con lenguajes arabes.

Public Const WS_EX_STATICEDGE = &H20000& : crea una ventana con un borde tridimensional, pensado para ser usado en elementos que no aceptan entrada de usuario.

Public Const WS_EX_TOOLWINDOW = &H80&

Public Const WS_EX_TOPMOST = &H8& : Fuerza a la ventana a ser ubicada en el TOP-LEVEL, inclusive si esta desactivada se mantiene por encima de todas las demas.

Public Const WS_EX_TRANSPARENT = &H20&

Public Const WS_EX_WINDOWEDGE = &H100& : Crea una ventana con un borde un poco levantado

Algunas constantes no fueron explicadas, una por que son combinaciones de otras ya existentes, y las otras les dejo para que vean se explicación en la documentación oficial, ya que no les veo uso, desde mi punto de vista.

LpClassName = en este parámetro pasamos el nombre de la clase que fue registrada previamente.

LpWindowName = le pasamos el nombre de la ventana, en un formulario equivale al nombre que es mostrado en el CAPTION.

DwStyle= estilo de la ventana, estos estilos ya fueron explicados arriba, y se pueden combinar con el operador OR.

x, y, nWidth, nHeight = estos 4 parámetros indican la posición de la ventana en la pantalla y su respectivo tamaño, los valores vienen especificados en píxeles.

HWndParent = en caso de ser una ventana hija, aquí es donde se coloca el hwnd de la ventana padre.

HMenu = en caso de poseer menú, es aquí donde se coloca el handle del objeto menú

HInstance = le pasamos la instancia de la aplicación.

LpParam = utilizada para pasarle una variable del tipo CREATESTRUCT, pero en realidad todavía yo no le he visto utilidad a esta variable.

Si la función tiene éxito al crear la ventana retorna un valor distinto de 0.

¿Como la usamos?. Veamos nuestro ejemplo:

hWnd = CreateWindowEx(0, "mipropiaclase", "Ventana Principal", WS_OVERLAPPEDWINDOW Or WS_HSCROLL Or WS_VSCROLL, 0, 0, 500, 400, 0, 0, App.hInstance, ByVal 0&)

Con lo explicado, ya deberían de entender lo que hace la función, básicamente estamos creando una ventana que no es hija, ya que en HwndParent estamos pasando 0, pero OJO, no es hija de una ventana creada por nosotros, pero es hija del Desktop Windows o Escritorio de Windows. Noten que como segundo parámetro le pasamos el nombre de la clase registrada por nosotros con anterioridad.

En el código puse ejemplos de varias ventanas, véanlas una por una, quitando los comentarios correspondiente.

Vamos a ver estas dos líneas:

hwnd2 = CreateWindowEx(0, "button", "Presioname", BS_PUSHBUTTON Or WS_CHILD, 0, 0, 100, 50, hWnd, 0, App.hInstance, ByVal 0&)

hwnd3 = CreateWindowEx(0, "button", "Salir", BS_PUSHBUTTON Or WS_CHILD, 0, 100, 100, 50, hWnd, 0, App.hInstance, ByVal 0&)

Con estas dos líneas creamos 2 botones, de la clase “button”, esta clase no fue creada por nosotros; Windows tiene unas clases “system class” o clases de sistemas, las cuales sirven para la creación de controles básicos como un botón, combobox, listbox, edit, o label (static). Ya estas clases están registradas por Windows cuando este arranca.

Pero vean lo siguiente, estas ventanas son hijas de la ventana principal, como son hijas hay que especificar WS_CHILD en los estilos y un “hwnd” en el parámetro HwndParent con esto le estamos diciendo que son hijas del “hwnd”, en pocas palabras son hijas de la ventana que creamos al principio. Hay algo curioso que es la constante BS_PUSHBUTTON, ¿Es esto un estilo de ventana?. No exactamente, este estilo es especifico y solo sirve para los botones, no funciona para ningún otro control. Cada control de las clases del sistema “system class”, posee estilos propios, el documentarse sobre esto, si se los dejo a ustedes, pero no se preocupen no son nada del otro mundo, si han entendido mas o menos todo esto de seguro entenderán esos estilos sin problemas.

Lo que sucede es que no quiero extender el capitulo en cosas muy pocos usadas. Pero ustedes pueden decir ¿Para que sirve todo lo visto hasta el momento?. Al final del capitulo les contare una anécdota.

Luego de haber creado las ventanas hay que mostrarlas y para ello es la función que se muestra a continuación:

ShowWindow

Api: Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long

Esta función realiza una acción dependiendo del valor del parámetro nCmdShow, pueden ver que como primer parámetro se le pasa el “hwnd” o ventana a la cual queremos aplicar dicha acción.

Valores para nCmdShow:

Public Const SW_HIDE = 0 : Esconde la ventana

Public Const SW_MAXIMIZE = 3 : Maximiza la ventana especificada.

Public Const SW_MINIMIZE = 6 : Minimiza la ventana especificada.

Public Const SW_RESTORE = 9 : Restaura la ventana, si esta se encuentra maximizada o minimizada, es parecido a lo que hace el botón que esta entre el boton de minimizar y maximizar.

Public Const SW_SHOW = 5 : Activa y muestra la ventana, usando el tamaño y posición actual.

Public Const SW_SHOWMAXIMIZED = 3 : Activa la ventana y la maximiza.

Public Const SW_SHOWMINIMIZED = 2 : Activa la ventana y la minimiza.

Public Const SW_SHOWMINNOACTIVE = 7 : Muestra la ventana minimizada, y no la activa, dejando activa la que se encuentra en esa momento activada.

Public Const SW_SHOWNA = 8 : Muestra la ventana pero sin activarla, es decir, sin darle el foco.

Public Const SW_SHOWNOACTIVATE = 4 : Igual que SW_SHOW pero no la activa.

Public Const SW_SHOWNORMAL = 1 : Activa y muestra la ventana.

Pueden ver que es sencillo lo que hace esta función.

Luego de mostrar las ventanas en caso de que hayan tenido éxito su creación, nos vamos a este bloque de código:

Do While (GetMessage(mensaje, 0, 0, 0))

TranslateMessage mensaje

DispatchMessage mensaje

Loop

Este bucle “finito” es el que habilita nuestro oido para escuchar.

GetMessage

Api: Declare Function GetMessage Lib "user32" Alias "GetMessageA" (lpMsg As Msg, ByVal hWnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long) As Long

Función que toma el mensaje que se encuentra en la cola. Como primer parámetro le pasamos una variable que es de tipo MSG, la cual es una estructura con las siguientes características:

Type Msg

hWnd As Long ‘ Ventana a la cual pertenece el mensaje

message As Long ‘ Mensaje que se envía

wParam As Long ‘ Información del mensaje

lParam As Long ‘ Información del mensaje

time As Long ‘ Tiempo que tiene el mensaje en la cola.

pt As POINTAPI ‘ Coordenada del puntero del mouse al momento de poner el mensaje en la cola

End Type

Como segundo parámetro tenemos “hWnd”, que no es mas que la ventana de los cuales queremos recibir sus mensajes, es decir, si colocamos nuestro “hwnd” obtenido en la creación de la ventana, pues el GetMessage recibirá los mensajes destinados solamente a ese HWND. Ustedes dirán ¿Pero pusiste 0, que significa eso?. Que tomo los mensajes correspondiente al hilo de la aplicación.

Todo aplicación cuando arranca, crea lo que se llama un hilo o thread, en ese hilo nosotros ejecutamos todo nuestro código, al poner 0, le digo al programa que tome los mensajes de todas las ventanas que pertenecen a ese hilo. Si nada mas pongo el “hwnd” que me retorna CreateWindowEx, entonces recibiré los mensajes de la ventana principal, pero los mensajes de cada boton no los recibo, al poner 0, como los botones fueron creados en el hilo de la aplicación, esto significa que la función tomara los mensajes correspondiente a los botones. La parte de hilo o thread será explicado mas adelante en un capitulo.

WmsgFilterMin, wMsgFilterMax = Para explicar estos parámetros tienen que tener en claro que los mensajes no son mas que números, con estos 2 parámetros podemos filtrar los mensajes que se encuentren en el intervalo que forman estos dos parámetros. Es muy raro utilizar dichos parámetros por lo que usualmente se les coloca 0 para que reciban todos los mensajes sin excepción.

TranslateMessage

Api: Declare Function TranslateMessage Lib "user32" (lpMsg As Msg) As Long

Esta función se encarga de convertir los mensajes que provienen de teclas virtuales en mensajes del tipo CHAR o WM_CHAR., siempre que la tecla tenga su equivalente valor ASCII. Como único parámetro tome un valor del tipo MSG.

DispatchMessage

Api: Declare Function DispatchMessage Lib "user32" Alias "DispatchMessageA" (lpMsg As Msg) As Long

Esta función se encargar de enviar el mensaje al gestor de mensajes configurado para ello. Como único parámetro toma un valor del tipo MSG.

Funciones Adicionales:

LoadCursor

Api: Declare Function LoadCursor Lib "user32" Alias "LoadCursorA" (ByVal hInstance As Long, ByVal lpCursorName As Any) As Long

Función utilizada para cargar un cursor del mouse, como primer parámetro le pasamos la instancia de nuestra aplicación. Y como segundo parámetro le pasamos o el nombre del recurso o una constante que identifica a un cursor que posee el sistema operativo. Como VB no trabaja con la filosofía de archivos de recursos, nada mas pondré las constante de los cursores predeterminados de Windows. Pero para que la función acepte dicha constante es necesario ponerle 0 al parámetro de hInstance.

Valores para “lpCursorName”:

Public Const IDC_ARROW = 32512& : Muestra la flecha tradicional.

Public Const IDC_APPSTARTING = 32650& : Muestra la flecha tradicional con un pequeño reloj de arena.

Public Const IDC_CROSS = 32515& : Cursor “crusshair”

Public Const IDC_HAND = (32649) : Muestra una manito, este sirve para NT 5.0 en adelante

Public Const IDC_IBEAM = 32513& : Cursor “I-beam”

Public Const IDC_NO = 32648& : Un circulo con una diagonal.

Public Const IDC_SIZEALL = 32646& : Muestra una cruz cuyas puntas son flechas.

Public Const IDC_SIZENESW = 32643& : Muestra una línea diagonal con flechas en sus puntas, en dirección Noreste – suroeste

Public Const IDC_SIZENS = 32645& : Muestra una línea, con flechas en sus puntas, en dirección Norte – sur

Public Const IDC_SIZENWSE = 32642& : Muestra una línea, con flechas en sus puntas, en dirección Noroeste – sureste

Public Const IDC_SIZEWE = 32644& : Muestra una línea, con flechas en sus puntas, en dirección Oeste – Este

Public Const IDC_UPARROW = 32516& : Flecha vertical.

Public Const IDC_WAIT = 32514& : Reloj de arena.

LoadIcon

Api: Declare Function LoadIcon Lib "user32" Alias "LoadIconA" (ByVal hInstance As Long, ByVal lpIconName As Long) As Long

Al igual que LoadCursor toma como primer parámetro la instancia de la aplicación y el segundo parámetro toma el nombre de un recurso, o una constante que representa a un icono predeterminado del sistema, al igual que LoadCursor en caso de tomar los iconos predeterminados de Windows es necesario poner 0 en el valor de hInstance; esas constantes pueden ser:

Public Const IDI_APPLICATION = 32512& : Icono de la aplicación por defecto

Public Const IDI_ASTERISK = 32516& : Icono ASTERISK

Public Const IDI_ERROR = IDI_HAND

Public Const IDI_EXCLAMATION = 32515& : Icono de exclamación.

Public Const IDI_HAND = 32513& : Icono de una mano sombreada.

Public Const IDI_INFORMATION = IDI_ASTERISK

Public Const IDI_QUESTION = 32514& : Icono de un signo de interrogación

Public Const IDI_WARNING = IDI_EXCLAMATION

Public Const IDI_WINLOGO = 32517 : Icono del logo de windows.

DestroyWindow

Api: Declare Function DestroyWindow Lib "user32" (ByVal hWnd As Long) As Long

Función encargada de destruir una aplicación, enviando a la cola de mensajes, el mensaje WM_DESTROY. Recuerden, que si la ventana es la principal, todas las ventanas hijas a él se destruyen.

DefWindowProc

Api: Declare Function DefWindowProc Lib "user32" Alias "DefWindowProcA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Función encargada de utilizar el gestor de mensajes por defecto de una ventana. ¿Qué quiero decir?. Vamos al ejemplo:

GestorMensajes = DefWindowProc(hWnd, mensaje, wParam, lParam)

Aquí lo que hacemos, es que el mensaje, sea procesado por el gestor de mensajes por defecto, nuestra aplicación no gestiona todos los mensajes que existen, y siempre es bueno procesarlos todo, pero como algunos no son útiles para nosotros, dejamos que el gestor de mensajes por defecto se encargue de los mensajes que no nos interesa.

PostQuitMessage

Api: Declare Sub PostQuitMessage Lib "user32" (ByVal nExitCode As Long)

Con esta función salimos del hilo de la aplicación, es decir, salimos del Do While (GetMessage(mensaje, 0, 0, 0)), concluyendo así la aplicación. Como parámetro le pasamos 0, y así indicamos la finalización del programa, es como decirle a la aplicación que ya no reciba mas mensajes.

ENUMERANDO VENTANAS

A continuación vamos a ver un proyecto muy interesante, que muestra la manipulación de las ventanas de Windows, lo que vamos hacer en el proyecto es tomar todas las ventanas abiertas, y en algunas vamos hacer ciertas cositas.

Para este proyecto se necesita 1 formulario llamado “frmEnumWin” y 1 modulo. En el formulario colocan lo siguientes controles:

5 Botones.

6 Etiquetas o Label.

2 ListBox

Nota: no aprieten los botones mostrados hasta que yo los indique lo que vamos hacer.

Ver Codigo

Cuando arrancan el proyecto verán un formulario con 2 listbox, uno de ellos se encuentra lleno, y podrán ver que hay como dos columnas, una donde hay números y la otra donde hay un texto, ejm: “456 XXXXX”, déjenme decirle que lo que ven en ese listbox son las aplicaciones o ventanas principales abiertas en ese momento, no son todas, ya que filtre aquellas aplicaciones que no poseen “texto”, ya lo veremos cuando explique el código.

Si dan clic, en el list1, se darán cuenta que para algunas aplicaciones el list2 se llena, este list2 contiene TODAS las ventanas HIJAS que posee la aplicación principal.

¿Cómo jugar con eso?. Habrán la calculadora y pónganla en opción “ESTANDAR”. Luego cuando la tengan abierta denle al botón “R” ubicado arriba del list1, este botón lo que hace es refrescar la lista de aplicaciones abiertas.

Después de apretar “R”, se darán cuenta que la calculadora se encuentra en el list1, ella posee un número “XXXX Calculadora”, ese XXXX, (puede ser de 3 cifras, 2 cifras o mas), es el HWDN de esa ventana, ahora denle clic en el list1, a la fila que tiene la calculadora. En ese momento en el list2, se encuentran todas las ventanas hijas de la calculadora.

En el list2, debería de haber una fila que sea de esta manera “XXXX 0,” XXXX puede ser cualquier número, si se dan cuenta “0,” es el texto que se muestra en la calculadora, ¡Sorprendente!, seleccionen esa fila en el list2, cuando este seleccionada presionen el botón “Enviar Texto” que se encuentra en la aplicación.

Al presionarlo no ven nada, pero ahora minimicen la calculadora y restáurenla de nuevo. ¿Y que ven?. MAGIA!!!, mucho se deben de preguntar, ¿Dios!!!, como lo hiciste?, vamos a seguir dando sorpresas, si dan clic a los números que no poseen texto, se darán cuenta abajo en la etiqueta la clase a la cual pertenece, y también se darán cuenta que la mayoría son de la clase “button”, es decir todos los HWND que tenga clase “button” nosotros sabemos que representa a los botones de la calculadora.

Pero si son curiosos deberían de estar preguntándose ¿Por qué no vemos como texto los números de los botones?, interesante pregunta que no he podido responder, es muy raro que no aparezca dicho texto, eso me hace pensar que los números son gráficos, pero es como ilógico, ya que en algo tan simple, ¿para que usar gráficos?, en realidad esa respuesta no la se. Pero eso no impide que sigamos jugando, ¿Cómo sabemos cual botón corresponde a los números?. Fácil, vamos a dar clic a cada fila cuyo HWND sea de la clase “button” y luego presionan el botón “ENVIAR CLICK”, tengan en cuenta que deben de ver el formulario y la calculadora al mismo tiempo, cuando den con el botón indicado verán como la calculadora escribe el número sin haber tocado directamente la calculadora.

En mi caso los números empiezan aparecer desde la fila 5 del list2. ¡sorprendente!, luego que jueguen un poco con eso, en el list1 verifiquen que este seleccionado la fila correspondiente de la calculadora y presionen el botón “DESTRUIR APLICACIÓN”, después de presionarlo verán que ya no hay calculadora, siguen viéndolo en la lista por que tienen que refrescar presionando el botón “R”.

Si siguieron todo al pie de la letra, y les funciono, se habrán dado cuenta que pudimos manipular una ventana desde otra. A continuación vamos a ver las funciones que permitieron realizar esa magia.

EnumWindows

Api: Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Boolean

Esta función es similar “en como funciona” a la función que se encargaba de enumerar las fuentes, lo que se le pasa a esta función es la dirección de otra función que cumple ciertas características, dicha función va ser invocada por cada ventana que este abierta.

En conclusión, esta función enumera las ventanas principales abiertas del sistema, como primer parámetro le pasamos la dirección de memoria de la función que procesara la información de cada ventana, y como segundo parámetro le pasamos alguna información adicional que nosotros creamos conveniente.

La función que se le pasa en el primer parámetro debe de cumplir los siguientes requisitos o parámetros:

Public Function EnumWindowsProc(ByVal hWnd As Long, ByVal lParam As Long) As Boolean

Esta función solo tiene 2 parámetros el primero, es el “hwnd” de la ventana abierta y como segundo parámetro es la información que mandamos como segundo parámetro de la función EnumWindows.

Lo que quiero que vean es que, lo que hace el programa es entrar a esta función tantas veces como ventana abiertas existan. Y cada ves que entra es con un “hwnd” distinto.

EnumChildWindows

Api: Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Esta función es la encargada de enumerar todas las ventanas hijas de una ventana padre.

Casi igual a la anterior, con la diferencia, que como primer parámetro tenemos un “hwnd”, el cual es el padre del cual queremos ver que ventana hijas posee, el segundo parámetro es la dirección de la función a la cual se le pasa la información de la ventana hija y como tercer parámetro tenemos la información adicional que puede ser enviada a la función.

Esa función debe de cumplir la siguiente estructura:

Public Function EnumChildProc(ByVal hWnd As Long, ByVal lParam As Long) As Long

Como pueden ver es exactamente igual a la función anterior, con la diferencia que esta función se va a ejecutar tantas veces como ventanas hijas posea el padre que fue pasado en el parámetro “hWndParent”

¿Qué hacemos dentro de esas funciones?. ¡Lo que quieran!, ya que poseen el HWND, número clave al momento de manipular cualquier ventana, en nuestro caso, lo que hice fue tomar el texto de la ventana y para ello use 2 funciones claves.

GetWindowTextLength

Api: Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long

Esta función lo que hace es retornar la longitud del texto que tiene la ventana ¿qué texto?. Se recuerdan en la función CreateWindowEx, el parámetro “LpWindowName” ese es el texto al cual me refiero, o mas fácil aun, en un formulario es el titulo que esta en la barra del caption, en un botón sería el texto que muestra, y así dependiendo del control o tipo de ventana que sea.

Esta función la utilizamos para reservar el espacio en memoria para dicho texto, ya que la función que toma el texto es la siguiente:

GetWindowText

Api: Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Esta función es la encargada de tomar el texto de una ventana, como primer parámetro le pasamos el “hwnd” de la ventana a la cual queremos tomar el texto, como segundo parámetro, pasamos la variable que almacenara dicho texto, y como tercer parámetro le pasamos la cantidad de caracteres que queremos recuperar.

¿Cómo usamos todo lo mencionado en nuestro proyecto?.

En el LOAD del formulario tenemos esta línea:

EnumWindows AddressOf EnumWindowsProc, ByVal 0&

Con esta línea activamos la función EnumWindowsProc la cual se ejecutara tantas veces como ventanas principales abiertas existan. Dicha función esta formada por el siguiente bloque de código.

Public Function EnumWindowsProc(ByVal hWnd As Long, ByVal lParam As Long) As Boolean

Dim texto As String ‘ Declaramos la variable de tipo texto que almacenara el nombre de la ventana

texto = Space$(GetWindowTextLength(hWnd) + 1) ‘ Reservamos en memoria el espacio para dicho nombre, en pocas palabras, llenamos con espacio en blanco la variable “texto”.

GetWindowText hWnd, texto, Len(texto) ‘ Tomamos el texto de la ventana.

texto = Left$(texto, Len(texto) - 1) ‘Quitamos el último carácter, que es un espacio en blanco.

If texto <> "" Then frmEnumWin.List1.AddItem Trim(Str$(hWnd) + " " + texto) ‘ Si el nombre es distinto a “” entonces lo añado al listado.

EnumWindowsProc = True ‘Retorno TRUE para que la función siga buscando ventanas, ya que en caso de poner FALSE, pues la enumeración de las ventanas se para.

End Function

Pueden ver que hago If texto <> "" Then, si ustedes quitan este IF verán TODAS las ventanas abiertas con nombre y sin nombres. Particularmente una ventana sin nombre, es muy difícil saber que es.

Pueden preguntarse ¿Por qué hay nombre tan raros?. Y ¿Por qué hay mas ventanas abiertas de las que puedo ver?. Con esto se dan cuenta de los procesos que Windows maneja internamente, hay programas no activados por nosotros sino por el sistema operativo, algunos programas pueden ser pequeños o carecer de ventanas hijas. Estas aplicaciones no son controladas por nosotros sino por Windows, por eso es que tienen que tener cuidado con el botón “Destruir Aplicación”, úsenlo siempre y cuando sepan que aplicación van a destruir, no destruyan cualquier aplicación ya que puede producir que Windows se quede colgado.

La explicación de arriba vale perfectamente para la función EnumChildProc con la diferencia que esta llena el list2, cada vez que damos clic en una fila del list1.

Cada vez que daban clic a uno de los 2 listbox, se daban cuenta que en una etiqueta en la parte de abajo, llenábamos dicha etiqueta con el nombre de la clase a la cual pertenece la aplicación, a continuación vamos a explicar la función encargada de retornarnos el nombre de dicha clase.

GetClassName

Api: Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

Con esta función tomamos el nombre de la clase de cierta ventana. Como primer parámetro le pasamos el “hwnd” de la ventana, como segundo parámetro le pasamos la variable de tipo String que almacenara dicho nombre, y como tercer parámetro le pasamos la cantidad de caracteres que queremos retornar.

El valor que retorna dicha función es la longitud del nombre de la clase.

En el ejemplo lo usamos en List1_Click de la siguiente manera:

hWnd = Mid(List1.Text, 1, InStr(1, List1.Text, " ")) Con este codigo tomamos el HWND del listbox, recuerden que el listbox tiene “XXXX NOMBRE DE LA VENTANA” esta línea de codigo toma el XXXX que es el hwnd de la ventana

'Buscamos la clase

nombreclase = Space(256) Reservamos 256 espacios

resultado = GetClassName(CLng(hWnd), nombreclase, 256) Llamamos a la función pasándole la variable “nombreclase” variable que almacenara dicho nombre y en la variable “resultado” almacenamos la longitud de la cadena

Label6 = Left$(nombreclase, resultado) Quitamos los espacios en blanco e imprimimos el nombre en la etiqueta.

La explicación vale para List2_Click

GetDesktopWindow

Api: Declare Function GetDesktopWindow Lib "user32" () As Long

Esta función retorna el “hwnd” del escritorio de Windows, esta función lo usamos cuando le damos clic al botón "Enumerar los Child del Destkop de Windows". Que tiene el siguiente codigo:

EnumChildWindows GetDesktopWindow, AddressOf EnumChildProc, ByVal 0&

Al apretar clic en este botón llenamos el list2 con TODAS las ventanas abiertas, como el padre es el escritorio de Windows, en el list2 verán TODO!, cuando me refiero a todo, hablo de ventanas principales, y sus respectivas ventanas hijas, por esa razón algunas filas del list2 se repiten en el list1.

SetWindowText

Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String) As Long

Función utilizada para asignar un texto en una ventana, como primer parámetro le pasamos el “hwnd” de la ventana, y como segundo parámetro le pasamos el texto que queremos mostrar.

El uso de esta función, creo que es simple y se encuentra en el evento Command3_Click

Mensajes

¿Cuándo estábamos con la calculadora, como enviábamos los mensajes?. Usando la función SendMessage, pero vamos a ver el codigo:

Private Sub Command2_Click()

Dim hWnd As String

hWnd = Mid(List1.Text, 1, InStr(1, List1.Text, " ") - 1) ‘ Tomamos el “hwnd” de la ventana

SendMessage CLng(hWnd), WM_CLOSE, 0, 0 ‘Enviamos el mensaje WM_CLOSE, mensaje encargado de cerrar una aplicación

End Sub

En el otro botón:

Private Sub Command4_Click()

Dim hWnd As String

hWnd = Mid(List2.Text, 1, InStr(1, List2.Text, " ") - 1)

SendMessage CLng(hWnd), BM_CLICK, 0, 0

End Sub

Noten que enviamos el mensaje BM_CLICK, ese es un mensaje exclusivo de los botones, cuando un botón es pulsado Windows le manda el mensaje BM_CLICK para indicarle al botón que se le ha presionado. Este mensaje no es un mensaje para las ventanas en general, es solo usado para los controles de la clase “button” y que deriven de esa clase. Ya que el botón de VB no es de clase “button”, pero acepta el mensaje “BM_CLICK”

La función SetWindowPos no lo explico aquí ya que se explica en la sección que viene a continuación.

BUSCANDO Y POSICIONANDO VENTANAS

Para este proyecto es necesario: 2 Botones, 1 TextBox y 1 Label. Luego pegan este codigo en el formulario:

Ver Codigo

Para explicar lo que hace el programa iremos directo a las funciones.

SetWindowPos

Api: Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

Esta función lo que hace es cambiar el posicionamiento de la ventana y su tamaño. Como primer parámetro tenemos el “hwnd” que queremos alterar.

El segundo parámetro determina en que posición vamos a colocar nuestra ventana, este parámetro puede tomar los siguientes valores:

Private Const HWND_BOTTOM = 1 : Envía la ventana al fondo, es decir si hay 10 ventanas abiertas y la ventana que queremos alterar esta en el inicio, esta es enviada al final de todas las demás ventanas.

Private Const HWND_NOTOPMOST = -2 : Coloca la ventana por encima de todas las ventanas no-topmmost. Pero queda detrás de aquellas que estén en el topmost

Private Const HWND_TOP = 0 : Coloca la ventana en el TOPE es decir de primera.

Private Const HWND_TOPMOST = -1 : Coloca la ventana en el TOPE, y se mantiene en esa posición inclusive si la ventana se encuentra desactivada.

Esta ultima opción es la utilizada, cuando usamos formularios de presentación en el inicio de nuestro programa, como por ejemplo, la ventana de inicio de Word, que se mantiene en el inicio hasta que la aplicación se encuentra cargada completamente, inclusive VB también lo utiliza.

Para ver tal efecto aprieten el botón “Siempre en el Top”, y verán que no importa que ventana intente superponer, siempre nuestro formulario estará de primero, para regresarla a la normalidad vuelvan a presionar el botón.

Parámetros x, y, cx, cy : los dos primeros, determinan las nuevas coordenadas x e y donde será colocada la ventana y cx, cy, determinan el nuevo alto y ancho de la ventana.

Tenemos como último parámetro wFlags, que puede tomar los siguientes valores (dichos valores pueden ser combinados con el operador OR):

Private Const SWP_ASYNCWINDOWPOS = &H4000

Private Const SWP_DEFERERASE = &H2000 : Previene la generación del mensaje WM_SYNCPAINT

Private Const SWP_FRAMECHANGED = &H20 : Dibuja el marco alrededor de la ventana

Private Const SWP_DRAWFRAME = SWP_FRAMECHANGED

Private Const SWP_HIDEWINDOW = &H80 : Esconde la ventana

Private Const SWP_NOACTIVATE = &H10 : No activa la ventana, no importa si colocaron la ventana en el inicio, no se activa.

Private Const SWP_NOCOPYBITS = &H100

Private Const SWP_NOMOVE = &H2 : Retiene la posicion actual de la ventana, es decir, hace caso omiso a los parámetro X y Y de la función.

Private Const SWP_NOOWNERZORDER = &H200 : No se realiza ningún cambio de posicionamiento a la ventana padre.

Private Const SWP_NOREDRAW = &H8 : No repinta la ventana.

Private Const SWP_NOREPOSITION = SWP_NOOWNERZORDER

Private Const SWP_NOSENDCHANGING = &H400 : previne a la ventana para recibir el mensaje WM_WINDOWPOSCHANGING

Private Const SWP_NOSIZE = &H1 : Mantiene el ancho y la altura actual, es decir, hace caso omiso a los parámetros cx e cy.

Private Const SWP_NOZORDER = &H4 : Mantiene la posicion actual de la ventana, es decir, hace caso omiso al valor pasado en el parámetro hWndInsertAfter.

Private Const SWP_SHOWWINDOW = &H40 : Despliega la ventana.

FindWindow

Api: Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Función que se encarga de buscar a una ventana, hay dos formas de buscar una ventana o por su clase, o por su nombre, en caso de ser por su clase, deberán de colocar el nombre de la clase en el primer parámetro, y poner “vbnullstring” en el segundo, en caso de buscarla por nombre, pues colocan el nombre en el segundo parámetro y “vbnullstring” en el primero.

Si la búsqueda es exitosa entonces retorna el “hwnd” de dicha ventana, en caso de fallar la función retornara 0.

¿Para que buscar una ventana?.

Una vez, tenia que hacer un programa para un Cyber Café (centro de internet), el cliente quería que cuando la computadora estuviera inactiva, es decir, que no hubiese nadie en ella, en caso de que una persona mal intencionada se sentara y tratara de abrir el IE no pudiera, y no solo el IE sino cualquier programa de Office o los programas básicos de una computadora. Para solucionar ese problema hice un programa que se mantenía en memoria y buscaba los programas. Si una persona sin autorización habría el IE el programa mediante la función FindWindow encontraba que la aplicación se encontraba abierta y la cerraba automáticamente. Esta técnica es un poco obsoleta, pero estamos hablando de hace un año y medio. Con esto les quiero decir, que si hay casos posibles para usar esta función.

¿Es mejor usar clase o el nombre de la ventana?.

Resulta que hay ventanas cuyo titulo cambia dependiendo de su contenido, ejemplo fácil es Microsoft Word, el titulo de la ventana depende del archivo que este abierto. ¿Cómo saber si Word esta abierto?. Por el titulo de la ventana sería imposible, entonces recurrimos a la clase, inclusive cuando salen nuevas versiones, los programas suelen mantener el mismo nombre de clase, con esto pudieran hacer un programa que verificara si una aplicación esta abierta sin importar la versión que se abre. ¿Cómo sabemos el nombre de la clase?. Ya les di el truco en el proyecto anterior a este. Inclusive en el programa del proyecto anterior, debajo del list1, hay una etiqueta que muestra la clase de la aplicación seleccionada.

¿Pero, tu nos dijiste que pueden haber ventanas con el mismo nombre de la clase?.

Cierto, y en caso de que haya mas de una ventana abierta con la misma clase, la función FindWindow retorna la ventana que este mas arriba, es decir, la que este mas hacia el TOPE o TOP. Pero vamos a estar claro, las nombres de las clases no suelen ser fáciles de copiar, la probabilidades que ustedes creen una aplicación con el mismo nombre de la clase de Word, por ejemplo, sería casi imposible, y también es casi imposible encontrar 2 aplicaciones comerciales a nivel mundial que posean el mismo nombre de clase. OJO!! En este me refiero a la ventana PRINCIPAL. Es evidente que 2 aplicaciones pueden compartir ventanas hijas de la misma clase, pero la clase de la ventana principal suele ser muy difícil que se repita en software comerciales. Es posible que ustedes desarrollen clases propias para uso interno y se de el caso, pero es muy raro verlo.

En el programa lo que tienen que hacer, es poner el nombre de una ventana en el Text1 y si la encuentra muestra en el Label1 el nombre de la clase, para hacerlo sencillo, habrán la calculadora de Windows, cuyo titulo es “Calculadora” pónganlo en el text1 luego presionen el botón de buscar y el programa mostrara la clase correspondiente a esa ventana.

FindWindowEx

Api: Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

La función de arriba solo buscaba ventanas principales o “main window”, esta función hace exactamente lo mismo, pero busca ventanas hijas dentro de una ventana padre.

Como primer parámetro le pasamos el “hwnd” de la ventana padre. Como segundo parámetro le pasamos el “hwnd” de la ventana hija de donde se empezara a buscar, usualmente se le coloca 0, para decirle a la función que busque en todas las ventanas hijas existentes.

El tercer parámetro equivale al nombre de la clase y el cuarto parámetro equivale al nombre de la ventana, y se trabaja de la misma manera que FindWindow.

Si la búsqueda es exitosa entonces retorna el “hwnd” de dicha ventana, en caso de fallar la función retornara 0.

Con esta función si puede haber la posibilidad de buscar en ventanas que puedan tener el mismo nombre de la clase e inclusive el mismo nombre de la ventana, y al igual que Findwindow, la función retornara el “hwnd” de la ventana que esta mas arriba o hacia el TOP.

ESTILOS Y PARIENTES

Para este proyecto es necesario 2 formularios, el formulario de arranque tendrá el nombre “frmWindowsstyle” y el segundo formulario será “frmHija”, en el formulario “frmWindowsstyle”, colocaran 3 botones y una etiqueta Label. Luego le colocan este código:

Ver Codigo

Al arrancar la aplicación, verán que los dos formularios arrancan, esto sucede, por que el formulario de arranque llama al formulario “frmHija”, estos 2 formularios no tienen relación alguna, el padre de ambos formularios es el escritorio de Windows, pero ustedes dirán ¿Pero cuando cerré la aplicación los 2 formularios se cerraron?. Esto es por que en el UNLOAD del formulario de arranque, yo descargo el formulario “frmHija”.

En este proyecto trabajamos el cambio de estilos y el cambio de padre, presionen el botón “Cambiar Padre”, verán como el formulario “frmhija” es incrustado dentro del formulario de arranque, a partir de este momento el padre de “frmhija” es el formulario “frmWindowsstyle”.

Ahora presionen el botón “Cambiar Estilo”, notaran que el formulario” frmWindowsstyle”, cambia de estilo, ya que los botones del área no cliente y el menú del sistema se desaparecen. ¿Cómo lo hicimos? Veamos las funciones:

SetWindowLong

Api: Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Esta función es utilizada para cambiar los atributos de una ventana.

Como primer parámetro se le pasa el”hwnd” de la ventana cuyos atributos queremos cambiar. Como segundo parámetro pasamos una constante que identifica lo que queremos cambiar, esta puede tomar los siguientes valores:

Public Const GWL_EXSTYLE = (-20) : Con este parámetro le indicamos a la función que lo que queremos cambiar son los estilos “extras”

Public Const GWL_HINSTANCE = (-6) : Con este indicamos que cambiamos la instancia de la ventana

Public Const GWL_ID = (-12) : Le ponemos un nuevo identificador a la ventana

Public Const GWL_STYLE = (-16) : Le cambiamos los estilos a la ventana

Public Const GWL_WNDPROC = (-4) : Le cambiamos el gestor de mensaje a la ventana

Public Const GWL_USERDATA = (-21)

En nuestro proyecto usamos la constante GWL_STYLE, es decir, cambiamos el estilo de la ventana.

Como tercer parámetro pasamos el nuevo valor a cambiar. Veamos el ejemplo:

Private Sub Command1_Click()

Me.Hide ‘Escondo la ventana para evitar interferencia

r = SetWindowLong(Me.hWnd,GWL_STYLE, WS_CAPTION Or WS_MAXIMIZE) ‘ Vean que le estoy asignando 2 nuevos estilos, es decir, los viejos se eliminan y se sustituyen por los estilos coloreados en azul

SetWindowPos Me.hWnd, HWND_TOP, 10, 10, 300, 300, SWP_SHOWWINDOW ‘ repinto la ventana para visualizar los cambios.

Me.Refresh ‘ Refresco el formulario

End Sub

Si la función se ejecuta exitosamente, el valor que regresa, es el valor anterior que poseía la ventana, es decir, en nuestro ejemplo, la variable “r” tiene los estilos que tenia la ventana anteriormente, si cambiamos la instancia, “r” tendría la instancia anterior de la ventana, y así con los demás cambios.

Mas adelante vamos a ver como cambiar el gestor de mensaje.

Si la función se ejecuta exitosamente, el valor que regresa, es el valor anterior que poseía la ventana, es decir, en nuestro ejemplo, la variable “r” tiene los estilos que tenia la ventana anteriormente, si cambiamos la instancia, “r” tendría la instancia anterior de la ventana, y así con los demás cambios.

Mas adelante vamos a ver como cambiar el gestor de mensaje.

GetWindowLong

Api: Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long

Esta función aunque no es utilizada, es útil cuando queremos tomar información de la ventana. Como primer parámetro le pasamos el “hwnd” de la ventana, de la cual extraeremos dicha información, y como segundo parámetro especificamos por una constante que información queremos tomar. Estas constante son iguales a las constantes explicadas en SetWindowLong, pero esta función toma una constante mas, que es:

Public Const GWL_HWNDPARENT = (-8) : Este parámetro permite obtener el “hwnd” de la ventana padre.

Un ejemplo sería algo así:

r = GetWindowLong(Me.hWnd, GWL_STYLE)

En este caso “r” tendrá el valor de los estilos de la ventana, y el valor “r” dependerá de la constante que haya sido pasado en “nIndex”.

Noten que con GetWindowLong podemos tomar el “hwnd” de la ventana padre con la constante GWL_HWNDPARENT, pero SetWindowLong no sirve para cambiar la ventana padre, para hacer eso contamos con la siguiente función:

SetParent

Api: Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long

Función sencilla, como primer parámetro pasamos el “hwnd” que queremos que sea “hijo”, y como segundo parámetro pasamos el “hwnd” que será padre de la ventana identificada en el primer parámetro.

En nuestro proyecto usamos la función de la siguiente manera:

SetParent frmHija.hWnd, Me.hWnd

Creo que es sencillo ver lo que hace.

ChildWindowFromPointEx

Api: Declare Function ChildWindowFromPointEx Lib "user32" (ByVal hWnd As Long, ByVal xPoint As Long, ByVal yPoint As Long, ByVal un As Long) As Long

Esta función es utilizada para saber si hay una ventana hija en una ventana padre y en una coordenada especifica.

Como primer parámetro pasamos el “hwnd” de la ventana padre, los parámetros “xPoint” y “yPoint”, son las coordenadas que queremos evaluar, el cuarto parámetro “un” es como el filtro del tipo de ventanas a tomar en cuenta, este parámetro puede tomar los siguientes valores:

Public Const CWP_ALL = &H0 : Toma en cuenta todas las ventanas

Public Const CWP_SKIPDISABLED = &H2 : Toma en cuenta todas las ventanas excepto las que están desactivadas.

Public Const CWP_SKIPINVISIBLE = &H1 : Toma en cuenta todas las ventanas excepto las que están invisibles.

Public Const CWP_SKIPTRANSPARENT = &H4 : Toma en cuenta todas las ventanas excepto las que están transparente.

En caso de encontrar alguna ventana hija en dicha coordenada, la función retorna el “hwnd” de dicha ventana.

¿Qué significa esto?. Veamos el ejemplo:

hwndresultado = ChildWindowFromPointEx(Me.hWnd, 100 \ 15, 140 \ 15, CWP_ALL)

Intencionalmente coloque las coordenadas donde esta el botón “command1”, es por esa razón que el label1 muestra "Estas en el botón 1", si yo pusiera la siguiente línea:

hwndresultado = ChildWindowFromPointEx(Me.hWnd, 100 \ 15, 140 \ 15, CWP_SKIPINVISIBLE)

Y ponen el botón “command1” invisible, es decir, la propiedad “visible” en false, el programa mostrara "Estas en el formulario", ya que el programa esta saltando todas las ventanas invisibles, si lo dejan como esta y vuelven a colocar CWP_ALL, el programa mostrara de nuevo "Estas en el botón 1", sin importar que el botón este visible o no.

SUBCLASSING

Un día una persona en el foro de VB en la web del programador, puso una pregunta, donde preguntaba si existía la posibilidad de que cuando el mouse se moviera por encima de un control ComboBox, este desplegara el listado que él contiene.

Me sorprendí por la pregunta ya que parecía muy sencillo, hasta que me di cuenta que el control ComboBox no poseía el evento MouseMove como los demás controles, pero la persona en su pregunta daba como IMPOSIBLE, encontrar dicha solución.

En ese momento, le escribí diciéndole, antes que nada, “que en la programación no hay nada imposible pero si difícil”, y luego le mostré el código que les presento a continuación.

Lo único que necesitan es un ComboBox en el formulario y un modulo.

En el modulo ponen este codigo:

Ver Codigo

Si arrancan el programa, sorprendentemente cuando pasen el mouse por encima del control este habilita o muestra el listado que contiene. Lo que la persona considero como IMPOSIBLE si hizo realidad. Y espero que haya aprendido la lección.

Lo que hicimos fue cambiar nuestro “oído” o gestor de mensajes, en este caso el “oído” del control ComboBox, a este truquito se le llama SubClassing, es decir, el cambiar el gestor de mensaje de un “hwnd” cualquiera se le denomina con este nombre. Recuerden que algunos de los proyecto del capitulo sobre GDI usamos Subclassing pero no lo mencione directamente. Inclusive las API que intervenían en dicho proceso no las explique, ya que este es el capitulo que trata de ellas.

En este proyecto solo hay una función nueva que explicar. Pero vamos analizar el codigo que usamos:

ViejoGestor = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf GestorMensaje)

Ya vimos que hace la función SetWindowLong, en este caso usamos la constante GWL_WNDPROC para indicar que vamos a cambiar el gestor de mensaje. Pasando como tercer parámetro la ubicación en la memoria de la nueva función que gestionara nuestros mensajes. La variable ViejoGestor contiene la dirección de memoria de la función original que VB le pone el control ComboBox.

En el nuevo gestor de mensajes llamado “GestorMensaje”, capturamos el evento WM_MOUSEMOVE y si la variable “mensaje” es igual a ese evento ejecutamos esta línea:

SendMessage hwnd, CB_SHOWDROPDOWN, True, 0

Lo que hacemos aquí es enviarle un mensaje a nuestro comboBox indicándole que abra o despliegue su listado, este mensaje, fue visto en el Capitulo II. Luego, como no vamos a capturar todos los mensajes del comboBox, sino nada mas el WM_MOUSEMOVE, usamos esta función:

GestorMensaje = CallWindowProc(ViejoGestor, hwnd, mensaje, wParam, lParam)

Aquí lo que estamos haciendo es llevando el mensaje al gestor de mensajes que originalmente tenia nuestro ComboBox.

CallWindowProc

Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Ya sabemos lo que hace la función, llama o envía un mensaje a un gestor de mensajes que se encuentre en memoria, este gestor es pasado en el primer parámetro “lpPrevWndFunc” de la función, los otros parámetro son conocidos por ustedes.

“HABLANDO” ENTRE VENTANAS

En este proyecto vamos a ver una función que nos permite crear nuestros propios mensajes, que pueden ser utilizados para comunicar 2 aplicaciones. NOTA: estoy hablando de una comunicación local y no! en red.

Para este proyecto se necesitan un formulario, un modulo y un boton adentro del formulario.

En el formulario colocan este codigo:

Ver Codigo

Luego generan un archivo .exe, es decir, compilan este codigo. Pero necesitamos otro .exe, pero al codigo de arriba le van a cambiar lo siguiente:

Me.Caption = "Formulario 1" = Me.Caption = "Formulario 2"

MsgBox "Se he recibido un mensaje en formulario 1" = MsgBox "Se he recibido un mensaje en formulario 2"

SendMessage FindWindow(vbNullString, "Formulario 2"), MS_PERSONAL, 0, 0 = SendMessage FindWindow(vbNullString, "Formulario 1"), MS_PERSONAL, 0, 0

Es decir, las lineas del codigo que estan en negro, las cambian por la que estan en azul, y luego generan otro archivo .exe, con diferente nombre.

Cuando tengan los 2 archivos .exe creados, procedan a ejecutarlos, cuando presionen clic en el boton veran que le envian un mensaje a la otra aplicación, esto lo pueden ver, por el contenido del “msgbox”. ¿Cómo hicimos eso?, algunos prodrian decir, que dicho efecto lo hicimos con la calculadora en uno de los codigos de arriba ¿Cuál es la diferencia?. La diferencia esta, es que estamos comunicando 2 aplicaciónes con un mensaje privado o personalizado, en los ejemplos de arriba, usabamos mensajes, que son globales para todas las ventanas, pero en ocasiones puede ser importante comunicar 2 aplicaciones para que interactuan entre si.

RegisterWindowMessage

Api: Declare Function RegisterWindowMessage Lib "user32" Alias "RegisterWindowMessageA" (ByVal lpString As String) As Long

Esta función se encarga de registrar un mensaje, como primer parametro se le pasa una cadena de caracteres, si logra registrar dicho mensaje la función retorna el ID del mensaje. Si otra aplicación trata de registrar un mensaje ya registrado, este no se registra de nuevo, lo que hace la función es retornar el ID del mensaje.

En nuestro proyecto lo usamos de esta manera:

MS_PERSONAL = RegisterWindowMessage("MS_PERSONAL")

Aqui registro un nuevo mensaje, y almaceno dicho ID en la variable MS_PERSONAL, luego uso dicho ID en el gestor de mensaje, al igual como hemos tratado todos los mensajes. Creo que es sencillo ver lo que hace.

ABRIENDO VENTANAS

Para este proyecto necesitamos un modulo .bas, y dos formularios los cuales tendrán estos nombres: frmDraw y frmDraw1, en el primero colocaran un botón y en las propiedades del formulario “StartUpPosition” coloquen “CenterScreen”, luego copian este código:

Ver Codigo

Luego arrancan el programa y presionan el botón, ¿Ven el efecto?. No lo ven!!, cierren el formulario y dale al botón de nuevo, todavía no ves el efecto, colócale comentario a la línea:

DrawAnimatedRects Me.hWnd, IDANI_CAPTION, recf1s, recf1d

Arranca y dale al botón, veras que antes el formulario hacia un efecto animado de abajo hacia arriaba, y ahora tan sencillamente abre el formulario sin tal efecto, si no lo ves todavía quita el comentario y ejecútalo, ¡fui fastidioso! ya que yo tuve que arrancar varias veces el programa para ver el efecto.

Las API’S a utilizar son sencilla y las describo a continuación:

SetRect

Api: Declare Function SetRect Lib "User32" (lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long

Esta API es simple lo único que hace es llenar una variable de tipo RECT. Donde X1 es el equivalente a Left, Y1 a Top, X2 a Right, y Y2 a Bottom.

DrawAnimatedRects

Api: Declare Function DrawAnimatedRects Lib "User32" (ByVal hWnd As Long, ByVal idAni As Long, lprcFrom As RECT, lprcTo As RECT) As Long

Esta función es utilizada para realizar el efecto de desplazamiento desde un lugar de origen hasta un lugar destino.

Como primer parámetro le pasamos el “hwdn” de la ventana a la cual queremos aplicar dicho efecto. El segundo parámetro especifica el tipo de animación, en la documentación oficial, solo veo una sola constante que es IDANI_CAPTION , como tercer parámetro le pasamos una variable de tipo RECT que determina el rectángulo de donde partirá la ventana, y como último parámetro pasamos otra variable de tipo RECT que determina el rectángulo destino.

En nuestro código tenemos:

SetRect recf1s, swidth, sheight, swidth, sheight ‘ Esta es la esquina inferior derecha de la pantalla

SetRect recf1d, 100, 300, 200, 200

DrawAnimatedRects Me.hWnd, IDANI_CAPTION, recf1s, recf1d

Es decir, animamos desde la esquina inferior derecha de la pantalla, hasta el punto 100,300, y la damos al formulario una dimensión de 200x200.

OTRAS FUNCIONES

CloseWindow

Api: Declare Function CloseWindow Lib "user32" Alias "CloseWindow" (ByVal hwnd As Long) As Long

Esta función se encarga de minimizar la ventana, especificada en el parámetro “hwnd”, OJO! Minimiza, mas no destruye.

EnableWindow

Api: Declare Function EnableWindow Lib "user32" Alias "EnableWindow" (ByVal hwnd As Long, ByVal fEnable As Long) As Long

Función encargada de habilitar o deshabilitar a la ventana, para que reciba los mensajes de mouse y teclado. Como primer parámetro se le pasa “hwnd” de la ventana, y como segundo parámetro se le pasa TRUE o FALSE, en caso de pasarle TRUE implica que la ventana esta activada, por lo tanto recibe los entradas de mouse y teclado, en caso contrario se le pasaría FALSE para no permitir dichas entradas.

FlashWindowEx

Api: Declare Function FlashWindowEx Lib "user32" (pfwi As FLASHWINFO) As Boolean

Esta función hace que el caption de la ventana se ponga intermitente, es parecido cuando estamos en una ventana, y otra aplicación requiere de atención del usuario, esta empieza a titilar, avisando que necesita atención, esa efecto de titilar se logra con esta función.

Como parámetro tiene una estructura de esta forma:

Private Type FLASHWINFO

cbSize As Long

hwnd As Long

dwFlags As Long

uCount As Long

dwTimeout As Long

End Type

CbSize = es el tamaño de la estructura, para ello apliquen la función Len() de VB.

Hwnd = es el hwnd de la ventana a la cual queremos aplicar el Flash.

DwFlags = especifica el status de dicho flash, puede tomar los siguientes valores:

Public Const FLASHW_STOP = 0 : Si el flash se encuentra activado, al usar este parámetro se desactiva.

Public Const FLASHW_CAPTION = &H1 : Con este parámetro el caption de la ventana empieza a titilar (flash).

Public Const FLASHW_TRAY = &H2 : Con este parámetro titila el botón ubicado en el “TaskBar Button”, es decir, el botón de la ventana que esta en la barra de herramientas donde esta el botón “inicio”

Public Const FLASHW_ALL = (FLASHW_CAPTION Or FLASHW_TRAY) : Es la combinacion de los 2 ya mencionados.

Public Const FLASHW_TIMER = &H4 : El flash o titilar, se mantinen continuo hasta que se especifique FLASHW_STOP

Public Const FLASHW_TIMERNOFG = &HC : El flash es continuo hasta seleccionar la ventana.

UCount = especifica el número de veces que titilara la ventana.

DwTimeout = especifica el rango, en milisegundos, en el cual la ventana empezara a titilar. Puede ser 0, en este caso el sistema usa un rango por defecto.

IsChild

Api: Declare Function IsChild Lib "user32" Alias "IsChild" (ByVal hWndParent As Long, ByVal hwnd As Long) As Long

Función que determina si una ventana es hija de una ventana padre, como primer parámetro pasamos el “hwnd” de la ventana padre a evaluar, y como segundo parámetro pasamos la supuesta ventana hija que queremos comprobar.

Si la ventana hija se encuentra en la ventana padre, la función retorna un valor distinto de 0, en caso de que la ventana no sea hija retorna el valor 0.

IsIconic

Api: Declare Function IsIconic Lib "user32" Alias "IsIconic" (ByVal hwnd As Long) As Long

Función que determina si la ventana esta minimizada, como único parámetro toma el “hwnd” que queremos evaluar. Si la función retorna distinto de 0 implica que la ventana esta minimizada, en caso contrario retorna 0.

IsWindowEnabled

Api: Declare Function IsWindowEnabled Lib "user32" Alias "IsWindowEnabled" (ByVal hwnd As Long) As Long

Función que determina si la ventana especificada en el primer parámetro, se encuentra activada “enabled” o desactivada “disabled”, a las entradas del mouse y teclado.

Si la ventana esta activada, el valor que retorna es 0, en caso contrario retornara un valor distinto de 0.

IsZoomed

Api: Declare Function IsZoomed Lib "user32" Alias "IsZoomed" (ByVal hwnd As Long) As Long

Determina si la ventana pasada en el primer parámetro, esta maximizada. En caso de ser así, la función retornara un valor distinto de 0, en caso contrario retornara 0.

LockWindowUpdate

Api: Declare Function LockWindowUpdate Lib "user32" Alias "LockWindowUpdate" (ByVal hwndLock As Long) As Long

Función que bloquea el pintado de la ventana, es decir, el mensaje WM_PAINT es bloqueado. Para bloquear dicha ventana, se le pasa su “hwnd” en el primer parámetro, para volver a activar dicha ventana se le pasa FALSE en el primer parámetro.

MoveWindow

Api: Declare Function MoveWindow Lib "user32" Alias "MoveWindow" (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long

Función que se encarga de mover la ventana pasada en el primer parámetro. Del segundo al cuarto parámetro se establece la nueva posición y el nuevo tamaño. Como sexto parámetro “bRepaint”, la pasamos TRUE si queremos que la ventana se repinta una vez movida. O FALSE en caso contrario.

En conclusión ¿Para que sirve todo lo visto en este capitulo?, específicamente ¿Para que crear una ventana de la forma indicada en C y en el primer proyecto de dicho capitulo?

Ciertamente, puede que muchos programadores no usen nada de esto en el transcurso de su carrera, pero uno nunca que sabe que problema nos depara el destino. Vamos a poner un ejemplo que me paso a mi.

Un día estaba desarrollando una aplicación en VB; después que finalizo el proyecto, le hice al cliente un instalador, ese instalador lo hice usando un software que genera dicho instaladores, por lo que la instalación no queda de parte mía, sino del software que se encarga de ello. Cuando el cliente ve la instalación, le pareció perfecto, “PERO”, siempre el usuario tiene que salir con un “pero”, el cliente quería que cuando el usuario introdujera el CD-ROM el programa detectara si estaba instalado el programa, en caso de estarlo, arrancar dicho programa, en caso contrario ejecutar la instalación.

Ahí teníamos un serio problema ¿Por qué?. Porque ¿Cómo hacia el programa? Ustedes dirán ¡PERO SI ES FACIL!, usas VB, haces un formulario que detecte el programa y LISTO!!. Y yo les digo: ¿Y como arrancan ese programa si no tienen las DLL necesarias para arrancar la aplicación?. Todos sabemos que para arrancar una aplicación mínima en VB se necesita que la maquina del usuario posea ciertos archivos DLL, pero entonces algunos dirán: Sigue siendo fácil, haces un programa que copie las DLL. ¿Y con que lenguaje hacen ese programa?. Si lo hacen en VB quedan en el mismo problema.

Para solucionar eso, tuve que hacer un pequeño programa en C similar al escrito en el Capitulo II, el programa se encargaba de detectar si la aplicación se encontraba instalada, era relativamente un programa pequeño, por lo que no me complique mucho con las API. ¿Y ese programa funciona en todas las versiones de Windows, no necesita DLL?. Por supuesto que necesita DLL, pero son DLL que ya vienen con Windows, y funcionara en todas las versiones, siempre y cuando usen funciones API’S compatibles con todas las versiones. Con ese programa solucione el problema, sabiendo que siempre iba a funcionar, sin necesidad de instalar ningún archivo extra.

<-- Capítulo IV

Regresar arriba

Regresar a la página principal

Capítulo VI -->

Desarrollado por Eduardo Roa. Copyright 2002-2003 Todos los derechos reservados