Thema: Delphi WMContextMenu-Apokalpse

Einzelnen Beitrag anzeigen

TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#1

WMContextMenu-Apokalpse

  Alt 6. Jul 2021, 13:18
Hi Gemeinde,

ich programmiere jetzt über 10 Jahre in dieser Sprache und Umgebung, aber manche Dinge überraschen mich dann doch.

Ein Kollege bemerkte etwas Merkwürdiges:
Wenn in meinen neu geschaffenen Frame rechtsgeklickt wird, friert die Applikation ein.
Die CPU-Auslastung eines Kerns geht hoch und der VCL-Mainthread rödelt sich einen Wolf, hört dann nach einiger Zeit aber auf.
"Sch***e!", dachte ich mir, "Oberflächenprogrammierung ist hart und eigentlich nicht so mein Steckenpferd, was habe ich denn jetzt kaputt gemacht, uff..."

Das Problem ist sowohl in unseren XE5, als auch Sydney (10.4.2) Kompilaten zu finden.

Einiges hin und her mit AQtime und tiefes Debugging in den VCL-Sourcen brachte dann folgende Erkenntnis zu Tage:
Unsere Schachtelungstiefe für Komponenten scheint (zu) hoch zu sein oder der Weg, wie ein Popup-/Contextmenu in der VCL ermittelt wird, ist Mist.



Im Problemfall habe ich beispielsweise eine Verschachtelungstiefe von 18 bis 19: Mainform | Panel | Panel | Panel | Frame | Panel | Pagecontrol | TabSheet | Pagecontrol | TabSheet | ScrollBox | Frame | Panel | Panel | Frame | Frame | Frame | Panel | Panel
Das Problem lässt sich auch mit simplen geschachtelten TPanels nachvollziehen.
Siehe dazu angehängte Beispielapplikation.

RightClickTestProject2_2021-07-06.zip

Bei einem Klick auf das unterste visuelle Element in der Verschachtelung fängt die VCL an einen tierischen Aufwand zu betreiben, um ein Popupmenu zu finden.

Beispiel-Callstack:

Code:
... // geht hier weiter und weiter
Vcl.Controls.TControl.WMContextMenu((1700976, (), 1700880, -3468, 25, (), (-3468, 25), (), 5568898))
Vcl.Controls.TWinControl.WMContextMenu((123, (), 1514020, 1053, 543, (), (1053, 543), (), 0))
Vcl.Controls.TControl.WndProc((123, 1514020, 35587101, 0, 6692, 23, (), 1053, 543, (), 0, 0, ()))
Vcl.Controls.TWinControl.WndProc((123, 1514020, 35587101, 0, 6692, 23, (), 1053, 543, (), 0, 0, ()))
Vcl.Controls.TControl.Perform(???,???,35587101)
Vcl.Controls.TWinControl.DefaultHandler((no value))
Vcl.Controls.TControl.WMContextMenu((123, (), 1514020, 1053, 543, (), (1053, 543), (), 0))
Vcl.Controls.TWinControl.WMContextMenu((123, (), 1514020, 1053, 543, (), (1053, 543), (), 0))
Vcl.Controls.TControl.WndProc((123, 1514020, 35587101, 0, 6692, 23, (), 1053, 543, (), 0, 0, ()))
Vcl.Controls.TWinControl.WndProc((123, 1514020, 35587101, 0, 6692, 23, (), 1053, 543, (), 0, 0, ()))
Vcl.Controls.TWinControl.MainWndProc(???)
System.Classes.StdWndProc(1514020,123,1514020,35587101)
Meine Messungen der Durchläufe durch TControl.WMContextMenu ergab, dass pro Verschachtelungsschicht n sich folgende Formel ergibt:
WMContextMenu_Call_Count := (2^n) - 1;

Bei einem Rechtsklick auf die Form kommen wir nur einmal durch.
Bei einem Panel auf der Form dreimal.
Bei einem Panel im Panel auf der Form insgesamt 7 mal.
Panel im Panel im Panel auf der Form sind es 15 Durchläufe.

Ich vermute, die VCL sucht im aktuellen rechtsgeklickten Control nach einen Popupmenu, findet nichts und fragt den Parent und der guckt und fragt alle seine Children.
Dann wird nichts gefunden und der Aufruf weiter nach oben gereicht, wo dieser übergeordnete Parent erstmal alle Children fragt usw. usf.

Hier spielen eine Menge Aufrufe von mit dynamic; gekennzeichneten Methoden mit rein, so dass es teuer wird, die über die Dynamic Method Table (DMT -> http://hallvards.blogspot.com/2006/0...structure.html) aufzulösen.

Kennt ihr das auch und wenn ja, wie war/wäre euer Lösungsansatz für das Problem?
Einfach leere Popupmenus zwischendurch einfügen, damit nicht hoch zur Mainform nach etwas gesucht wird, was ggf. gar nicht da ist oder
die TControl.WMContextMenu geschickt irgendwo überschreiben?
Gibt vielleicht eine noch einfachere Möglichkeit (die eine Property im Objekt-Inspektor, die man immer übersieht oder so?).

Geändert von TiGü ( 6. Jul 2021 um 13:22 Uhr)
  Mit Zitat antworten Zitat