View Issue Details

IDProjectCategoryView StatusLast Update
0033923LazarusLCLpublic2019-06-12 10:20
ReporterBrunoK Assigned ToMichl  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
PlatformAny compiled OSWindows 
Product Version1.4.4 
Summary0033923: Behavior of EnableWindow since last MS Upgrades
DescriptionThe recent upgrades of Windows 10 have changed the notification of WM_ENABLE by sending WM_ENABLE notification to HWND's even if their WS_DISABLED has not changed.

The new behaviour provokes an recursive avalanche of WM_ENABLE in/by TWindowProcHelper.DoMsgEnable that slows programs that have many embedded WinControl's. There we mean a FormCreate taking 10's of seconds instead of some 100's of milliseconds.

The problem is due to the way EnableWindow is called.
Steps To ReproduceNot blocking, but annoying, when Tools->Options... is clicked. For a nastier situation see report 0033705
Additional InformationThis problem appears suddenly when latest Windows 10 upgrades get applied. Any stable distribution above 1.4.4 will show this change when Upgrade Windows 10 1803 install itself.

Changing the Compatibility of the .EXE settings to XP mitigates the problem but is not a lasting solution.

The submitted patches change the way toggling TWinControl.Enabled are handled. New state in EnableWindow is proactively applied to children instead of responding to WM_ENABLE message.

It is, I think, compatible with any Windows > XP and in case of success should be also applied to the stable release and not only to Trunk.
TagsNo tags attached.
Fixed in Revision58448, 58497, 58511, 58912
LazTarget-
Widgetset
Attached Files

Relationships

duplicate of 0033705 resolvedMichl Program starts slowly 
related to 0034018 closedMichl LCL: Regression from r58448 & r58497 
related to 0034763 resolvedMichl Regression: not every TGrouBox child follows parent's "Enabled" property. 

Activities

BrunoK

2018-06-28 16:17

reporter  

EnableWindow_win32.patch (6,145 bytes)   
Index: win32callback.inc
===================================================================
--- win32callback.inc	(revision 58399)
+++ win32callback.inc	(working copy)
@@ -1469,7 +1469,7 @@
   end;
 end;
 
-procedure TWindowProcHelper.DoMsgEnable;
+procedure TWindowProcHelper.DoMsgEnable;     { Response to bug #0033705 }
 begin
   if WParam <> 0 Then
     LMessage.Msg := LM_SETEDITABLE;
@@ -1484,10 +1484,8 @@
       Screen.EnableForms(DisabledForms);
     end;
 
-  // disable child windows of for example groupboxes, but not of forms
-  if Assigned(lWinControl) and not (lWinControl is TCustomForm) then
-    EnableChildWindows(lWinControl, WParam<>0);
-
+  // ~bk 2018.06.28 Is it still necessary with new handling of
+  //     EnableWindow + invalidate. Someone please test.
   // ugly hack to give bitbtns a nice look
   // When no theming active, the internal image needs to be
   // recreated when the enabled state is changed
Index: win32winapi.inc
===================================================================
--- win32winapi.inc	(revision 58414)
+++ win32winapi.inc	(working copy)
@@ -1212,17 +1212,95 @@
 end;
 
 {------------------------------------------------------------------------------
-  Method:  EnableWindow
-  Params: HWnd    - handle to window
-          BEnable -  whether to enable the window
-  Returns: If the window was previously disabled
+  Class:  TControlFull
+          TWinControlFull
 
+  Subclassed TWinControl so it is possible to access to access their protected
+  properties/methods.
+ ------------------------------------------------------------------------------}
+type
+  TWinControlFull = class(TWinControl);
+
+{------------------------------------------------------------------------------
+  Method:  EnableWindow                       Response to bug #0033705
+  Params:  HWnd    - handle to window
+           BEnable -  whether to enable the window
+  Returns: If the window was previously disabled.
+
+           June 2018: To satisfy W10 new behavior, if the HWND parameter
+           effectively changes the Enabled state of its window, all its children
+           will be broadcasted the change and they will do not attempt to ignore
+           the Windows.EnableWindow for themselves.
+
   Enables or disables mouse and keyboard input to the specified window or
   control.
  ------------------------------------------------------------------------------}
-function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
+function TWin32WidgetSet.EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean;
+  { Retrieve TWinControl via its Handle}
+  function GetWinControl(aHWnd : HWND):TWinControlFull;
+  begin
+    Result := TWinControlFull(GetWin32WindowInfo(aHWnd)^.WinControl);
+  end;
+
+  { Unconditionally set the HWnd enable for all children off aWinControl and
+    invalidate TControl }
+  procedure EnableHWndChildren(aWinControl: TWinControl; aRequestedEnable : boolean);
+  var
+    i: integer;
+    ChildControl: TControl;
+    ChildWinControl: TWinControl absolute ChildControl;
+    lRequestedEnable : boolean;
+    lEnableResult : boolean;
+  begin
+    for i := 0 to aWinControl.ControlCount-1 do begin
+      ChildControl := aWinControl.Controls[i];
+      if ChildControl is TWinControl then begin
+        lRequestedEnable := ChildWinControl.Enabled and aRequestedEnable;
+        if ChildWinControl.HandleAllocated then begin
+          lEnableResult := Boolean(Windows.EnableWindow(ChildWinControl.Handle, lRequestedEnable));
+          if lEnableResult=lRequestedEnable then
+            ChildWinControl.Invalidate;
+        end;
+        EnableHWndChildren(ChildWinControl, lRequestedEnable);
+      end
+      else // Seems a necessary evil. TControl having no handle we
+           // have nothing to determine previous UI enabled state.
+        ChildControl.Invalidate;
+    end;
+  end;
+
+  { Retrieve TWinControl parent WS_DISABLED state }
+  function GetParentHWNDEnabled(aHWND : HWND):boolean;
+  var
+    lParentHWND : HWND;
+  begin
+    lParentHWND := GetParent(aHWND);
+    if lParentHWND=0 then
+      Exit(True)
+    else
+      Exit(IsWindowEnabled(lParentHWND));
+  end;
+
+var
+  lWinControl : TWinControlFull;   // The control that is represented by HWnd
+  lRequestedEnable : boolean;
 begin
-  Result := Boolean(Windows.EnableWindow(HWnd, BEnable));
+  lRequestedEnable := BEnable and GetParentHWNDEnabled(aHWnd);
+  Result := Boolean(Windows.EnableWindow(aHWnd,lRequestedEnable));
+  if (Result<>lRequestedEnable) then // No change, exit (Note inverted EnableWindow result)
+    Exit;
+  lWinControl := GetWinControl(aHWnd); // Retrieve WinControl
+  if not Assigned(lWinControl) then    // Should never happen
+    exit;
+  { Initializing the HWND enable state when it gets CreateWnd'd. -> children do
+    not yet have a handle }
+  if (wcfInitializing in lWinControl.FWinControlFlags) then
+    exit;
+  { The HWND enable state changed, completely and unquestionably update HWnd
+    Enable'd state of Children }
+  if Result=lRequestedEnable then      // As per doc, previous state is NEG
+    lWinControl.invalidate;
+  EnableHWndChildren(lWinControl, lRequestedEnable);
 end;
 
 {------------------------------------------------------------------------------
Index: win32winapih.inc
===================================================================
--- win32winapih.inc	(revision 58287)
+++ win32winapih.inc	(working copy)
@@ -69,7 +69,7 @@
 function Ellipse(DC: HDC; X1, Y1, X2, Y2: Integer): Boolean; override;
 function EmptyClipBoard: Boolean;
 function EnableScrollBar(Wnd: HWND; WSBFlags, WArrows: Cardinal): Boolean; override;
-function EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean; override;
+function EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean; override;
 function EndPaint(Handle : hwnd; var PS : TPaintStruct): Integer; override;
 function EnumDisplayMonitors(hdc: HDC; lprcClip: PRect; lpfnEnum: MonitorEnumProc; dwData: LPARAM): LongBool; override;
 function EnumFontFamilies(DC: HDC; Family:Pchar; EnumFontFamProc: FontEnumProc; LParam:Lparam):longint; override;
EnableWindow_win32.patch (6,145 bytes)   

BrunoK

2018-06-29 16:19

reporter   ~0109129

Slightly revised patch.
Test application, see buttons and check box at the bottom right of the form.

BrunoK

2018-06-29 16:20

reporter  

EnableWindow_win32_v1.patch (6,744 bytes)   
Index: win32callback.inc
===================================================================
--- win32callback.inc	(revision 58399)
+++ win32callback.inc	(working copy)
@@ -1469,7 +1469,7 @@
   end;
 end;
 
-procedure TWindowProcHelper.DoMsgEnable;
+procedure TWindowProcHelper.DoMsgEnable;     { Response to bug #0033705 }
 begin
   if WParam <> 0 Then
     LMessage.Msg := LM_SETEDITABLE;
@@ -1484,15 +1484,15 @@
       Screen.EnableForms(DisabledForms);
     end;
 
-  // disable child windows of for example groupboxes, but not of forms
-  if Assigned(lWinControl) and not (lWinControl is TCustomForm) then
-    EnableChildWindows(lWinControl, WParam<>0);
-
+  // ~bk 2018.06.28 Is it still necessary with new handling of
+  //     EnableWindow + invalidate. Someone please test.
   // ugly hack to give bitbtns a nice look
   // When no theming active, the internal image needs to be
   // recreated when the enabled state is changed
+  {
   if not ThemeServices.ThemesEnabled and (lWinControl is TCustomBitBtn) then
     DrawBitBtnImage(TCustomBitBtn(lWinControl), TCustomBitBtn(lWinControl).Caption);
+  }
 end;
 
 function TWindowProcHelper.DoMsgEraseBkgnd(var WinResult: LResult): Boolean;
Index: win32winapi.inc
===================================================================
--- win32winapi.inc	(revision 58414)
+++ win32winapi.inc	(working copy)
@@ -1212,17 +1212,101 @@
 end;
 
 {------------------------------------------------------------------------------
-  Method:  EnableWindow
-  Params: HWnd    - handle to window
-          BEnable -  whether to enable the window
-  Returns: If the window was previously disabled
+  Class:  TWinControlFull
 
+  Subclassed TWinControl so it is possible to access to access their protected
+  properties/methods.
+ ------------------------------------------------------------------------------}
+type
+  TWinControlFull = class(TWinControl);
+
+{------------------------------------------------------------------------------
+  Method:  EnableWindow                       Response to bug #0033923
+  Params:  HWnd    - handle to window
+           BEnable -  whether to enable the window
+  Returns: If the window was previously disabled.
+
   Enables or disables mouse and keyboard input to the specified window or
-  control.
+  control and updates the graphical appearance of the control
+
+ ------------------------------------------------------------------------------
+  Note June 2018:
+  In normal lazarus behavior, Enable is called by
+    1� - TWinControl.InitializeWnd (wcfInitializing in TWinControl.FWinControlFlags)
+    2� - When poperty TWinControl.Enabled changes : TWinControl.CMENABLEDCHANGED
+
+  To satisfy W10 new behavior, if the HWND parameter
+  effectively changes the Enabled state of its window, all its children
+  will be broadcasted the change and they will not attempt to ignore the
+  Windows.EnableWindow for themselves.
  ------------------------------------------------------------------------------}
-function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
+function TWin32WidgetSet.EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean;
+  { Retrieve TWinControl via its Handle}
+  function GetWinControl(aHWnd : HWND):TWinControlFull;
+  begin
+    Result := TWinControlFull(GetWin32WindowInfo(aHWnd)^.WinControl);
+  end;
+
+  { Unconditionally set the HWnd Enabled for all children of aWinControl and
+    invalidate TControl's }
+  procedure EnableHWndChildren(aWinControl: TWinControl; aRequestedEnable : boolean);
+  var
+    i: integer;
+    ChildControl: TControl;
+    ChildWinControl: TWinControl absolute ChildControl;
+    lRequestedEnable : boolean;
+    lEnableResult : boolean;
+  begin
+    for i := 0 to aWinControl.ControlCount-1 do begin
+      ChildControl := aWinControl.Controls[i];
+      if ChildControl is TWinControl then begin
+        if not ChildWinControl.HandleAllocated then // Do nothing more
+          continue;
+        lRequestedEnable := ChildWinControl.Enabled and aRequestedEnable;
+        Windows.EnableWindow(ChildWinControl.Handle, lRequestedEnable);
+        { Stop if some handler has recursively changed it
+          in its WM_ENABLE / LM_SETEDITABLE handler }
+        if lRequestedEnable<>IsWindowEnabled(ChildWinControl.Handle) then
+          Exit;
+        { Propagate to children }
+        EnableHWndChildren(ChildWinControl, lRequestedEnable);
+      end
+      else // Seems necessary. TControl having no handle we have nothing
+           // to determine previous UI enabled state.
+        ChildControl.Invalidate;
+    end;
+  end;
+
+  { Retrieve TWinControl parent WS_DISABLED state }
+  function GetParentHWNDEnabled(aHWND : HWND):boolean;
+  var
+    lParentHWND : HWND;
+  begin
+    lParentHWND := GetParent(aHWND);
+    if lParentHWND=0 then
+      Exit(True)
+    else
+      Exit(IsWindowEnabled(lParentHWND));
+  end;
+
+var
+  lWinControl : TWinControlFull;   // The control that is represented by HWnd
+  lRequestedEnable : boolean;
 begin
-  Result := Boolean(Windows.EnableWindow(HWnd, BEnable));
+  lRequestedEnable := BEnable and GetParentHWNDEnabled(aHWnd);
+  Result := Boolean(Windows.EnableWindow(aHWnd,lRequestedEnable));
+  // Result contains WS_DISABLED style. True means disabled
+  if (Result<>lRequestedEnable) then // No change, exit (Note inverted EnableWindow result)
+    Exit;
+  lWinControl := GetWinControl(aHWnd); // Retrieve WinControl
+  if not Assigned(lWinControl) then    // Should never happen
+    exit;
+  { Initializing the HWND enable state when it gets InitializeWnd'd. -> children
+    do not yet have a handle }
+  if (wcfInitializing in lWinControl.FWinControlFlags) then
+    exit;
+  { Force actualization of Children }
+  EnableHWndChildren(lWinControl, lRequestedEnable);
 end;
 
 {------------------------------------------------------------------------------
Index: win32winapih.inc
===================================================================
--- win32winapih.inc	(revision 58287)
+++ win32winapih.inc	(working copy)
@@ -69,7 +69,7 @@
 function Ellipse(DC: HDC; X1, Y1, X2, Y2: Integer): Boolean; override;
 function EmptyClipBoard: Boolean;
 function EnableScrollBar(Wnd: HWND; WSBFlags, WArrows: Cardinal): Boolean; override;
-function EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean; override;
+function EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean; override;
 function EndPaint(Handle : hwnd; var PS : TPaintStruct): Integer; override;
 function EnumDisplayMonitors(hdc: HDC; lprcClip: PRect; lpfnEnum: MonitorEnumProc; dwData: LPARAM): LongBool; override;
 function EnumFontFamilies(DC: HDC; Family:Pchar; EnumFontFamProc: FontEnumProc; LParam:Lparam):longint; override;
EnableWindow_win32_v1.patch (6,744 bytes)   

BrunoK

2018-06-29 16:21

reporter  

W10Testbds_1.7z (7,251 bytes)

BrunoK

2018-07-07 07:15

reporter  

BrunoK

2018-07-07 07:20

reporter  

win32_33705.patch (1,505 bytes)   
Index: win32proc.pp
===================================================================
--- win32proc.pp	(revision 58287)
+++ win32proc.pp	(working copy)
@@ -162,6 +162,7 @@
     wv8,
     wv8_1,
     wv10,
+    wv10ge17134,       // ~bk build number >= 17134 #bug 0033705
     wvLater
   );
 
@@ -1655,7 +1656,12 @@
     end;
     10: begin
      case Win32MinorVersion of
-       0: WindowsVersion := wv10;
+       0: begin
+            if Win32BuildNumber<17134 then   // ~bk #bug 0033705
+              WindowsVersion := wv10
+            else
+              WindowsVersion := wv10ge17134;
+       end
      else
        WindowsVersion := wvLater;
      end;
Index: win32winapi.inc
===================================================================
--- win32winapi.inc	(revision 58287)
+++ win32winapi.inc	(working copy)
@@ -1222,7 +1222,13 @@
  ------------------------------------------------------------------------------}
 function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
 begin
-  Result := Boolean(Windows.EnableWindow(HWnd, BEnable));
+  if WindowsVersion<>wv10ge17134 then
+    Result := Boolean(Windows.EnableWindow(HWnd, BEnable))
+  else // ~bk potential solution for a change in windows 10 of EnableWindow #bug 0033705
+    if IsWindowEnabled(HWnd)<>BEnable then
+      Result := Boolean(Windows.EnableWindow(HWnd, BEnable))
+    else
+      Result := False;
 end;
 
 {------------------------------------------------------------------------------
win32_33705.patch (1,505 bytes)   

BrunoK

2018-07-07 07:38

reporter   ~0109254

W10Testbds_1_Uninstrumented.7z project without need for debugserver. (uninstrumented). -> FormCreate of this simplified app takes 2 SECONDS the one patched with win32_33705.patch takes 78 milliseconds.

win32_33705.patch urgency patch as per 0033705.

Reopened because Michl does not have W10 1803

BrunoK

2018-07-07 07:42

reporter   ~0109255

Reopened because revision 58448 is totally irrelevant for this problem. Please reread analysis.

Michl

2018-07-07 10:57

developer  

test.zip (13,504 bytes)

Michl

2018-07-07 11:26

developer   ~0109260

> Reopened because Michl does not have W10 1803
That is not correct.

> Reopened because revision 58448 is totally irrelevant for this problem. Please reread analysis.

I debugged the issue. Here the problem was the fixed one. After the fix, no unneeded WM_ENABLE is sent.

I add your example from 33705 (your last added example here is not complete) with a little starting time output. Just tested, with your patch, I can't see a starting speed up on current trunk, as with your app TWin32WidgetSet.EnableWindow is now never called.

Did you try to open your app with current trunk?

wp

2018-07-07 11:39

developer   ~0109261

I can confirm that the demo attached to the other report now starts immediately.

BrunoK

2018-07-07 14:38

reporter   ~0109266

I just reverted to trunk and Michl test.zip takes 26'375 MILLISECONDS to load, yes 26 SECONDS. With my win32_33705.patch "urgency" patch it is something like 1 second.

So I don't think you are addressing the problem. I'll go go on using my patch (a more elaborate based on EnableWindow_win32_v1.patch) and see what happens when and if more people are hit.

System corei3-6100 3.70 GHz 16 GB ram 4 cores.

My version of win10 is through command line :
>ver

Microsoft Windows [version 10.0.17134.81]

Thats the latest Win 10 upgrade.

What is your result of ver from the command line ?
Are you sure you are not running with some compatibility mode on the program ?

wp

2018-07-07 15:08

developer   ~0109267

Last edited: 2018-07-07 15:28

View 5 revisions

test.zip: time to load as read from the title bar
  --> 735 ms Laz trunk (r58449)/fpc 3.0.4 - 32 bit
  --> 759 ms Laz trunk (r58449)/fpc 3.0.4 - 64 bit
but
  --> 37,687 ms Laz trunk (r58030) / fpc 3.1.1

ver --> Microsoft Windows [Version 10.0.17134.112]

Machine --> about 8 years old, Core i5-2500 3.30 GHz, 8 GByte RAM, SSD

Michl

2018-07-07 16:12

developer   ~0109271

ver --> Microsoft Windows [Version 10.0.17134.137]

@BrunoK: I use Lazarus 1.9.0 r58448 FPC 3.0.4 i386-win32-win32/win64, what is your current Lazarus/FPC version?


Can you test my app and place a breakpoint in win32winapi.inc, line 1225

function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
begin
  Result := Boolean(Windows.EnableWindow(HWnd, BEnable)); // <-- here
end;

and add the CallStack with Max 50 entries here?


Further can you place a breakpoint in win32callback.inc, line 295

procedure EnableChildWindows(WinControl: TWinControl; Enable: boolean);
var
  i: integer;
  ChildControl: TWinControl;
begin
  for i := 0 to WinControl.ControlCount-1 do // <-- here

and add the CallStack with Max 50 entries here?

BrunoK

2018-07-07 16:20

reporter   ~0109273

We will see what happens, without win32_33705.patch or my newer one, I'm stuck to the very slow FormCreate due to huge recursive loops in win32callback.inc, + DoMsgEnable.
Maybe [Version 10.0.17134.112] changed something but I couldn't upgrade to it.

Anyway if someone is interested in my strategy to NOT use a notification (WM_ENABLE) to execute changes that themselves might call WM_ENABLE, I have it ready.

Michl

2018-07-07 16:33

developer   ~0109276

Thatswhile please answer my question (menu Help -> About Lazarus -> right click on window) and please make the two little tests and post the CallStack here (set breakpoint and <Ctrl> + <Alt> + <S>, set "Max 10" to "Max 50")! Thank you!

BrunoK

2018-07-07 16:47

reporter   ~0109277

@Michl
1° Lazarus 1.9.0 rUnknown FPC 3.0.4 i386-win32-win32/win64
2° You'll have it sometimes tomorrow because I have leave my PC for the evening. I can already tell you that 100 is not enough to catch the stack depth because I have already tried it.

BrunoK

2018-07-08 16:46

reporter  

DeepestInEnableWindow.txt (18,250 bytes)   
Instrumentation of ENABLEWINDOW for recursion detection

{$IFDEF DbgDoMsgEnable} {$bk}
var
  lDbgEWDepth : integer = 0;
  lDbgEWMaxDepth : integer = 0;
{$ENDIF DbgDoMsgEnable} {$bk}

function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
begin
  {$IFDEF DbgDoMsgEnable} {$bk}
  inc(lDbgEWDepth);
  if lDbgEWDepth > lDbgEWMaxDepth then begin
    lDbgEWMaxDepth:=lDbgEWDepth;
    if lDbgEWMaxDepth>11 then
      lDbgEWMaxDepth:=lDbgEWDepth; // Stop point to catch call stack at deepest <<<<<~bk
  end;
  {$ENDIF DbgDoMsgEnable} {$bk}
  Result := Boolean(Windows.EnableWindow(HWnd, BEnable)); //<<<< What is beeing tested <<<
  {$IFDEF DbgDoMsgEnable} {$bk}
  dec(lDbgEWDepth);
  if lDbgEWDepth<1 then begin
    lDbgDepth := lDbgDepth;   // Stop point to catch max stack depth value <<<<<<<<<<~bk
    lDbgEWMaxDepth:=0;
  end;
  {$ENDIF DbgDoMsgEnable} {$bk}
end;
 

BK Note : The deepest Calltrace : 13 levels of calls in ENABLEWINDOW

#0 ENABLEWINDOW(0x26f0cf0, 2363382, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1236
#1 ENABLEWINDOW(2363382, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#2 INITIALIZEWND(0x81a37f0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#3 INITIALIZEWND(0x81a37f0) at ..\..\laz-svn-19Trunk\lcl\include\customedit.inc:43
#4 CREATEWND(0x81a37f0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#5 CREATEHANDLE(0x81a37f0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#6 HANDLENEEDED(0x81a37f0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#7 GETHANDLE(0x81a37f0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#8 ENABLECHILDWINDOWS(0x81a2450, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#9 DOMSGENABLE(0x2683288) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#10 DOWINDOWPROC(0x2683288) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#11 WINDOWPROC(1313532, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#12 USER32!AddClipboardFormatListener at :0
#13 USER32!DispatchMessageW at :0
#14 USER32!DispatchMessageW at :0
#15 USER32!SystemParametersInfoW at :0
#16 ntdll!KiUserCallbackDispatcher at :0
#17 ?? at :0
#18 ENABLEWINDOW(0x26f0cf0, 1313532, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#19 ENABLEWINDOW(1313532, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#20 INITIALIZEWND(0x81a2450) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#21 CREATEWND(0x81a2450) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#22 CREATEHANDLE(0x81a2450) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#23 HANDLENEEDED(0x81a2450) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#24 GETHANDLE(0x81a2450) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#25 ENABLECHILDWINDOWS(0x81a20b0, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#26 DOMSGENABLE(0x2683168) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#27 DOWINDOWPROC(0x2683168) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#28 WINDOWPROC(1183008, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#29 USER32!AddClipboardFormatListener at :0
#30 USER32!DispatchMessageW at :0
#31 USER32!DispatchMessageW at :0
#32 USER32!SystemParametersInfoW at :0
#33 ntdll!KiUserCallbackDispatcher at :0
#34 ?? at :0
#35 ENABLEWINDOW(0x26f0cf0, 1183008, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#36 ENABLEWINDOW(1183008, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#37 INITIALIZEWND(0x81a20b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#38 CREATEWND(0x81a20b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#39 CREATEHANDLE(0x81a20b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#40 HANDLENEEDED(0x81a20b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#41 GETHANDLE(0x81a20b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#42 ENABLECHILDWINDOWS(0x26ccf90, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#43 DOMSGENABLE(0x2683048) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#44 DOWINDOWPROC(0x2683048) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#45 WINDOWPROC(2166108, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#46 USER32!AddClipboardFormatListener at :0
#47 USER32!DispatchMessageW at :0
#48 USER32!DispatchMessageW at :0
#49 USER32!SystemParametersInfoW at :0
#50 ntdll!KiUserCallbackDispatcher at :0
#51 ?? at :0
#52 ENABLEWINDOW(0x26f0cf0, 2166108, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#53 ENABLEWINDOW(2166108, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#54 INITIALIZEWND(0x26ccf90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#55 CREATEWND(0x26ccf90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#56 CREATEHANDLE(0x26ccf90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#57 HANDLENEEDED(0x26ccf90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#58 GETHANDLE(0x26ccf90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#59 ENABLECHILDWINDOWS(0x26cc920, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#60 DOMSGENABLE(0x2682f28) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#61 DOWINDOWPROC(0x2682f28) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#62 WINDOWPROC(2166116, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#63 USER32!AddClipboardFormatListener at :0
#64 USER32!DispatchMessageW at :0
#65 USER32!DispatchMessageW at :0
#66 USER32!SystemParametersInfoW at :0
#67 ntdll!KiUserCallbackDispatcher at :0
#68 ?? at :0
#69 ENABLEWINDOW(0x26f0cf0, 2166116, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#70 ENABLEWINDOW(2166116, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#71 INITIALIZEWND(0x26cc920) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#72 CREATEWND(0x26cc920) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#73 CREATEHANDLE(0x26cc920) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#74 HANDLENEEDED(0x26cc920) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#75 GETHANDLE(0x26cc920) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#76 ENABLECHILDWINDOWS(0x26cc340, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#77 DOMSGENABLE(0x2682e08) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#78 DOWINDOWPROC(0x2682e08) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#79 WINDOWPROC(2558720, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#80 USER32!AddClipboardFormatListener at :0
#81 USER32!DispatchMessageW at :0
#82 USER32!DispatchMessageW at :0
#83 USER32!SystemParametersInfoW at :0
#84 ntdll!KiUserCallbackDispatcher at :0
#85 ?? at :0
#86 ENABLEWINDOW(0x26f0cf0, 2558720, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#87 ENABLEWINDOW(2558720, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#88 INITIALIZEWND(0x26cc340) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#89 CREATEWND(0x26cc340) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#90 CREATEWND(0x26cc340) at ..\..\laz-svn-19Trunk\lcl\include\scrollingwincontrol.inc:23
#91 CREATEHANDLE(0x26cc340) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#92 HANDLENEEDED(0x26cc340) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#93 GETHANDLE(0x26cc340) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#94 ENABLECHILDWINDOWS(0x26cbfa0, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#95 DOMSGENABLE(0x2682ce8) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#96 DOWINDOWPROC(0x2682ce8) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#97 WINDOWPROC(921452, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#98 USER32!AddClipboardFormatListener at :0
#99 USER32!DispatchMessageW at :0
#100 USER32!DispatchMessageW at :0
#101 USER32!SystemParametersInfoW at :0
#102 ntdll!KiUserCallbackDispatcher at :0
#103 ?? at :0
#104 ENABLEWINDOW(0x26f0cf0, 921452, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#105 ENABLEWINDOW(921452, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#106 INITIALIZEWND(0x26cbfa0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#107 CREATEWND(0x26cbfa0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#108 CREATEHANDLE(0x26cbfa0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#109 HANDLENEEDED(0x26cbfa0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#110 GETHANDLE(0x26cbfa0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#111 ENABLECHILDWINDOWS(0x26cbc00, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#112 DOMSGENABLE(0x2682bc8) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#113 DOWINDOWPROC(0x2682bc8) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#114 WINDOWPROC(856602, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#115 PAGEWINDOWPROC(856602, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32pagecontrol.inc:105
#116 USER32!AddClipboardFormatListener at :0
#117 USER32!DispatchMessageW at :0
#118 USER32!DispatchMessageW at :0
#119 USER32!SystemParametersInfoW at :0
#120 ntdll!KiUserCallbackDispatcher at :0
#121 ?? at :0
#122 ENABLEWINDOW(0x26f0cf0, 856602, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#123 ENABLEWINDOW(856602, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#124 INITIALIZEWND(0x26cbc00) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#125 CREATEWND(0x26cbc00) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#126 CREATEHANDLE(0x26cbc00) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#127 HANDLENEEDED(0x26cbc00) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#128 GETHANDLE(0x26cbc00) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#129 ENABLECHILDWINDOWS(0x26d7c50, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#130 DOMSGENABLE(0x2682aa8) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#131 DOWINDOWPROC(0x2682aa8) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#132 WINDOWPROC(2232674, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#133 CUSTOMTABCONTROLWNDPROC(2232674, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32pagecontrol.inc:338
#134 USER32!AddClipboardFormatListener at :0
#135 USER32!DispatchMessageW at :0
#136 USER32!DispatchMessageW at :0
#137 USER32!SystemParametersInfoW at :0
#138 ntdll!KiUserCallbackDispatcher at :0
#139 ?? at :0
#140 ENABLEWINDOW(0x26f0cf0, 2232674, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#141 ENABLEWINDOW(2232674, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#142 INITIALIZEWND(0x26d7c50) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#143 INITIALIZEWND(0x26d7c50) at ..\..\laz-svn-19Trunk\lcl\include\customnotebook.inc:340
#144 CREATEWND(0x26d7c50) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#145 CREATEWND(0x26d7c50) at ..\..\laz-svn-19Trunk\lcl\include\customnotebook.inc:303
#146 CREATEHANDLE(0x26d7c50) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#147 HANDLENEEDED(0x26d7c50) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#148 GETHANDLE(0x26d7c50) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#149 ENABLECHILDWINDOWS(0x26d5f90, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#150 DOMSGENABLE(0x2682988) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#151 DOWINDOWPROC(0x2682988) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#152 WINDOWPROC(2165588, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#153 USER32!AddClipboardFormatListener at :0
#154 USER32!DispatchMessageW at :0
#155 USER32!DispatchMessageW at :0
#156 USER32!SystemParametersInfoW at :0
#157 ntdll!KiUserCallbackDispatcher at :0
#158 ?? at :0
#159 ENABLEWINDOW(0x26f0cf0, 2165588, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#160 ENABLEWINDOW(2165588, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#161 INITIALIZEWND(0x26d5f90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#162 CREATEWND(0x26d5f90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#163 CREATEHANDLE(0x26d5f90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#164 HANDLENEEDED(0x26d5f90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#165 GETHANDLE(0x26d5f90) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#166 ENABLECHILDWINDOWS(0x26d73b0, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#167 DOMSGENABLE(0x2682868) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#168 DOWINDOWPROC(0x2682868) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#169 WINDOWPROC(2952208, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#170 USER32!AddClipboardFormatListener at :0
#171 USER32!DispatchMessageW at :0
#172 USER32!DispatchMessageW at :0
#173 USER32!SystemParametersInfoW at :0
#174 ntdll!KiUserCallbackDispatcher at :0
#175 ?? at :0
#176 ENABLEWINDOW(0x26f0cf0, 2952208, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#177 ENABLEWINDOW(2952208, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#178 INITIALIZEWND(0x26d73b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#179 CREATEWND(0x26d73b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#180 CREATEWND(0x26d73b0) at ..\..\laz-svn-19Trunk\lcl\include\scrollingwincontrol.inc:23
#181 CREATEHANDLE(0x26d73b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#182 HANDLENEEDED(0x26d73b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#183 GETHANDLE(0x26d73b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#184 ENABLECHILDWINDOWS(0x26d59b0, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#185 DOMSGENABLE(0x2682748) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#186 DOWINDOWPROC(0x2682748) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#187 WINDOWPROC(2493302, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#188 PAGEWINDOWPROC(2493302, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32pagecontrol.inc:105
#189 USER32!AddClipboardFormatListener at :0
#190 USER32!DispatchMessageW at :0
#191 USER32!DispatchMessageW at :0
#192 USER32!SystemParametersInfoW at :0
#193 ntdll!KiUserCallbackDispatcher at :0
#194 ?? at :0
#195 ENABLEWINDOW(0x26f0cf0, 2493302, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#196 ENABLEWINDOW(2493302, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#197 INITIALIZEWND(0x26d59b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#198 CREATEWND(0x26d59b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#199 CREATEHANDLE(0x26d59b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#200 HANDLENEEDED(0x26d59b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#201 GETHANDLE(0x26d59b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:6507
#202 ENABLECHILDWINDOWS(0x26d5390, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303
#203 DOMSGENABLE(0x2682628) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:1506
#204 DOWINDOWPROC(0x2682628) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2209
#205 WINDOWPROC(986496, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:2722
#206 CUSTOMTABCONTROLWNDPROC(986496, 10, -1, 0) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32pagecontrol.inc:338
#207 USER32!AddClipboardFormatListener at :0
#208 USER32!DispatchMessageW at :0
#209 USER32!DispatchMessageW at :0
#210 USER32!SystemParametersInfoW at :0
#211 ntdll!KiUserCallbackDispatcher at :0
#212 ?? at :0
#213 ENABLEWINDOW(0x26f0cf0, 986496, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32winapi.inc:1239
#214 ENABLEWINDOW(986496, true) at ..\..\laz-svn-19Trunk\lcl\include\winapi.inc:235
#215 INITIALIZEWND(0x26d5390) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7653
#216 INITIALIZEWND(0x26d5390) at ..\..\laz-svn-19Trunk\lcl\include\customnotebook.inc:340
#217 CREATEWND(0x26d5390) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7562
#218 CREATEWND(0x26d5390) at ..\..\laz-svn-19Trunk\lcl\include\customnotebook.inc:303
#219 CREATEHANDLE(0x26d5390) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#220 HANDLENEEDED(0x26d5390) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#221 CREATEWND(0x26d45b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7581
#222 CREATEWND(0x26d45b0) at ..\..\laz-svn-19Trunk\lcl\include\scrollingwincontrol.inc:23
#223 CREATEWND(0x26d45b0) at ..\..\laz-svn-19Trunk\lcl\include\customform.inc:2703
#224 CREATEWND(0x26d45b0) at ..\..\laz-svn-19Trunk\lcl\include\customform.inc:3171
#225 CREATEHANDLE(0x26d45b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7446
#226 HANDLENEEDED(0x26d45b0) at ..\..\laz-svn-19Trunk\lcl\include\wincontrol.inc:7899
#227 CREATEFORM(0x26990f0, 0x5ca688, 0) at ..\..\laz-svn-19Trunk\lcl\include\application.inc:2256
#228 main at W10testBK.lpr:18



DeepestInEnableWindow.txt (18,250 bytes)   

BrunoK

2018-07-08 16:46

reporter  

win32EnWin.patch (8,739 bytes)   
Index: win32callback.inc
===================================================================
--- win32callback.inc	(revision 58457)
+++ win32callback.inc	(working copy)
@@ -287,26 +287,6 @@
     TWin32WSWinControl.SetText(ComboBox, ComboBox.Items[Index]);
 end;
 
-procedure EnableChildWindows(WinControl: TWinControl; Enable: boolean);
-var
-  i: integer;
-  ChildControl: TWinControl;
-begin
-  for i := 0 to WinControl.ControlCount-1 do
-  begin
-    if WinControl.Controls[i] is TWinControl then
-    begin
-      ChildControl := TWinControl(WinControl.Controls[i]);
-
-      if not Enable or ChildControl.Enabled then
-      begin
-        EnableWindow(ChildControl.Handle, Enable and ChildControl.Enabled);
-        EnableChildWindows(ChildControl, Enable and ChildControl.Enabled);  // Recursive call
-      end;
-    end;
-  end;
-end;
-
 // A helper class for WindowProc to make it easier to split code into smaller pieces.
 // The original function was about 2400 lines.
 
@@ -1469,7 +1449,7 @@
   end;
 end;
 
-procedure TWindowProcHelper.DoMsgEnable;
+procedure TWindowProcHelper.DoMsgEnable;     { Response to bug #0033923 }
 begin
   if WParam <> 0 Then
     LMessage.Msg := LM_SETEDITABLE;
@@ -1484,15 +1464,15 @@
       Screen.EnableForms(DisabledForms);
     end;
 
-  // disable child windows of for example groupboxes, but not of forms
-  if Assigned(lWinControl) and not (lWinControl is TCustomForm) then
-    EnableChildWindows(lWinControl, WParam<>0);
-
+  { ~bk 2018.06.28 Is it still necessary with new handling of
+        EnableWindow + invalidate. Someone please test. }
   // ugly hack to give bitbtns a nice look
   // When no theming active, the internal image needs to be
   // recreated when the enabled state is changed
+  {
   if not ThemeServices.ThemesEnabled and (lWinControl is TCustomBitBtn) then
     DrawBitBtnImage(TCustomBitBtn(lWinControl), TCustomBitBtn(lWinControl).Caption);
+  }
 end;
 
 function TWindowProcHelper.DoMsgEraseBkgnd(var WinResult: LResult): Boolean;
@@ -2183,7 +2163,7 @@
       end;
     end;
     WM_DRAWITEM: DoMsgDrawItem;
-    WM_ENABLE:   DoMsgEnable;
+    WM_ENABLE: DoMsgEnable;
     WM_ERASEBKGND:
       if DoMsgEraseBkgnd(Result) then Exit;
     WM_EXITMENULOOP:
Index: win32winapi.inc
===================================================================
--- win32winapi.inc	(revision 58457)
+++ win32winapi.inc	(working copy)
@@ -1212,17 +1212,118 @@
 end;
 
 {------------------------------------------------------------------------------
-  Method:  EnableWindow
-  Params: HWnd    - handle to window
-          BEnable -  whether to enable the window
-  Returns: If the window was previously disabled
+  Class:  TWinControlFull
 
+  Subclassed TWinControl so it is possible to access to access their protected
+  properties/methods.
+ ------------------------------------------------------------------------------}
+type
+  TWinControlFull = class(TWinControl);
+
+{------------------------------------------------------------------------------
+  Method:  EnableWindow                       Response to bug #0033923
+  Params:  HWnd    - handle to window
+           BEnable -  whether to enable the window
+  Returns: If the window was previously disabled.
+
   Enables or disables mouse and keyboard input to the specified window or
-  control.
+  control and updates the graphical appearance of the control
+
+ ------------------------------------------------------------------------------
+  Note June 2018:
+  In normal lazarus behavior, Enable is called by
+    1� - TWinControl.InitializeWnd (wcfInitializing in TWinControl.FWinControlFlags)
+    2� - When property TWinControl.Enabled changes : TWinControl.CMENABLEDCHANGED
+
+  To satisfy W10 new behaviour, if the aHWND parameter effectively changes the
+  Enabled state of its window, except if it is a TCusomForm descendant,
+  all its direct children will be asked to update their UI state if needed and
+  recusively propagate down the Controls tree.
  ------------------------------------------------------------------------------}
-function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
+function TWin32WidgetSet.EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean;
+  { Retrieve TWinControl via its Handle}
+  function GetWinControl(aHWnd : HWND):TWinControlFull;
+  begin
+    Result := TWinControlFull(GetWin32WindowInfo(aHWnd)^.WinControl);
+  end;
+
+  { Unconditionally set the HWnd Enabled for all children of aWinControl and
+    invalidate TControl's }
+  procedure EnableHWndChildren(aWinControl: TWinControl; aRequestedEnable : boolean);
+  var
+    i: integer;
+    ChildControl: TControl;
+    ChildWinControl: TWinControl absolute ChildControl;
+    lRequestedEnable : boolean;
+    lHWNDEnabledOld : boolean;   // State prior to Windows.EnableWindow
+    lHWNDEnabledNew : boolean;
+  begin
+    for i := 0 to aWinControl.ControlCount-1 do begin
+      ChildControl := aWinControl.Controls[i];
+      if ChildControl is TWinControl then begin
+        if not ChildWinControl.HandleAllocated then  // Do nothing more
+          continue;
+        lRequestedEnable := ChildWinControl.Enabled and aRequestedEnable;
+        lHWNDEnabledOld := IsWindowEnabled(ChildWinControl.Handle);
+        Windows.EnableWindow(ChildWinControl.Handle, lRequestedEnable);
+        lHWNDEnabledNew := IsWindowEnabled(ChildWinControl.Handle);
+        { Test if propagating to children make sense }
+        if (lHWNDEnabledOld=lHWNDEnabledNew) or   // Did not change, children OK
+          (ChildWinControl.ControlCount < 1) or   // No children
+           (lHWNDEnabledNew<>lRequestedEnable)    // Not the requested enabled
+        then
+          Continue;
+        { Propagate to children }
+        EnableHWndChildren(ChildWinControl, lRequestedEnable);
+      end
+      else // Seems necessary. TControl having no handle we have nothing
+           // to determine previous UI enabled state.
+        ChildControl.Invalidate;
+    end;
+  end;
+
+  { Retrieve TWinControl parent HWND enabled state }
+  function GetParentHWNDEnabled(aHWND : HWND):boolean;
+  var
+    lParentHWND : HWND;
+  begin
+    lParentHWND := GetParent(aHWND);
+    if lParentHWND=0 then
+      Exit(True)
+    else
+      Exit(IsWindowEnabled(lParentHWND));
+  end;
+
+var
+  lWinControl : TWinControlFull;     // The control that is represented by HWnd
+  lIsForm : boolean;
+  lRequestedEnable : boolean;
 begin
-  Result := Boolean(Windows.EnableWindow(HWnd, BEnable));
+  lWinControl := GetWinControl(aHWnd);      // Retrieve WinControl top form
+  lIsForm := assigned(lWinControl) and (lWinControl.InheritsFrom(TCustomForm));
+  if lIsForm then
+    lRequestedEnable := BEnable             // ignore IsWindowEnabled(lParentHWND)
+  else
+    lRequestedEnable := BEnable and GetParentHWNDEnabled(aHWnd);
+
+{$IFDEF UseLogger}
+  if assigned(lWinControl) then
+    SendDebugFmt('TWin32WidgetSet.EnableWindow; %d; %s; %s; %s',[aHWnd,lWinControl.ClassName,lWinControl.Name,BoolToStr(lRequestedEnable)])
+  else
+    SendDebugFmt('TWin32WidgetSet.EnableWindow; %d; %s; %s; %s',[aHWnd,'','',BoolToStr(lRequestedEnable)]);
+{$ENDIF UseLogger}
+  Result := Boolean(Windows.EnableWindow(aHWnd,lRequestedEnable));
+  // Result contains WS_DISABLED style. True means disabled
+  if (Result<>lRequestedEnable) then // No change, exit (Note inverted EnableWindow result)
+    Exit;
+  if lIsForm or not Assigned(lWinControl) or (lWinControl.ControlCount<1) then
+    exit;
+  { Initializing the HWND enable state when it gets InitializeWnd'd. -> children
+    do not yet have a handle yet }
+  if (wcfInitializing in lWinControl.FWinControlFlags) then
+    exit;
+  { Force actualization of Children }
+  EnableHWndChildren(lWinControl, lRequestedEnable);
 end;
 
 {------------------------------------------------------------------------------
Index: win32winapih.inc
===================================================================
--- win32winapih.inc	(revision 58457)
+++ win32winapih.inc	(working copy)
@@ -69,7 +69,7 @@
 function Ellipse(DC: HDC; X1, Y1, X2, Y2: Integer): Boolean; override;
 function EmptyClipBoard: Boolean;
 function EnableScrollBar(Wnd: HWND; WSBFlags, WArrows: Cardinal): Boolean; override;
-function EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean; override;
+function EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean; override;
 function EndPaint(Handle : hwnd; var PS : TPaintStruct): Integer; override;
 function EnumDisplayMonitors(hdc: HDC; lprcClip: PRect; lpfnEnum: MonitorEnumProc; dwData: LPARAM): LongBool; override;
 function EnumFontFamilies(DC: HDC; Family:Pchar; EnumFontFamProc: FontEnumProc; LParam:Lparam):longint; override;
win32EnWin.patch (8,739 bytes)   

BrunoK

2018-07-08 16:48

reporter   ~0109294

Last edited: 2018-07-09 09:39

View 2 revisions

@Michl

2° Call stack and explanation of instrumenting depth detection in attachment
DeepestInEnableWindow.txt

Based on remarks of kpp in https://bugs.freepascal.org/view.php?id=33705#c109192, the revised patch I use in my current version of Trunk lazarus in :

win32EnWin.patch

--> to be tested by whose who would have the symptoms described in 0033705.

The change in logic is that the call to EnableWindow is proactive and WM_ENABLE/LM_SETEDITABLE happen as a consequence (notification change) of it and is/can be handled atomically by TWinControl descendants.

It is an error to change a HWND control state in a notification except for very valid reason.

Previous link does not work, type 'EnableWindow' in your browser search field, one of the first link should be :
EnableWindow function (Windows) - MSDN - Microsoft

Windows.EnableWindow(aHWnd,EnableState) -> 1° notify WM_CANCELMODE if needed -> 2° notify WM_ENABLE (always in W10 1803).

This gives a user application (in our case a lazarus app) the occasion to handle state changes BEFORE EnableWindow returns. Here I think, for example, preparing a new appearance of a TWinControl before a eventual custom draw or whatever.

I spend hours in rummaging through MS doc, and considering the massive upgrade they made to Office2003 for W10 1803, I suspect the new behaviour is deliberate and likely to stay.

Michl

2018-07-08 22:01

developer   ~0109295

Last edited: 2018-07-08 22:12

View 2 revisions

Sorry for my bad english. Somehow you misread my note in #c109271. Your current test doesn't enlighten me. I'm interested in the very first EnableWindow call, not in the looping calls, this is what I understand before I fixed the issue in Lazarus trunk. If you want help to find the real reason please do the steps as I tried to ask you.

You create patches and do a lot of work what neither I nor someone else will publish to Lazarus, if the reason isn't clear. Maybe we need your workaround but when I puplish it, I have to react on possible regressions. So I have to comprehend the issue.

Please do exact these steps:
- clean up your Lazarus trunk and revert changes (maybe install a second / third Lazarus trunk for testing)
- checkout Lazarus trunk with SVN to get the last version
- rebuild Lazarus (check that in Menu -> Tools -> Configure Build Lazarus -> "Clean all" is checked)
- after rebuild, check that revision of Lazarus is correct (Menu -> Help -> About Lazarus -> right click on window)
- open the project test.zip.

- first test:
- set breakpoint in win32winapi.inc, line 1225

function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
begin
  Result := Boolean(Windows.EnableWindow(HWnd, BEnable)); // <-- here

- run the test and wait for break
- open CallStack window (<Ctrl> + <Alt> + <S>) and post the result here


- second test:
- remove breakpoint from test before
- set breakpoint in win32callback.inc, line 295

procedure EnableChildWindows(WinControl: TWinControl; Enable: boolean);
var
  i: integer;
  ChildControl: TWinControl;
begin
  for i := 0 to WinControl.ControlCount-1 do // <-- here

- run the test and wait for break
- open CallStack window (<Ctrl> + <Alt> + <S>) and post the result here

Thank you very much!

PS: Your link doesn't work here.

jamie philbrook

2018-07-09 01:53

reporter   ~0109297

Last edited: 2018-07-09 01:56

View 2 revisions

Looking at the code it seems that in the TWinControl.CMEnabledChanged…

Is using "EnabledWindow" without first testing to see if the current state of
the window already matches the new state?
  
  Is it possible Windows 10 has changed the behavior of this and is now sending
WM_ENABLE without regard of the current state?
 
 This could cause a flood of messages !

 I've used in the past "IsWindowEnabled" to test for the current state..

 Should we be doing this before using the EnableWindow API call?

 If this is indeed the cause of it, Windows10 Sending WM_ENABLE senselessly
 , this should stop it..?

P.S.
It is also being used here without first testing the current state of the
window..
procedure TWinControl.InitializeWnd;

BrunoK

2018-07-09 10:02

reporter   ~0109304

Last edited: 2018-07-09 10:09

View 2 revisions

DeepestInEnableWindow.txt contains the stack on unmodified latest trunk.
There you'll see :

0000025 ENABLECHILDWINDOWS(0x81a20b0, true) at ..\..\laz-svn-19Trunk\lcl\interfaces\win32\win32callback.inc:303 and more ...

By default Windows.CreateWnd is called with no indication of WA_DISABLED thus by default "HWND.Enabled" is true after CreateWnd.

What the stack dump shows is that W10 >= 1803 sends an WM_ENABLE even if the "HWND.enabled" state hasn't changed in this case True -> True.

At the top DeepestInEnableWindow.txt is just an indication of how I TEMPORARILY 'decorated' Windows.EnableWindow to grab the deeepest stack.

The places from where Windows.EnableWindow is called (relative to patched with win32EnWin.patch) are documented there.

They are :
 In normal lazarus behavior, Enable is called by
    1° - TWinControl.InitializeWnd (wcfInitializing in TWinControl.FWinControlFlags)
    2° - When property TWinControl.Enabled changes : TWinControl.CMENABLEDCHANGED

Now I cant do anything more except take notice of concerned users of misbehaviour such as the one that kpp notified.

Edit addition : I see very well how to >>> HACK <<< ENABLECHILDWINDOWS to avoid excessive recursion and reentrancy thus emulating Windows < 10, but it would be a hack that might be punishing later on.

Michl

2018-07-09 11:42

developer   ~0109307

Why don't you test the current trunk??? You don't have it or you will see, that:

> In normal lazarus behavior, Enable is called by
> 1° - TWinControl.InitializeWnd (wcfInitializing in TWinControl.FWinControlFlags)

isn't there anymore!


If you test the lastest trunk and the issue isn't fixed for you, please do exact the steps from #c0109295 and post the call stack results! Without it, I'll not waste my time anymore, sorry.

Resolving

BrunoK

2018-07-09 15:28

reporter   ~0109317

Last edited: 2018-07-09 15:29

View 2 revisions

At revision: 58470

Now with your brilliant patch, do the following :
Take your test.zip application.
> Object inspector
>> Select TabSheet3
>>> Change Enabled to FALSE.

Save, compile, Run

Report how disabled TabSheet3 looks.

You have altered the initialization of the controls for Windows and it is no more graphically reflected. Windows still thinks its HWND is enabled.

Additionally, removing this at the >>>> TWINCONTROL.INC <<<< level, you impact all the other widget-sets that might require their enabled state (gtk2) to be correctly set during initialization.

Another test :
Add a top level clickable button on the form and put in its OnClick, for example,
TabSheet3.Enabled:=not TabSheet3.Enabled;

Click that button a few times and report how nice it looks, if it doesn't flicker as hell when you go from Enabled := False to Enabled := True, it means you do not have a W10 affected by the WM_ENABLE beeing send even if no change happened.

Bravo.

I reopen for a last time, then you can resolve and I wont reopen.

Michl

2018-07-12 11:23

developer   ~0109399

> Take your test.zip application.
> > Object inspector
> >> Select TabSheet3
> >>> Change Enabled to FALSE.

That is a valid point, thank you very much for the hint! Fixed in revision 58497.


> Additionally, removing this at the >>>> TWINCONTROL.INC <<<< level, you impact all the other widget-sets that
> might require their enabled state (gtk2) to be correctly set during initialization.

I tested this on different widgetsets and can't see any problems. If there is one, this has to be fixed in the related widgetset. Please report it!


> Another test :
> Add a top level clickable button on the form and put in its OnClick, for example,
> TabSheet3.Enabled:=not TabSheet3.Enabled;

> Click that button a few times and report how nice it looks, if it doesn't flicker as hell when you go from
> Enabled := False to Enabled := True, it means you do not have a W10 affected by the WM_ENABLE beeing send
> even if no change happened.

Thank you for that step by step description! Now I can see the problem too!

For me your first patch work fine:

 function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
 begin
   if IsWindowEnabled(HWnd) <> BEnable then
     Result := Boolean(Windows.EnableWindow(HWnd, BEnable))
   else
     Result := False;
 end;

Can you explain, why you make the big test? What controls are effected and what are the disadvantage of your first more simple patch?

BTW, IMHO: The patch is on the wrong place. EnableWindow should not be called if not necessary. So the fix has to be in TWindowProcHelper.DoMsgEnable.

BTW2: Yor commented code in TWindowProcHelper.DoMsgEnable is needed further. Simple try a empty form with a BitBtn and disabled theme. Then do BitBtn1.Enabled := not BitBtn.Enabled and see the effect.

BrunoK

2018-07-12 13:19

reporter   ~0109405

@Michl 2018-07-12 11:23
Re:
>Re: BTW, IMHO etc...
The change of order and the complicated testing results from some of the reading I did when trying to tackle the subject. I hope that approach is right.
If you reread the thread, I explain that WM_ENABLE is a notification, that TBitBtn draw in DoMsgEnable is exactly what a notification is for, punctually change something for a control. It should not generate a cascade of UI state changes that themselves start a cascade of events.

>BTW2: Yor commented code in TWindowProcHelper.DoMsgEnable is needed further. Simple try a empty form with a BitBtn and disabled theme. Then do BitBtn1.Enabled := not BitBtn.Enabled and see the effect.

I didn't know where to turn off themes, but thanks to lazarus forum search I found it. And yes those disabled lines in my last patch should stay ACTIVE there, that was why I put that
 { ~bk 2018.06.28 Is it still necessary etc ...
comment in my patch.

For the rest, I am attempting to track some other glitches that surfaced, and will reply later.

Ondrej Pokorny

2018-07-13 07:45

developer   ~0109419

Just my 2 cents. IMO "win32_33705.patch" is almost the optimal patch.

I would do:

  function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
  var
    OldEnable: Boolean;
  begin
    OldEnable := IsWindowEnabled(HWnd);
    if OldEnable<>BEnable then
      Result := Boolean(Windows.EnableWindow(HWnd, BEnable))
    else
      Result := not OldEnable;
  end;

1.) Don't use wv10ge17134 and other target-specific code. The above version of TWin32WidgetSet.EnableWindow will work for all windows versions. Having a special code for a specific version is always bug-prone and should be used only if absolutely necessary.

2.) The result of "win32_33705.patch" was wrong. See https://msdn.microsoft.com/library/windows/desktop/ms646291(v=vs.85).aspx : Return value
If the window was previously disabled, the return value is nonzero.
If the window was not previously disabled, the return value is zero.

---

@Michl: BTW, IMHO: The patch is on the wrong place. EnableWindow should not be called if not necessary. So the fix has to be in TWindowProcHelper.DoMsgEnable.

I wouldn't be so strict here. This concept (if old<>new then applynew) is used at various places: in LCL properties and in win32 code as well. It's valid.

It is definitely easier than to find all the places that call EnableWindow and make them check IsWindowEnabled :)

Michl

2018-07-14 00:10

developer   ~0109438

Thank you Ondrej for your useful hints! So fixed it in that way in revision 58511.

I'll wait for feedback from BrunoK before resolving the issue.

BrunoK

2018-07-14 13:53

reporter  

win32EnWin180714.patch (8,685 bytes)   
Index: win32callback.inc
===================================================================
--- win32callback.inc	(revision 58470)
+++ win32callback.inc	(working copy)
@@ -287,26 +287,6 @@
     TWin32WSWinControl.SetText(ComboBox, ComboBox.Items[Index]);
 end;
 
-procedure EnableChildWindows(WinControl: TWinControl; Enable: boolean);
-var
-  i: integer;
-  ChildControl: TWinControl;
-begin
-  for i := 0 to WinControl.ControlCount-1 do
-  begin
-    if WinControl.Controls[i] is TWinControl then
-    begin
-      ChildControl := TWinControl(WinControl.Controls[i]);
-
-      if not Enable or ChildControl.Enabled then
-      begin
-        EnableWindow(ChildControl.Handle, Enable and ChildControl.Enabled);
-        EnableChildWindows(ChildControl, Enable and ChildControl.Enabled);  // Recursive call
-      end;
-    end;
-  end;
-end;
-
 // A helper class for WindowProc to make it easier to split code into smaller pieces.
 // The original function was about 2400 lines.
 
@@ -1469,7 +1449,7 @@
   end;
 end;
 
-procedure TWindowProcHelper.DoMsgEnable;
+procedure TWindowProcHelper.DoMsgEnable;     { Response to bug #0033923 }
 begin
   if WParam <> 0 Then
     LMessage.Msg := LM_SETEDITABLE;
@@ -1484,13 +1464,8 @@
       Screen.EnableForms(DisabledForms);
     end;
 
-  // disable child windows of for example groupboxes, but not of forms
-  if Assigned(lWinControl) and not (lWinControl is TCustomForm) then
-    EnableChildWindows(lWinControl, WParam<>0);
-
-  // ugly hack to give bitbtns a nice look
-  // When no theming active, the internal image needs to be
-  // recreated when the enabled state is changed
+  // When themes are not enabled, it is necessary to redraw the BitMap associated
+  // with the TCustomBitBtn so Windows will reflect the new UI appearence.
   if not ThemeServices.ThemesEnabled and (lWinControl is TCustomBitBtn) then
     DrawBitBtnImage(TCustomBitBtn(lWinControl), TCustomBitBtn(lWinControl).Caption);
 end;
@@ -2183,7 +2158,7 @@
       end;
     end;
     WM_DRAWITEM: DoMsgDrawItem;
-    WM_ENABLE:   DoMsgEnable;
+    WM_ENABLE: DoMsgEnable;
     WM_ERASEBKGND:
       if DoMsgEraseBkgnd(Result) then Exit;
     WM_EXITMENULOOP:
Index: win32winapi.inc
===================================================================
--- win32winapi.inc	(revision 58470)
+++ win32winapi.inc	(working copy)
@@ -1212,17 +1212,118 @@
 end;
 
 {------------------------------------------------------------------------------
-  Method:  EnableWindow
-  Params: HWnd    - handle to window
-          BEnable -  whether to enable the window
-  Returns: If the window was previously disabled
+  Class:  TWinControlFull
 
+  Subclassed TWinControl so it is possible to access to access their protected
+  properties/methods.
+ ------------------------------------------------------------------------------}
+type
+  TWinControlFull = class(TWinControl);
+
+{------------------------------------------------------------------------------
+  Method:  EnableWindow                       Response to bug #0033923
+  Params:  HWnd    - handle to window
+           BEnable -  whether to enable the window
+  Returns: If the window was previously disabled.
+
   Enables or disables mouse and keyboard input to the specified window or
-  control.
+  control and updates the graphical appearance of the control
+
+ ------------------------------------------------------------------------------
+  Note June 2018:
+  In normal lazarus behavior, Enable is called by
+    1� - TWinControl.InitializeWnd (wcfInitializing in TWinControl.FWinControlFlags)
+    2� - When property TWinControl.Enabled changes : TWinControl.CMENABLEDCHANGED
+
+  To satisfy W10 new behaviour, if the aHWND parameter effectively changes the
+  Enabled state of its window, except if it is a TCusomForm descendant,
+  all its direct children will be asked to update their UI state if needed and
+  recusively propagate down the Controls tree.
  ------------------------------------------------------------------------------}
-function TWin32WidgetSet.EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean;
+function TWin32WidgetSet.EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean;
+  { Retrieve TWinControl via its Handle}
+  function GetWinControl(aHWnd : HWND):TWinControlFull;
+  begin
+    Result := TWinControlFull(GetWin32WindowInfo(aHWnd)^.WinControl);
+  end;
+
+  { Unconditionally set the HWnd Enabled for all children of aWinControl and
+    invalidate TControl's }
+  procedure EnableHWndChildren(aWinControl: TWinControl; aRequestedEnable : boolean);
+  var
+    i: integer;
+    ChildControl: TControl;
+    ChildWinControl: TWinControl absolute ChildControl;
+    lRequestedEnable : boolean;
+    lHWNDEnabledOld : boolean;   // State prior to Windows.EnableWindow
+    lHWNDEnabledNew : boolean;
+  begin
+    for i := 0 to aWinControl.ControlCount-1 do begin
+      ChildControl := aWinControl.Controls[i];
+      if ChildControl is TWinControl then begin
+        if not ChildWinControl.HandleAllocated then  // Do nothing more
+          continue;
+        lRequestedEnable := ChildWinControl.Enabled and aRequestedEnable;
+        lHWNDEnabledOld := IsWindowEnabled(ChildWinControl.Handle);
+        Windows.EnableWindow(ChildWinControl.Handle, lRequestedEnable);
+        lHWNDEnabledNew := IsWindowEnabled(ChildWinControl.Handle);
+        { Test if propagating to children make sense }
+        if (lHWNDEnabledOld=lHWNDEnabledNew) or   // Did not change, children OK
+          (ChildWinControl.ControlCount < 1) or   // No children
+           (lHWNDEnabledNew<>lRequestedEnable)    // Not the requested enabled
+        then
+          Continue;
+        { Propagate to children }
+        EnableHWndChildren(ChildWinControl, lRequestedEnable);
+      end
+      else // Seems necessary. TControl having no handle we have nothing
+           // to determine previous UI enabled state.
+        ChildControl.Invalidate;
+    end;
+  end;
+
+  { Retrieve TWinControl parent HWND enabled state }
+  function GetParentHWNDEnabled(aHWND : HWND):boolean;
+  var
+    lParentHWND : HWND;
+  begin
+    lParentHWND := GetParent(aHWND);
+    if lParentHWND=0 then
+      Exit(True)
+    else
+      Exit(IsWindowEnabled(lParentHWND));
+  end;
+
+var
+  lWinControl : TWinControlFull;     // The control that is represented by HWnd
+  lIsForm : boolean;
+  lRequestedEnable : boolean;
 begin
-  Result := Boolean(Windows.EnableWindow(HWnd, BEnable));
+  lWinControl := GetWinControl(aHWnd);      // Retrieve WinControl top form
+  lIsForm := assigned(lWinControl) and (lWinControl.InheritsFrom(TCustomForm));
+  if lIsForm then
+    lRequestedEnable := BEnable             // ignore IsWindowEnabled(lParentHWND)
+  else
+    lRequestedEnable := BEnable and GetParentHWNDEnabled(aHWnd);
+
+{$IFDEF UseLogger}
+  if assigned(lWinControl) then
+    SendDebugFmt('TWin32WidgetSet.EnableWindow; %d; %s; %s; %s',[aHWnd,lWinControl.ClassName,lWinControl.Name,BoolToStr(lRequestedEnable)])
+  else
+    SendDebugFmt('TWin32WidgetSet.EnableWindow; %d; %s; %s; %s',[aHWnd,'','',BoolToStr(lRequestedEnable)]);
+{$ENDIF UseLogger}
+  Result := Boolean(Windows.EnableWindow(aHWnd,lRequestedEnable));
+  // Result contains WS_DISABLED style. True means disabled
+  if (Result<>lRequestedEnable) then // No change, exit (Note inverted EnableWindow result)
+    Exit;
+  if lIsForm or not Assigned(lWinControl) or (lWinControl.ControlCount<1) then
+    exit;
+  { Initializing the HWND enable state when it gets InitializeWnd'd. -> children
+    do not yet have a handle yet }
+  if (wcfInitializing in lWinControl.FWinControlFlags) then
+    exit;
+  { Force actualization of Children }
+  EnableHWndChildren(lWinControl, lRequestedEnable);
 end;
 
 {------------------------------------------------------------------------------
Index: win32winapih.inc
===================================================================
--- win32winapih.inc	(revision 58470)
+++ win32winapih.inc	(working copy)
@@ -69,7 +69,7 @@
 function Ellipse(DC: HDC; X1, Y1, X2, Y2: Integer): Boolean; override;
 function EmptyClipBoard: Boolean;
 function EnableScrollBar(Wnd: HWND; WSBFlags, WArrows: Cardinal): Boolean; override;
-function EnableWindow(HWnd: HWND; BEnable: Boolean): Boolean; override;
+function EnableWindow(aHWnd: HWND; BEnable: Boolean): Boolean; override;
 function EndPaint(Handle : hwnd; var PS : TPaintStruct): Integer; override;
 function EnumDisplayMonitors(hdc: HDC; lprcClip: PRect; lpfnEnum: MonitorEnumProc; dwData: LPARAM): LongBool; override;
 function EnumFontFamilies(DC: HDC; Family:Pchar; EnumFontFamProc: FontEnumProc; LParam:Lparam):longint; override;
win32EnWin180714.patch (8,685 bytes)   

BrunoK

2018-07-14 13:53

reporter  

W10Testbds_1.NonInst.7z (12,005 bytes)

BrunoK

2018-07-14 13:58

reporter   ~0109449

All you are doing is just putting some plaster, I'll point where all these recent changes concerning WN_ENABLE are plaster.

58448 LCL: Win32: Program starts slowly. Issue 0033705,
removed :
  if not (csDesigning in ComponentState) then
    EnableWindow(Handle, Enabled);

Plaster 1 : just avoiding problem of recursive broadcasting in DoMsgEnable, broke at once
re : (0109317)
BrunoK (reporter)
2018-07-09 15:28
edited on: 2018-07-09 15:29

58492 LCL: Win32: Groupbox disabled painted when parent is disabled.
Plaster 2 : Since HWND is not correctly setup due to patch 58448, you have to do correct it somewhere else.

58494 LCL: Win32: Groupbox: Remove doubled code.
Plaster 3 : See plaster 2

58497 LCL: WinControl: Fixed revision 58448: Create disabled control when control is disabled in designer. Issue 0033923
Well, may be correct, but I suspect that 58497 is the source of having to do 58494 and 58492 instead of normally initializing as before 58448.
I'am not sure the contained controls will be setup correctly and doing it this way, contained TWinControl's HWND have not yet been instantiated.

You don't understand what you are doing.

My last patch takes in account remarks regarding not disabling child controls when a TForm gets disabled due to a Modal form showing, and michl remark about the TBitBtn needing redrawing on HWND.Enable change. I have removed the notion of 'ugly trick', because it is not an ugly trick and is exactly what WM_ENABLE notification is for. I would have moved it to the routine that preprocess the TCustomBitBtn, but it's OK there.

Additionally, at the begin of DoMsgEnable, 2 possible messages are created that will be propagated down the WndProc of TWinControl's
  if WParam <> 0 Then
    LMessage.Msg := LM_SETEDITABLE;

And should be replaced by
  LMessage.Msg := LM_SETEDITABLE;
  LMessage.WParam := WParam;

But its not that important since I didn't find any code using LM_SETEDITABLE and WM_ENABLE outside of function TWindowProcHelper.DoWindowProc: LResult;
 
THIS IS TRUNK, so my revision should be at least tested until proven in error.

W10Testbds_1.NonInst.7z is your test test.zip project with additional buttons and features to verify that my revision is consistent and transparent. You'll see that 58448, 58492, 58494, 58497 are naturally taken care off by it.

I have been working with lazarus with my revision for some days, and haven't yet found problems. Notify me of the problems it might cause you.

Now :
- off to understand why there is so much flicker when one selects the heavily loaded tabs.
- off to understand a strangeness re TBitBtn that seems to have surfaced during testing.

Michl

2018-07-14 22:02

developer   ~0109462

Uhh...

> 58492 LCL: Win32: Groupbox disabled painted when parent is disabled.
> Plaster 2 : Since HWND is not correctly setup due to patch 58448, you have
> to do correct it somewhere else.

This has absolutely nothing to do with revision 58448! You can see the problem in a older trunk version or 1.8.4. It's related to 0027491, see test project from 0031590.


> 58494 LCL: Win32: Groupbox: Remove doubled code.
> Plaster 3 : See plaster 2

Yes, I don't know everything and I make mistakes (this even wasn't one, just code what was already there). So if I found a better solution, I'll use it.


> You don't understand what you are doing.

Personal abuse isn't one of business.


Just some last things here from my side.

Yes, I think, I'll change back revision 58448 (I tuck in testing). Not because of you helped me (I asked you multiple times) to understand why it is currently needed, but because of tests I made, e.g. some controls (like TGroupBox) has problems on GTK2 or Qt when they are created disabled.

Your patches maybe work better then the current implementation. I don't know, as you never told me / give me a example, why is needed, so I don't see any benefit. And I wouldn't apply your patch as it is because of bad code design (hack to accessing private class fields is nothing, I would apply).

Ondrej Pokorny

2018-07-15 11:41

developer   ~0109486

> You don't understand what you are doing.

LOL, you mean Michl doesn't know what he is doing when he applies a slightly modified patch of yours? LOL again.

You fail to realize that TWin32WidgetSet.* methods are only wrappers around WinAPI calls. They should not have any code - only direct API calls.

Strictly speaking Michl is right that even the currect state (r58511) is wrong - but bearing in mind the WinAPI behavior changed across versions it can be accepted as a valid workaround. (Definitely simpler than finding all the places in LCL that have to be changed.)

Your latest patch (win32EnWin180714.patch) definitely doesn't belong into LCL-Win32.

BrunoK

2018-07-15 12:34

reporter   ~0109487

@Michl (developer) 2018-07-14 22:02
My patch win32EnWin180714.patch revision solves requirement for 58492 and 58494. The basic urgency patch for user hit by the revision is sufficient to take care 58492 and 58494, at least for Windows.
My stubborn resistance to change TControl and TWinControl is due to the fact, as you mentioned, that some widgetsets either do not know what is WS_DISABLED and probably need the call to EnableWindow in TWinControl.InitializeWnd to correctly setup their user interface state.

>hack to accessing private class
It is a hack when we do it as a developer of descendents of TWinControl.
There we are in a parent class of TWinControl so it is perfectly valid to reach the component state change indicators.

@Ondrej Pokorny 2018-07-15 11:41
>slightly modified patch of yours
No trouble there, its better to return the correct value.
>Your latest patch (win32EnWin180714.patch) definitely doesn't belong into LCL-Win32.
Where should it go, do you prefer that I transpose it to \lcl\interfaces\win32, procedure EnableChildWindows(WinControl: TWinControl; Enable: boolean);
Would that suit you better ?

Janusz Tomczak

2018-07-15 18:01

reporter   ~0109493

Component PopupNotifier not work properly after patch. Button "X" is disabled.

Michl

2018-07-15 21:13

developer   ~0109498

> Component PopupNotifier not work properly after patch. Button "X" is disabled.

A simple test works fine here with revision 58531. Also the test shipped with Lazarus is working fine here.

Can you be more specific? What do you mean with patch? Do you mean a special revision? Can you add a minimal example?

Janusz Tomczak

2018-07-16 19:57

reporter   ~0109526

Sorry. My mistake. Reinstalation of Lazarus solved the problem.

Luiz Americo

2018-07-23 02:29

developer   ~0109639

Just tested now, before and after the patches and the issue seems fixed in win32

> My stubborn resistance to change TControl and TWinControl is due to the fact, as you mentioned, that some widgetsets either do not know what is WS_DISABLED and probably need the call to EnableWindow in TWinControl.InitializeWnd to correctly setup their user interface state.

For good and bad, most of VCL/LCL design is based on win32 and many params flag are checked in widgetset code to determine the control state, so seems reasonable to do it with WS_DISABLED.

IMO the best way is to keep the patches and fix the widgetsets. Qt already has a patch. We should look at gtk2.

Michl

2018-08-22 23:24

developer   ~0110243

> IMO the best way is to keep the patches and fix the widgetsets. Qt already has a patch. We should look at gtk2.

Delphi (Delphi 10.2 Tokyo tested) also set WS_DISABLED in CreateParams, so yes we should use it.

Win32, Qt (Qt4), QT5, GTK2, GTK3 and Cocoa are fixed and tested. Now it works like before revision 58448.

Michl

2018-09-08 22:20

developer   ~0110568

I reverted 58448 and affected. On GTK2 a enabled control on a disabled control was not disabled. There was no way to disable it, as in the way, it was done before 58448.

BrunoK

2018-09-21 11:45

reporter   ~0110924

I suggest that line 304
        EnableChildWindows(ChildControl, Enable and ChildControl.Enabled); // Recursive call
of win32callback.inc should be commented out and if successfully tested, removed.

Reason : line 303 EnableWindow(ChildControl.Handle, Enable and ChildControl.Enabled);
  -> Windows already generates the WM_ENABLE notification for ChildControl thus propagating the Enable state change to the contained controls.

I didn't test but will try when I have an occasion to resynchronize with trunk.

BrunoK

2019-06-12 10:20

reporter   ~0116684

Resolved in revision 60224

Issue History

Date Modified Username Field Change
2018-06-28 16:16 BrunoK New Issue
2018-06-28 16:17 BrunoK File Added: EnableWindow_win32.patch
2018-06-28 16:25 Bart Broersma Relationship added related to 0033705
2018-06-29 07:44 Michl Assigned To => Michl
2018-06-29 07:44 Michl Status new => assigned
2018-06-29 16:19 BrunoK Note Added: 0109129
2018-06-29 16:20 BrunoK File Added: EnableWindow_win32_v1.patch
2018-06-29 16:21 BrunoK File Added: W10Testbds_1.7z
2018-07-06 22:15 Michl LazTarget => -
2018-07-06 22:15 Michl Relationship replaced duplicate of 0033705
2018-07-06 22:15 Michl Status assigned => resolved
2018-07-06 22:15 Michl Resolution open => duplicate
2018-07-07 07:15 BrunoK File Added: W10Testbds_1_Uninstrumented.7z
2018-07-07 07:20 BrunoK File Added: win32_33705.patch
2018-07-07 07:38 BrunoK Note Added: 0109254
2018-07-07 07:42 BrunoK Note Added: 0109255
2018-07-07 07:42 BrunoK Status resolved => assigned
2018-07-07 07:42 BrunoK Resolution duplicate => reopened
2018-07-07 10:57 Michl File Added: test.zip
2018-07-07 11:26 Michl Note Added: 0109260
2018-07-07 11:26 Michl Status assigned => feedback
2018-07-07 11:39 wp Note Added: 0109261
2018-07-07 14:38 BrunoK Note Added: 0109266
2018-07-07 14:38 BrunoK Status feedback => assigned
2018-07-07 15:08 wp Note Added: 0109267
2018-07-07 15:12 wp Note Edited: 0109267 View Revisions
2018-07-07 15:18 wp Note Edited: 0109267 View Revisions
2018-07-07 15:28 wp Note Edited: 0109267 View Revisions
2018-07-07 15:28 wp Note Edited: 0109267 View Revisions
2018-07-07 16:12 Michl Note Added: 0109271
2018-07-07 16:20 BrunoK Note Added: 0109273
2018-07-07 16:33 Michl Note Added: 0109276
2018-07-07 16:47 BrunoK Note Added: 0109277
2018-07-08 16:46 BrunoK File Added: DeepestInEnableWindow.txt
2018-07-08 16:46 BrunoK File Added: win32EnWin.patch
2018-07-08 16:48 BrunoK Note Added: 0109294
2018-07-08 22:01 Michl Note Added: 0109295
2018-07-08 22:12 Michl Note Edited: 0109295 View Revisions
2018-07-09 01:53 jamie philbrook Note Added: 0109297
2018-07-09 01:56 jamie philbrook Note Edited: 0109297 View Revisions
2018-07-09 09:39 BrunoK Note Edited: 0109294 View Revisions
2018-07-09 10:02 BrunoK Note Added: 0109304
2018-07-09 10:09 BrunoK Note Edited: 0109304 View Revisions
2018-07-09 11:42 Michl Note Added: 0109307
2018-07-09 11:43 Michl Status assigned => resolved
2018-07-09 11:43 Michl Resolution reopened => fixed
2018-07-09 15:28 BrunoK Note Added: 0109317
2018-07-09 15:28 BrunoK Status resolved => assigned
2018-07-09 15:28 BrunoK Resolution fixed => reopened
2018-07-09 15:29 BrunoK Note Edited: 0109317 View Revisions
2018-07-12 11:09 Michl Fixed in Revision => 58448, 58497
2018-07-12 11:23 Michl Note Added: 0109399
2018-07-12 13:19 BrunoK Note Added: 0109405
2018-07-13 07:45 Ondrej Pokorny Note Added: 0109419
2018-07-14 00:10 Michl Note Added: 0109438
2018-07-14 00:10 Michl Fixed in Revision 58448, 58497 => 58448, 58497, 58511
2018-07-14 13:53 BrunoK File Added: win32EnWin180714.patch
2018-07-14 13:53 BrunoK File Added: W10Testbds_1.NonInst.7z
2018-07-14 13:58 BrunoK Note Added: 0109449
2018-07-14 22:02 Michl Note Added: 0109462
2018-07-15 11:41 Ondrej Pokorny Note Added: 0109486
2018-07-15 12:34 BrunoK Note Added: 0109487
2018-07-15 18:01 Janusz Tomczak Note Added: 0109493
2018-07-15 21:13 Michl Note Added: 0109498
2018-07-16 19:57 Janusz Tomczak Note Added: 0109526
2018-07-22 11:10 Zeljan Rikalo Relationship added related to 0034018
2018-07-23 02:29 Luiz Americo Note Added: 0109639
2018-08-22 23:24 Michl Note Added: 0110243
2018-09-08 22:20 Michl Note Added: 0110568
2018-09-08 22:20 Michl Fixed in Revision 58448, 58497, 58511 => 58448, 58497, 58511, 58912
2018-09-08 22:20 Michl Status assigned => resolved
2018-09-08 22:20 Michl Resolution reopened => fixed
2018-09-21 11:45 BrunoK Note Added: 0110924
2018-12-26 14:43 Michl Relationship added related to 0034763
2019-06-12 10:20 BrunoK Status resolved => closed
2019-06-12 10:20 BrunoK Note Added: 0116684