htm=articulo/subclas.htm ok articulo/subclas.htm Subclasificando ventanas con VB5.

Subclasificando ventanas con VB5.

Este artículo describe la tecnica conocida como "subclasificación de ventanas". En el ejemplo se usa VB5.


Windows y ventanas.

El elemento central de Windows es la ventana (que raro, no?), además de las ventanas que cualquier usuario de windows conoce existen muchos otros elementos que tambien son ventanas, por ejemplo los botones, list-box, cuadros de seleccion, barras de tareas, barras de estado, etc... todo son ventanas. Incluso lo que a aprimera vista puede parece una sola ventana puede estar compuesto en realidad por varias ventanas, una para el marco con el menu, otra para mostrar un documento dentro de la anterior, otra que contiene un gráfico dentro del documento, etc...

Las ventanas se crean a partir de una 'clase' de ventana. Cuando se registra una clase de ventana se le asocia a esa clase un procedimiento de ventana que es la funcion encargada de procesar los mensajes que windows envía a cualquier ventana de esa clase. Al instanciar una ventana, windows crea una estructura en la que se almacena, entre otros datos, la direccion del procedimiento de ventana para esa clase.

Cada ventana concreta (cada instancia) se identifica en windows por un handle, todas las funciones de windows que actúan sobre una ventana necesitan el handle para identificar la ventana sobre la que se quiere ejecutar la funcion. En Visual Basic, las ventanas de windows tienen la propiedad hWnd que es de solo lectura y accesible solo en tiempo de ejecución.

Mensajes y ventanas.

En windows los programas funcionan por mensajes. Todo lo que sucede en windows se transforma en mensajes, los mensajes son enviados a las ventanas y recibidos por el procedimiento de ventana.

Algunos mensajes son acciones del usuario (pulsacion de teclado, movimiento del mouse, etc...), otros mensajes son generados por windows (calcular dimensiones de la ventana, pintar la ventana, etc ...), hay mensajes que dan lugar a otros mensajes, etc.. El modo en el que el procedimiento de ventana responde a los mensajes determina la apariencia y comportamiento de esta, los procedimientos de ventana son el 'alma' de las ventanas.

Existen centenares de mensajes predefinidos en windows, las aplicaciones tambien pueden definir sus propios mensajes privados. Los mensajes de windows son transformados por Visual Basic en eventos, sin embargo muchos de los mensajes de windows no generan eventos en Visual Basic y tampoco es posible añadir eventos privados a una aplicación.

Subclasificar ventanas.

Cuando se desea crear una ventana muy parecida pero diferente a la de una clase particular, no es preciso crear una nueva clase de ventana, se puede hacer lo que se conoce como 'subclasificación de ventanas'. Esta técnica consiste en crear una ventana y cabiarle el procedimiento de ventana por otro procedimiento que procesa algunos (generalmente pocos) mensajes. La mayor parte de los mensajes de la ventana subclasificada se envían al procedimiento de ventana antiguo (el de su clase) de modo que se obtiene una ventana que se comporta de un modo ligeramente diferente al resto de las ventanas de su clase.

La subclasificacion de ventanas es una técnica habitual en C e imposible de realizar en Visual Basic -versiones 3 y 4- a causa de las carencias de este lenguaje. Sin embargo la versión 5 de Visual Basic sí que permite la subclasificación ya que en esta version se puede obtener la direccion de una funcion (puntero a funcion) usando la palabra clave AddressOff. Se recurre a manejar los punteros a funcion como si fuesen enteros de tipo Long, ya que VB no incorpora un tipo de dato 'puntero a funcion'. Hay que tener en cuenta que AddressOff solo es válido cuando opera sobre una función situada en un módulo .BAS, así que el procedimineto de ventana se creará en un módulo .BAS.

El ejemplo en VB5.

Para concentranos solo en lo fundamental (la subclasificación), vamos a plantear un ejemplo que requiera el proceso de un solo mensaje de windows. Supongamos que queremos evitar que se dispare el salvapantallas de windows cuando un formulario en particular tiene el foco de entrada.

Sabemos que windows 'pide permiso' para disparar el salvapantallas usando el mensaje WM_SYSCOMMAND con wParam = SC_SCREENSAVE (los cuatro bits bajos de wParam pueden variar => no son significativos).

Funciones necesarias.

Se usará la funcion SetWindowLong para sustituir el puntero a funcion (procedimiento de ventana) y la funcion CallWindowProc para llamar desde el nuevo procedimiento de ventana al procedimiento antiguo. Definimos algunas constantes necesarias para facilitar la legibilidad del programa. Asimismo declaramos una variable estática para guardar la direccion del procedimiento de ventana antiguo. Las declaraciones son las sigueintes:

Option Explicit
'--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
'para subclasificar la ventana
Public Const GWL_WNDPROC = (-4)
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
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
'---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
' el mensaje que nos interesa
Public Const WM_SYSCOMMAND = &H112    ' mensaje WM_SYSCOMMAND
Public Const SC_SCREENSAVE = &H140    ' submensaje(wParam) SC_SCREENSAVE
'-----------------------------------------------------------------------
'ojo, variables globales (solo una ventana)
Dim OldWinProc As Long 'procedimiento de ventana anterior (puntero a funcion)

Procedimiento de ventana.

El nuevo procedimiento de ventana debe ser obligatoriamente una funcion que devuelve un Long y tiene cuatro parámetros que tambien son Long's. En nombre de los parámetros de esta funcion es tradicionalmente el que se muestra en el ejemplo, la documentacion de los mensajes de windows tambien se refiere a estos parámetros por estos nombres.

En este ejemplo solo procesamos el mensaje WM_SYSCOMMAND, devolviendo un valor distinto de cero cuando este mensaje hace referencia al salvapantallas. El resto de los mensajes de windows y otros submensajes englobados en WM_SYSCOMMAND se redirigen al procedimiento de ventana antiguo.

Function NewWinProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  Select Case Msg
  Case WM_SYSCOMMAND
    If (wParam And &HFF0) = SC_SCREENSAVE Then
      NewWinProc = -1
      Exit Function
    End If
  End Select
  NewWinProc = CallWindowProc(OldWinProc, hwnd, Msg, wParam, lParam)
End Function

La subclasificación.

En el ejemplo se muestra cómo se subclasifica y cómo se libera la ventana, aunque normalmente  no es necesario liberar la ventana. La subclasificacion consiste simplemente en guardar la direccion del procedimiento de ventana anterior y poner un puntero al nuevo procedimiento de ventana en la estructura que guarda windows asociada a cada ventana. Esto se hace muy facilmente en un solo paso con la funcion SetWindowLong. La liberacion de la ventana se hace tambien con la misma funcion.

Sub Subclasificar(hwnd As Long)   ' subclasificar ventana (solo una)
  If OldWinProc = 0 Then
     OldWinProc = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf NewWinProc)
  End If
End Sub

Sub DesSubclasificar(hwnd As Long) ' des-subclasificar ventana
  If OldWinProc <> 0 Then
     SetWindowLong hwnd, GWL_WNDPROC, OldWinProc
     OldWinProc = 0
  End If
End Sub

Resultado.

El ejemplo realizado permite impedir que se dispare el salvapantallas cuando la ventana subclasificada es la ventana activa -tiene el foco de entrada- en windows.

El ejemplo completo se puede descargar haciendo click aquí.


© info3@maicas.net