Mfc Windows aplicación puede no volver a dibujar el control cuando se cambia el tamaño si el control se multiplica anidado
Este artículo le ayuda a resolver el problema por el que MFC Windows aplicación no puede volver a dibujar el control de cambio de tamaño si las ventanas tienen una jerarquía anidada profunda.
Versión original del producto: Visual C++
Número de KB original: 2724997
Síntomas
Una aplicación MFC que tiene una jerarquía anidada profunda de ventanas no se puede recibir WM_SIZE para las ventanas secundarias anidadas. Como resultado, los controles no se cambiarán de tamaño una vez que la jerarquía de anidamiento de las ventanas supere una profundidad determinada en x64.
Causa
La causa principal del problema es una arquitectura de diseño de la aplicación que tiene una jerarquía de ventanas profundamente anidada que hace que la aplicación alcance una limitación de diseño del espacio de pila del kernel. En el modo de usuario, si se realizan llamadas recursivas profundas a una función, terminamos con una excepción de desbordamiento de pila. Lo mismo sucede en el modo kernel. Sin embargo, el kernel tiene que ser inteligente y controlar la excepción y, de alguna manera, evitar una pantalla azul y, al mismo tiempo, permitir la ejecución de la aplicación.
Considere la posibilidad de que una aplicación MFC tenga una jerarquía de windows anidada profunda. Ahora, considere la posibilidad de que alguien intente cambiar el tamaño de la ventana y, por lo tanto, también tiene que cambiar el tamaño de sus ventanas secundarias. Ahora, la función como SetWindowPos API se usaría para cambiar el tamaño de las ventanas. WM_SIZE se envía a la cola de mensajes y se llamará al procedimiento de ventana asociado a la ventana. En MFC, se llamará a sobrecargas como CWnd::OnSize() . Ahora, dado que las ventanas están anidadas, esto tiene que realizarse de forma recursiva para todas las ventanas secundarias.
En el ejemplo siguiente, vemos CMyView que tiene una ventana secundaria con el contenedor CMyChildView1.
// Resize the Child View 1
void CMyView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
m_ChildView1.MoveWindow(0, 0, cx, cy);
}
// Resize the Child View 2
void CMyChildView1::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// Resize list to fill the whole view.
m_ChildView2.MoveWindow(0, 0, cx, cy);
}
// Resize the Child View 3
void CMyChildView2::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// Resize list to fill the whole view.
m_ChildView3.MoveWindow(0, 0, cx, cy);
}
Verá una pila de llamadas similar a la siguiente instantánea lógica de callstack.
...[Snip]
CMyChildView3::OnSize()
USER32!UserCallWinProcCheckWow
USER32!DispatchClientMessage
USER32!__fnINLPWINDOWPOS
ntdll!KiUserCallbackDispatcherContinue
USER32!ZwUserSetWindowPos
CMyChildView2::OnSize()
USER32!UserCallWinProcCheckWow
USER32!DispatchClientMessage
USER32!__fnINLPWINDOWPOS
ntdll!KiUserCallbackDispatcherContinue
USER32!ZwUserSetWindowPos
CMyChildView1::OnSize()
USER32!UserCallWinProcCheckWow
USER32!DispatchClientMessage
USER32!__fnINLPWINDOWPOS
ntdll!KiUserCallbackDispatcherContinue
USER32!ZwUserSetWindowPos
CMyView::OnSize()
USER32!UserCallWinProcCheckWow
USER32!DispatchClientMessage
USER32!__fnINLPWINDOWPOS
ntdll!KiUserCallbackDispatcherContinue
USER32!ZwUserSetWindowPos
...[Snip]
Ahora, una SetWindowPos llamada pasará al modo kernel para realizar cambios en la posición de la ventana especificada. A continuación, hay una devolución de llamada del modo kernel al modo de usuario para llamar al procedimiento de ventana de la ventana para procesar los WM_WINDOWPOSCHANGING mensajes o WM_WINDOWPOSCHANGED . Una vez que se controlan los mensajes, se devuelve la SetWindowPos llamada.
Debido a la jerarquía profunda, el modo kernel restringe el crecimiento de la pila hasta cierto punto y tiene una comprobación de nivel de profundidad. Y, cuando encuentra el límite, simplemente omite el WM_SIZE procesamiento de ventanas y provoca el problema. No se trata de un error en Windows sistema operativo, sino una limitación de la que la aplicación nunca debe confiar.
Solución
Rediseñe la aplicación para evitar una jerarquía anidada profunda de ventanas.
Pruebe a usar PostMessage para el WM_SIZE mensaje en lugar de la SendMessage API. Esto tendrá una llamada de desbloqueo, que no sería necesario aumentar la pila al límite y dará una llamada de función asincrónica. Porque para SendMessage, hasta que se procese el mensaje, la pila de llamadas crecerá para sus ventanas anidadas sucesivas.
Más información
Este problema no se limita a Windows x64, ya que también puede producirse en x86. Windows, aunque se necesitaría una jerarquía de ventanas más profunda para que el problema se produjera en Windows x86. ¿Por qué la diferencia? Bueno, el tamaño de los punteros se duplicó de 32 bits a 64 bits y el tamaño de la pila de modo kernel no lo hizo.