Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieser Artikel hilft Ihnen, das Problem zu beheben, bei dem die MFC Windows-Anwendung das Steuerelement mit geänderter Größe nicht neu zeichnen kann, wenn die Fenster eine tiefe geschachtelte Hierarchie aufweisen.
Ursprüngliche Produktversion: Visual C++
Ursprüngliche KB-Nummer: 2724997
Symptome
Eine MFC-Anwendung mit einer tief verschachtelten Fensterhierarchie empfängt WM_SIZE nicht für verschachtelte Unterfenster. Daher wird die Größe von Steuerelementen nicht geändert, sobald die Schachtelungshierarchie von Fenstern eine bestimmte Tiefe auf x64 überschreitet.
Ursache
Die Ursache des Problems ist eine Entwurfsarchitektur der Anwendung, die eine tief geschachtelte Fensterhierarchie aufweist, wodurch die Anwendung eine Entwurfsbeschränkung des Kernelstapelbereichs erreicht. Im Benutzermodus kann es bei tief rekursiven Aufrufen einer Funktion zu einer Stapelüberlaufausnahme kommen. Dasselbe geschieht im Kernelmodus. Kernel muss jedoch intelligent sein und die Ausnahme behandeln und irgendwie einen Blauen Bildschirm vermeiden, während die Anwendung weiterhin ausgeführt werden kann.
Betrachten Sie eine MFC-Anwendung mit einer tiefen geschachtelten Fensterhierarchie. Berücksichtigen Sie nun, dass jemand versucht, die Größe des Fensters zu ändern, sodass auch die Größe der untergeordneten Fenster geändert werden muss. Ab jetzt werden Funktionen wie SetWindowPos API verwendet, um die Fenstergrößen zu ändern.
WM_SIZE wird an die Nachrichtenwarteschlange gesendet, und die dem Fenster zugeordnete Fensterprozedur wird aufgerufen. In MFC werden Überladungen wie CWnd::OnSize() aufgerufen. Da nun die Fenster geschachtelt sind, muss dies für alle untergeordneten Fenster rekursiv erfolgen.
Im folgenden Beispiel sehen wir CMyView, ein untergeordnetes Fenster, das den Wrapper CMyChildView1 enthält.
// 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);
}
Sie würden einen Callstack sehen, der der folgenden logischen Momentaufnahme des Callstacks ähnelt.
...[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]
Nun wechselt ein SetWindowPos Aufruf in den Kernelmodus, um Änderungen an der Position des angegebenen Fensters vorzunehmen. Es gibt dann einen Rückruf vom Kernelmodus in den Benutzermodus, um die Fensterprozedur aufzurufen, um die Nachrichten WM_WINDOWPOSCHANGING oder WM_WINDOWPOSCHANGED zu verarbeiten. Sobald die Nachrichten behandelt werden, wird der SetWindowPos Aufruf zurückgegeben.
Aufgrund der tiefen Hierarchie schränkt der Kernelmodus das Anwachsen des Stapels in gewissem Umfang ein und verfügt über eine Tiefenebenenüberprüfung. Und wenn das Limit erreicht wird, ignoriert es einfach die WM_SIZE Verarbeitung von Windows und verursacht dadurch das Problem. Dies ist kein Fehler im Windows-Betriebssystem, sondern eine Einschränkung, auf die sich die Anwendung niemals verlassen sollte.
Beschluss
Entwerfen Sie die Anwendung neu, um eine tiefe geschachtelte Hierarchie von Fenstern zu vermeiden.
Versuchen Sie, PostMessage für die WM_SIZE-Nachricht anstelle der SendMessage-API zu verwenden. Dies wird einen Entsperrungsaufruf beinhalten, der den Stapel nicht bis zum Grenzwert erhöht und somit einen etwas asynchronen Funktionsaufruf ermöglicht. Denn für SendMessage, bis die Nachricht verarbeitet wird, wird der Callstack für seine aufeinander folgenden geschachtelten Fenster wachsen.
Mehr Informationen
Dieses Problem ist nicht auf x64 Windows beschränkt, da es auch auf x86 auftreten kann. Windows würde zwar eine tiefere Fensterhierarchie erfordern, damit das Problem in x86 Windows auftritt. Warum der Unterschied? Nun, die Größe der Zeiger verdoppelte sich von 32-Bit auf 64-Bit und die Größe des Kernelmodusstapels nicht.