MFC Windows Anwendung kann das Steuerelement möglicherweise nicht neu zeichnen, wenn die Größe geändert wird, wenn das Steuerelement geschachtelt multipliziert wird

Dieser Artikel hilft Ihnen bei der Behebung des Problems, bei dem MFC Windows Anwendung das Steuerelement mit geänderter Größe nicht neu zeichnen kann, wenn die Fenster über eine tief geschachtelte Hierarchie verfügen.

Originalversion des Produkts:   Visual C++
Ursprüngliche KB-Nummer:   2724997

Problembeschreibung

Eine MFC-Anwendung mit einer tief geschachtelten Fensterhierarchie kann für geschachtelte untergeordnete Fenster nicht empfangen WM_SIZE werden. Daher wird die Größe von Steuerelementen nicht geändert, sobald die Schachtelungshierarchie von Fenstern eine bestimmte Tiefe in x64 überschreitet.

Ursache

Die Ursache des Problems ist eine Entwurfsarchitektur der Anwendung mit einer tief geschachtelten Fensterhierarchie, die dazu führt, dass die Anwendung eine Designeinschränkung des Kernelstapelbereichs erreicht. Wenn im Benutzermodus tiefe rekursive Aufrufe an eine Funktion ausgeführt werden, wird eine Stack Overflow-Ausnahme ausgelöst. Dasselbe geschieht im Kernelmodus. Der Kernel muss jedoch intelligent sein und die Ausnahme behandeln und irgendwie einen blauen Bildschirm vermeiden und gleichzeitig die Ausführung der Anwendung ermöglichen.

Betrachten Sie eine MFC-Anwendung mit einer tief geschachtelten Fensterhierarchie. Denken Sie nun daran, dass jemand versucht, die Größe des Fensters zu ändern, sodass auch die Größe der untergeordneten Fenster geändert werden muss. Nun würde die Funktion wie SetWindowPos die API verwendet werden, um die Größe der Fenster 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 die Fenster nun geschachtelt sind, muss dies rekursiv für alle untergeordneten Fenster erfolgen.

Im folgenden Beispiel wird ein untergeordnetes Fenster mit Wrapper CMyChildView1angezeigtCMyView.

// 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 sehen, ein Callstack ähnlich wie unten logische Momentaufnahme des Callstacks.

...[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 des Fensters aufzurufen, um die oder WM_WINDOWPOSCHANGED die WM_WINDOWPOSCHANGING Nachrichten zu verarbeiten. Nachdem die Nachrichten verarbeitet wurden, wird der SetWindowPos Anruf zurückgegeben.

Aufgrund der tiefen Hierarchie schränkt der Kernelmodus das Anwachsen des Stapels bis zu einem gewissen Grad ein und hat eine Tiefenprüfung. Und wenn es den Grenzwert erreicht, ignoriert es einfach die WM_SIZE Verarbeitung von Fenstern und verursacht das Problem. Dies ist kein Fehler in Windows Betriebssystem, sondern eine Einschränkung, auf die sich die Anwendung nie verlassen sollte.

Lösung

Entwerfen Sie die Anwendung neu, um eine tiefe geschachtelte Hierarchie von Fenstern zu vermeiden.

Versuchen Sie, die WM_SIZE Nachricht anstelle der SendMessage API zu verwendenPostMessage. Dies wird einen Aufruf zum Aufheben der Blockierung haben, der den Stapel nicht auf den Grenzwert erhöhen würde und einen etwas asynchronen Funktionsaufruf liefert. Denn für SendMessage, bis die Nachricht verarbeitet wird, wird der Callstack für seine aufeinander folgenden geschachtelten Fenster wachsen.

Weitere Informationen

Dieses Problem ist nicht auf x64-Windows beschränkt, da es auch auf x86 auftreten kann. Windows, obwohl es eine tiefere Fensterhierarchie für das Problem in x86-Windows erforderlich wäre. Warum der Unterschied? Nun, die Größe der Zeiger hat sich von 32-Bit auf 64-Bit verdoppelt und die Größe des Kernelmodusstapels nicht.