Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article vous aide à résoudre le problème où l’application Windows MFC ne peut pas redessiner le contrôle redimensionné si les fenêtres ont une hiérarchie imbriquée profonde.
Version du produit d’origine : Visual C++
Numéro de base de connaissances d’origine : 2724997
Symptômes
Une application MFC ayant une hiérarchie imbriquée profonde de fenêtres ne parvient pas à recevoir WM_SIZE
pour les fenêtres enfants imbriquées. Par conséquent, les contrôles ne sont pas redimensionnés une fois que la hiérarchie d’imbrication des fenêtres dépasse une certaine profondeur sur x64.
Cause
La cause racine du problème est une architecture de conception de l’application qui a une hiérarchie de fenêtres profondément imbriquée, ce qui entraîne l’atteinte d’une limitation de conception de l’espace de pile du noyau. En mode utilisateur, si des appels récursifs profonds à une fonction sont effectués, nous finissent par une exception de dépassement de capacité de pile. La même chose se produit en mode noyau. Toutefois, le noyau doit être intelligent et gérer l’exception et en quelque sorte éviter un écran bleu tout en permettant à l’application de s’exécuter.
Considérez une application MFC ayant une hiérarchie de fenêtres imbriquées profonde. À présent, quelqu’un essaie de redimensionner la fenêtre et donc ses fenêtres enfants doivent également être redimensionnées. Maintenant, la fonction comme SetWindowPos
l’API serait utilisée pour redimensionner les fenêtres. WM_SIZE
est envoyé à la file d’attente de messages et la procédure de fenêtre associée à la fenêtre est appelée. Dans MFC, les surcharges comme celles-ci CWnd::OnSize()
seront appelées. Maintenant, étant donné que les fenêtres sont imbriquées, cela doit être effectué de manière récursive pour toutes les fenêtres enfants.
Dans l’exemple ci-dessous, nous voyons CMyView
avoir une fenêtre enfant avec wrapper 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);
}
Vous verrez une pile d’appels similaire à l’instantané logique ci-dessous de la pile d’appels.
...[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]
À présent, un SetWindowPos
appel passe en mode noyau pour apporter des modifications à la position de la fenêtre spécifiée. Il existe ensuite un rappel du mode noyau en mode utilisateur pour appeler la procédure de fenêtre de la fenêtre pour traiter le ou WM_WINDOWPOSCHANGED
les WM_WINDOWPOSCHANGING
messages. Une fois les messages gérés, l’appel SetWindowPos
est retourné.
En raison de la hiérarchie profonde, le mode noyau limite la croissance de la pile dans une certaine mesure et a un contrôle de niveau de profondeur. Et, lorsqu’il rencontre la limite, il ignore simplement le WM_SIZE
traitement des fenêtres et provoque le problème. Ce n’est pas un bogue dans le système d’exploitation Windows, mais une limitation sur laquelle l’application ne doit jamais s’appuyer.
Résolution
Redéfinissez l’application pour éviter une hiérarchie imbriquée profonde de fenêtres.
Essayez d’utiliser PostMessage
le WM_SIZE
message au lieu de l’API SendMessage
. Cela aura un appel de déblocage, ce qui n’aurait pas besoin d’augmenter la pile à la limite et donnera un appel de fonction un peu asynchrone. Comme pour SendMessage
, jusqu’à ce que le message soit traité la pile d’appels augmente pour ses fenêtres imbriquées successives.
Plus d’informations
Ce problème n’est pas limité à Windows x64, car il peut également se produire sur x86. Windows, bien qu’il faudrait une hiérarchie de fenêtre plus approfondie pour que le problème se produise dans windows x86. Pourquoi la différence ? Eh bien, la taille des pointeurs a doublé de 32 bits à 64 bits et la taille de la pile du mode noyau n’a pas été prise en compte.