View Issue Details

IDProjectCategoryView StatusLast Update
0004846JEDI VCL00 JVCL Componentspublic2012-02-29 16:53
ReporterMoritz BeutelAssigned Toobones 
PrioritynormalSeveritycrashReproducibilitysometimes
Status resolvedResolutionfixed 
Product Version3.36 
Target VersionFixed in Version3.45 
Summary0004846: [Unit JvDockVSNetStyle] TJvDockVSNetStyle.Timer can cause stack overflow on Vista/Aero
DescriptionTJvDockVSNetStyle.Timer has a nested function the calls itself recursively:
// -----
  function PointIsOnPopup(P: TPoint; GlobalCheck: Boolean): Boolean;
  const
    GW_ENABLEDPOPUP = 6;
  var
    Control: TWinControl;
    Handle: HWND;
    Rect: TRect;
    ActivePopupWindow: Boolean;
  begin
    Control := FindVCLWindow(P);
    Result := ControlIsOnPopup(Control);
    if not Result then
    begin
      // Check whether a popup window is currently displayed (hint, popup menu)
      Handle := WindowFromPoint(P);
      ActivePopupWindow := IsPopupWindow(Handle);
      if not ActivePopupWindow and GlobalCheck then
      begin
        Handle := GetWindow(Application.Handle, GW_ENABLEDPOPUP);
        ActivePopupWindow := IsPopupWindow(Handle);
        if not ActivePopupWindow then
        begin
          Handle := GetTopWindow(GetDesktopWindow);
          ActivePopupWindow := IsPopupWindow(Handle);
        end;
      end;

      if ActivePopupWindow then
      begin
        GetWindowRect(Handle, Rect);
        // Search for a control one pixel to the left;
        Dec(Rect.Left);
        Result := PointIsOnPopup(Rect.TopLeft, False); // <--
        if not Result then
        begin
          // Search for a control one pixel to the Right;
          Inc(Rect.Right);
          Result := PointIsOnPopup(Point(Rect.Right, Rect.Top), False); // <--
        end;
      end;
    end;
  end;
// -----

I'm not entirely sure what caused this - it doesn't always reproduce, and I've never seen it on XP. On the first look, the function seems to miss a strong termination condition.

I've mostly seen this happen when a modal message box is shown. My guess is that, on Vista, WindowFromPoint(Point(WindowRect.Left - 1, WindowRect.Top)) is probing the thick frame of the window it comes from, therefore returns exactly that window, which causes PointIsOnPopup() to never terminate.

As for the solution, either the way of determining the right/left window must be adapted, or a termination condition should be included. (IMHO, the latter should happen in any case to prevent a stack overflow.)

Reference: 0004305 (http://issuetracker.delphi-jedi.org/view.php?id=4305)
TagsNo tags attached.

Relationships

related to 0004305 resolvedAHUser JvDocking JvDockVSnetStyle: Forms are autohidden while a context menu is displayed 

Activities

obones

2009-07-06 14:15

administrator   ~0015769

Please someone with vista test this

Moritz Beutel

2010-06-10 03:11

reporter   ~0017485

Suggested workaround attached as .diff file

2010-06-10 03:11

 

JvDockVSNetStyle.pas.diff (1,876 bytes)
--- E:/Eigene Dateien/RAD Studio/Projekte/repository/3rdparty/jedi-svn/jvcl/run/JvDockVSNetStyle.pas	Tue Jun 08 16:00:08 2010
+++ E:/Eigene Dateien/RAD Studio/Projekte/repository/3rdparty/Weaver/jvcl/run/JvDockVSNetStyle.pas	Mon Mar 29 12:30:30 2010
@@ -2532,11 +2532,11 @@
       LStyle := GetWindowLong(Handle, GWL_STYLE);
       Result := WS_POPUP and LSTYLE <> 0;
     end;
   end;
 
-  function PointIsOnPopup(P: TPoint; GlobalCheck: Boolean): Boolean;
+  function PointIsOnPopup(P: TPoint; GlobalCheck: Boolean; Recurse: Boolean): Boolean;
   const
     GW_ENABLEDPOPUP = 6;
   var
     Control: TWinControl;
     Handle: HWND;
@@ -2559,21 +2559,21 @@
           Handle := GetTopWindow(GetDesktopWindow);
           ActivePopupWindow := IsPopupWindow(Handle);
         end;
       end;
 
-      if ActivePopupWindow then
+      if Recurse and ActivePopupWindow then
       begin
         GetWindowRect(Handle, Rect);
         // Search for a control one pixel to the left;
         Dec(Rect.Left);
-        Result := PointIsOnPopup(Rect.TopLeft, False);
+        Result := PointIsOnPopup(Rect.TopLeft, False, False);
         if not Result then
         begin
           // Search for a control one pixel to the Right;
           Inc(Rect.Right);
-          Result := PointIsOnPopup(Point(Rect.Right, Rect.Top), False);
+          Result := PointIsOnPopup(Point(Rect.Right, Rect.Top), False, False);
         end;
       end;
     end;
   end;
 
@@ -2585,11 +2585,11 @@
   if (csDesigning in ComponentState) or not ChannelOption.MouseleaveHide or
      ((GetAsyncKeyState(VK_LBUTTON) and $8000) <> 0) then
     Exit;
 
   GetCursorPos(P);
-  if PointIsOnPopup(P, True) then
+  if PointIsOnPopup(P, True, True) then
   begin
     { Reset timer }
     FCurrentTimer := ChannelOption.HideHoldTime;
     Exit;
   end;

obones

2010-10-08 15:48

administrator   ~0017796

Thanks, this now in SVN

Issue History

Date Modified Username Field Change
2009-07-06 00:11 Moritz Beutel New Issue
2009-07-06 14:03 obones Relationship added related to 0004305
2009-07-06 14:15 obones Note Added: 0015769
2009-07-06 14:15 obones Status new => acknowledged
2010-06-10 03:11 Moritz Beutel Note Added: 0017485
2010-06-10 03:11 Moritz Beutel File Added: JvDockVSNetStyle.pas.diff
2010-10-08 15:48 obones Note Added: 0017796
2010-10-08 15:48 obones Status acknowledged => resolved
2010-10-08 15:48 obones Fixed in Version => Daily / SVN
2010-10-08 15:48 obones Resolution open => fixed
2010-10-08 15:48 obones Assigned To => obones
2012-02-29 16:53 obones Fixed in Version Daily / SVN => 3.45