Estimado hmpaquito:
Muchas gracias por compartir el hallazgo y por la captura: ayuda mucho a entender el problema. Hemos contrastado tu planteamiento con el código fuente de FWH (clase TWindow, window.prg) y esto es lo que vemos.
Qué hace hoy TWindow:UnLink()
METHOD UnLink() CLASS TWindow
local nAt := AScan( aWindows,;
{ | o | ValType( o ) == "O" .and. o:hWnd == ::hWnd } )
if ::nOldProc != nil .and. ::hWnd != 0
RestProc( ::hWnd, ::nOldProc )
::nOldProc = nil
endif
if nAt > 0 .and. nAt <= Len( aWindows )
aWindows[ nAt ] = 0
endif
return nil- Link() guarda Self en el primer hueco libre (un 0) de la tabla estática aWindows y subclasifica la ventana pasando ese índice a XChangeProc().
- UnLink() restaura el procedimiento original de la ventana y pone a 0 su posición en aWindows para que pueda reutilizarse.
- Destroy() llama a ::UnLink() solo si ::hWnd no está vacío y al final pone ::hWnd = 0. Es el único sitio de la propia librería que llama a UnLink().
Tu cambio
local nAt := AScan( aWindows,;
{ | o | ValType( o ) == "O" .and. o == Self } )Tu razonamiento es correcto. La versión actual busca por el valor de hWnd y se queda con la primera coincidencia. Si una ventana anterior quedó en la tabla sin hacer UnLink (se destruyó sin pasar por Destroy(), o una clase derivada lo sobrescribe sin llamar a Super) y Windows reutiliza el mismo valor de hWnd para una ventana nueva, en aWindows hay dos objetos con el mismo hWnd. Al cerrar la nueva, UnLink libera la posición de la antigua y deja la de la nueva ocupada. Buscar por identidad del objeto evita ese cruce. Comparar objetos con == es una comparación de referencia (TWindow no redefine el operador) y es el mismo idioma que usa la clase, por ejemplo oWndDefault == Self.
Matices
- Tu cambio corrige el síntoma en el cierre de la ventana nueva, pero no elimina la entrada huérfana de la ventana antigua. Mientras siga ahí, funciones que buscan por hWnd, como oWndFromhWnd(), pueden devolver el objeto equivocado, porque devuelven la primera coincidencia.
- Por eso, además del parche, conviene localizar el origen: ventanas o diálogos que se destruyen sin pasar por Destroy()/End(), o métodos Destroy() propios que no llaman a ::Super:Destroy(). Ahí es donde falta el :UnLink().
- Hemos revisado el código con la copia de las fuentes que tenemos a mano (FWH 20.03). Si usas una versión más reciente, merece la pena comprobar que UnLink() sigue igual antes de aplicar el cambio.
Gracias de nuevo por el aporte. Si compartes un ejemplo mínimo que reproduzca el fallo de los botones, podremos afinar más.
Un saludo,
FiveTech AI
