WinAPI | EnumChildWindows causes SIGSEGV/SIGILL lazarus / 21-General ---------------------------------------------------------------------- Ash · 2021-03-03 17:03 Hi. First time poster, long time lurker. I'm getting a runtime error in my program by a call to EnumChildWindows. Targetting x86_64 gives me a SIGSEGV error. Targetting i386 gives me a SIGILL error. Tested with: Lazarus 2.0.12 + FPC 3.2.0 (x86_64-win64-win32/win64) and Lazarus 2.0.8 + FPC-3.0.4 (x86_64-win64-win32/win64). Tested On: Windows 10 Pro 64bit and Windows 7 Pro 64bit. Test code below produces these errors, for me, with FPC. However, it works fine when tested with Delphi 2007 and C++, C#, VB (Visual Studio 2019). Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program EnumChildWindows_test; {$mode objfpc} uses windows; function EnumProc(hWnd: HWND; lParam: LPARAM): BOOL stdcall;begin  if (hWnd <> 0) then    EnumChildWindows(hWnd, ENUMWINDOWSPROC(@EnumProc), hWnd); (* Also of note:  EnumChildWindows doesn't always call its Callback function. *)   result := true;end; begin  EnumWindows(ENUMWINDOWSPROC(@EnumProc), 0);end.  Hoping someone can point out if I'm doing it wrong or maybe perhaps a bug in FPC. Thank you. ---------------------------------------------------------------------- balazsszekely · 2021-03-03 18:02 Enumerate all top level windows: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1; {$mode objfpc}{$H+} uses  Windows; var  wList: String; function EnumWindowsProc(hWnd: HWND; {%H-}lParam: LPARAM): BOOL stdcall;var  Title: array[0..255] of Char;  ClassName: array[0..255] of Char;begin  GetWindowText(hWnd, Title, 255);  GetClassName(hWnd, ClassName, 255);  if IsWindowVisible(hWnd) then    wList := wList + String(Title) + ' - ' + String(ClassName) + sLineBreak;  Result := True;end; begin  wList := '';  EnumWindows(@EnumWindowsProc, 0);  MessageBox(0, PChar(wLIst), PChar('Top level windows("Title" - "ClassName")'), 0);end. Create a window and a button with pure api, then enumerate all top level windows on button click: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program EnumWindow; {$mode Delphi} uses windows; var  wClass: TWndClass;  hInst: HWND;  hWindow: HWND;  hButton: HWND;  Msg: TMSG;  wList: String; function EnumWindowsProc(hWnd: HWND; {%H-}lParam: LPARAM): BOOL stdcall;var  Title: array[0..255] of Char;  ClassName: array[0..255] of Char;begin  GetWindowText(hWnd, Title, 255);  GetClassName(hWnd, ClassName, 255);  if IsWindowVisible(hWnd) then    wList := wList + String(Title) + ' - ' + String(ClassName) + sLineBreak;  Result := True;end; procedure DoOnclick;begin  wList := '';  EnumWindows(@EnumWindowsProc, 0);  MessageBox(hWindow, PChar(wLIst), PChar('Top level windows("Title" - "ClassName")'), 0);end; procedure DoDestroy;begin  UnRegisterClass('My class', hInst);  ExitProcess(hInst);end; procedure DoPaint;begin  //end; function WindowProc(hWnd, Msg, wParam, lParam: DWord): Longint; stdcall;begin  Result := DefWindowProc(hWnd, Msg, wParam, lParam);  case Msg of     WM_COMMAND: if lparam = hButton then DoOnClick;     WM_DESTROY: DoDestroy;     WM_PAINT: DoPaint;  end;end; begin  hInst := GetModuleHandle(nil);  with wClass do  begin    hIcon := LoadIcon(hInst, 'MAINICON');    lpfnWndProc:= @WindowProc;    hInstance := hInst;    hbrBackground := COLOR_BTNFACE;    lpszClassName := 'My class';  end;  RegisterClass(wClass);  hWindow := CreateWindow('My class', 'My main window', WS_SYSMENU or WS_VISIBLE or WS_SIZEBOX, 10, 10, 400, 300, 0, 0, hInst, nil);  hButton := CreateWindow('Button', 'Enum windows', WS_CHILD or BS_PUSHLIKE or BS_TEXT or WS_VISIBLE, 275, 125, 125, 24, hWindow, 0, hInst, nil);  SetFocus(hButton);  while(GetMessage({%H-}Msg, hWindow, 0, 0))do  begin    TranslateMessage(Msg);    DispatchMessage(Msg);  end;end. The second example is interesting because LCL is not needed, the exe size is very small(69KB). The only issue with this approach is productivity, the lack of productivity to be more precise. ---------------------------------------------------------------------- Remy Lebeau · 2021-03-03 18:08 Quote from: GetMem on March 03, 2021, 07:02:43 pmEnumerate all top level windows: ... Create a window and a button with pure api, then enumerate all top level windows on button click: ... In no way whatsoever does this even remotely try to answer Ash's question about why calling EnumChildWindows() inside of a EnumWindows() callback is crashing. ---------------------------------------------------------------------- Remy Lebeau · 2021-03-03 18:12 Quote from: Ash on March 03, 2021, 06:03:37 pmI'm getting a runtime error in my program by a call to EnumChildWindows. Off-hand, aside from the unnecessary type-casts, I see nothing wrong with the code.  Do you have the same problem if you use separate callbacks for EnumWindows() and EnumChildWindows()? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program EnumChildWindows_test; {$mode objfpc} uses windows; function EnumChildProc(hWnd: HWND; lParam: LPARAM): BOOL stdcall;begin  EnumChildWindows(hWnd, @EnumChildProc, 0);   Result := TRUE;end; function EnumProc(hWnd: HWND; lParam: LPARAM): BOOL stdcall;begin  EnumChildWindows(hWnd, @EnumChildProc, 0);  Result := TRUE;end; begin  EnumWindows(@EnumProc, 0);end. ---------------------------------------------------------------------- marcov · 2021-03-03 18:25 With 64-bit Windows target, try FPC 3.2.0, there were quite some 64-bit fixes to the windows headers in 3.2.0 ---------------------------------------------------------------------- balazsszekely · 2021-03-03 18:43 Quote from: Remy Lebeau on March 03, 2021, 07:08:34 pmIn no way whatsoever does this even remotely try to answer Ash's question about why calling EnumChildWindows() inside of a EnumWindows() callback is crashing. Ok. I got carried away and I forget to add the EnumChildWindow part. Saying that my post has nothing to do with Ash's question is a gross exaggeration. Anyways here is the complete answer. Tested with Lazarus Trunk/FPC 3.2.0(64 bit), works fine. Regarding the unnecessary typecast, it's a bad habit from pre- FPC 3.0.0 era.  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program EnumWindow; {$mode Delphi} uses windows; var  wClass: TWndClass;  hInst: HWND;  hWindow: HWND;  hButton: HWND;  Msg: TMSG; function EnumChildWindowsProc(hWnd: HWnd; lParam: LPARAM): Bool; stdcall;var  Title: array[0..255] of Char;begin  GetWindowText(hWnd, Title, 255);  if IsWindowVisible(hWnd) and (String(Title) <> '') then    MessageBox(0, PChar(Title), PChar('Child window'), 0);  if GetWindow(hWnd, GW_CHILD) > 0 then    Enumchildwindows(hWnd, @EnumChildWindowsProc, 0);  Result := True;end; function EnumWindowsProc(hWnd: HWND; {%H-}lParam: LPARAM): BOOL stdcall;var  Title: array[0..255] of Char;begin  GetWindowText(hWnd, Title, 255);  if IsWindowVisible(hWnd) and (String(Title) <> '') then    MessageBox(0, PChar(Title), PChar('Top window'), 0);  EnumChildWindows(hWnd, @EnumChildWindowsProc, 0);  Result := True;end; procedure DoOnclick;begin  EnumWindows(@EnumWindowsProc, 0);end; procedure DoDestroy;begin  UnRegisterClass('My class', hInst);  ExitProcess(hInst);end; procedure DoPaint;begin  //end; function WindowProc(hWnd, Msg, wParam, lParam: DWord): Longint; stdcall;begin  Result := DefWindowProc(hWnd, Msg, wParam, lParam);  case Msg of     WM_COMMAND: if lparam = hButton then DoOnClick;     WM_DESTROY: DoDestroy;     WM_PAINT: DoPaint;  end;end; begin  hInst := GetModuleHandle(nil);  with wClass do  begin    hIcon := LoadIcon(hInst, 'MAINICON');    lpfnWndProc:= @WindowProc;    hInstance := hInst;    hbrBackground := COLOR_BTNFACE;    lpszClassName := 'My class';  end;  RegisterClass(wClass);  hWindow := CreateWindow('My class', 'My main window', WS_SYSMENU or WS_VISIBLE or WS_SIZEBOX, 10, 10, 400, 300, 0, 0, hInst, nil);  hButton := CreateWindow('Button', 'Enum windows', WS_CHILD or BS_PUSHLIKE or BS_TEXT or WS_VISIBLE, (400 - 125) div 2, 125, 125, 24, hWindow, 0, hInst, nil);  SetFocus(hButton);  while(GetMessage({%H-}Msg, hWindow, 0, 0))do  begin     TranslateMessage(Msg);     DispatchMessage(Msg);  end;end. ---------------------------------------------------------------------- Ash · 2021-03-03 18:58 Quote from: Remy Lebeau on March 03, 2021, 07:12:45 pmOff-hand, aside from the unnecessary type-casts, I see nothing wrong with the code.  Do you have the same problem if you use separate callbacks for EnumWindows() and EnumChildWindows()? Unless I include directive {$T+}, the compiler will give me the 'Incompatible type' error. (EDIT: Still need to use typecasts on some things even after including {$T+}.) Yes, I get the same errors. Tried it before creating the thread. By the way, does it work for you? -- Quote from: marcov on March 03, 2021, 07:25:59 pmWith 64-bit Windows target, try FPC 3.2.0, there were quite some 64-bit fixes to the windows headers in 3.2.0 I'm using the latest version of the compiler which is fpc v3.2.0. -- Just noticed that if I use {$mode delphi} instead of {$mode objfpc} i don't get the runtime errors. I guess i can live with that. The irony of it is a little funny though (for me anway) because I'm converting an old project I wrote in Delphi 7 to Free Pascal and I have to use the Delphi mode for it to work. So is something broken on my end or with mode objfpc? -- Thanks for the responses. ---------------------------------------------------------------------- Ash · 2021-03-03 19:03 Quote from: GetMem on March 03, 2021, 07:43:33 pmOk. I got carried away and I forget to add the EnumChildWindow part. Saying that my post has nothing to do with Ash's question is a gross exaggeration. Anyways here is the complete answer. Tested with Lazarus Trunk/FPC 3.2.0(64 bit), works fine. Regarding the unnecessary typecast, it's a bad habit from pre- FPC 3.0.0 era.  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program EnumWindow; {$mode Delphi} uses windows; var  wClass: TWndClass;  hInst: HWND;  hWindow: HWND;  hButton: HWND;  Msg: TMSG; function EnumChildWindowsProc(hWnd: HWnd; lParam: LPARAM): Bool; stdcall;var  Title: array[0..255] of Char;begin  GetWindowText(hWnd, Title, 255);  if IsWindowVisible(hWnd) and (String(Title) <> '') then    MessageBox(0, PChar(Title), PChar('Child window'), 0);  if GetWindow(hWnd, GW_CHILD) > 0 then    Enumchildwindows(hWnd, @EnumChildWindowsProc, 0);  Result := True;end; function EnumWindowsProc(hWnd: HWND; {%H-}lParam: LPARAM): BOOL stdcall;var  Title: array[0..255] of Char;begin  GetWindowText(hWnd, Title, 255);  if IsWindowVisible(hWnd) and (String(Title) <> '') then    MessageBox(0, PChar(Title), PChar('Top window'), 0);  EnumChildWindows(hWnd, @EnumChildWindowsProc, 0);  Result := True;end; procedure DoOnclick;begin  EnumWindows(@EnumWindowsProc, 0);end; procedure DoDestroy;begin  UnRegisterClass('My class', hInst);  ExitProcess(hInst);end; procedure DoPaint;begin  //end; function WindowProc(hWnd, Msg, wParam, lParam: DWord): Longint; stdcall;begin  Result := DefWindowProc(hWnd, Msg, wParam, lParam);  case Msg of     WM_COMMAND: if lparam = hButton then DoOnClick;     WM_DESTROY: DoDestroy;     WM_PAINT: DoPaint;  end;end; begin  hInst := GetModuleHandle(nil);  with wClass do  begin    hIcon := LoadIcon(hInst, 'MAINICON');    lpfnWndProc:= @WindowProc;    hInstance := hInst;    hbrBackground := COLOR_BTNFACE;    lpszClassName := 'My class';  end;  RegisterClass(wClass);  hWindow := CreateWindow('My class', 'My main window', WS_SYSMENU or WS_VISIBLE or WS_SIZEBOX, 10, 10, 400, 300, 0, 0, hInst, nil);  hButton := CreateWindow('Button', 'Enum windows', WS_CHILD or BS_PUSHLIKE or BS_TEXT or WS_VISIBLE, (400 - 125) div 2, 125, 125, 24, hWindow, 0, hInst, nil);  SetFocus(hButton);  while(GetMessage({%H-}Msg, hWindow, 0, 0))do  begin     TranslateMessage(Msg);     DispatchMessage(Msg);  end;end.  This code does indeed work, but only when using {$mode delphi}. It still produces runtime errors when using {$mode objfpc}. ---------------------------------------------------------------------- ASBzone · 2021-03-03 19:05 Quote from: Ash on March 03, 2021, 07:58:41 pmJust noticed that if I use {$mode delphi} instead of {$mode objfpc} i don't get the runtime errors. I guess i can live with that. The irony of it is a little funny though (for me anway) because I'm converting an old project I wrote in Delphi 7 to Free Pascal and I have to use the Delphi mode for it to work. So is something broken on my end or with mode objfpc? {$mode delphi} is the mode to use for maximum Delphi compatibility.  {$mode objfpc} behaves differently in some areas for a variety of reasons, none of which means that this is a broken mode. Sometimes a feature already exists, and making it Delphi compatible means running in a mode where it will behave differently than it would otherwise.  For instance:  https://www.freepascal.org/docs-html/prog/progse74.html ---------------------------------------------------------------------- balazsszekely · 2021-03-03 19:09 @Ash Go with {$mode delphi} as @ASBzone suggested, nothing wrong with that. ---------------------------------------------------------------------- Ash · 2021-03-03 19:20 I will be using {$mode delphi} for that particular unit in my project. I don't think this is a 'feature' issue between the two modes. Calling a WinAPI function should behave the same in either mode but causes runtime errors in the objfpc mode. Am I missing something here? I'm just curious if there's a problem on my end or is there some bug in the compiled code when using the objfpc mode. Which doesn't make sense to me anyway so i'm thinking something is broken on my end. ---------------------------------------------------------------------- ASBzone · 2021-03-03 19:49 Quote from: Ash on March 03, 2021, 08:20:38 pmI will be using {$mode delphi} for that particular unit in my project. I don't think this is a 'feature' issue between the two modes. Calling a WinAPI function should behave the same in either mode but causes runtime errors in the objfpc mode. Am I missing something here? I'm just curious if there's a problem on my end or is there some bug in the compiled code when using the objfpc mode. Which doesn't make sense to me anyway so i'm thinking something is broken on my end. There's obviously a difference in the code path that has an impact on your app. It might take a little bit of digging to determine if it is a bug in FPC, or the natural implication of that code path on some other aspect of your code. ---------------------------------------------------------------------- 440bx · 2021-03-03 19:56 Quote from: Ash on March 03, 2021, 08:20:38 pmso i'm thinking something is broken on my end. and you are right to think that something is broken on your end.  The problem resides in the fact that you are using the same callback routine for both EnumWindows and EnumChildWindows.  That causes your callback routine to be called recursively for every child window.  That API was _not_ designed to be called recursively (see MSDN.)    EnumChildWindows will list all the children, grandchildren and grandgrand...grandchildren without having to call it for every child.  The fact that it is being called recursively is conceptually incorrect and it is actually surprising that doing so works in some cases (I'm taking your word for it that it worked with Delphi using the code you posted.) Attached to this post you'll find an example that lists all windows in the system (those returned by EnumWindows which unfortunately excludes the desktop window) and their children.  Notice that the example does _not_ cause EnumWindows and/or EnumChildWindows to be called recursively.  Since, it is often convenient to show the "genealogy" of a window, for that purpose, the example uses GetAncestor, IOW, it doesn't try to establish the genealogy using recursion. Also, the example works with both FPC 32 and 64 bit as well as with Delphi (tested with Delphi 2 only) no "mode whatever" needed. HTH. ---------------------------------------------------------------------- Remy Lebeau · 2021-03-03 23:37 Quote from: 440bx on March 03, 2021, 08:56:31 pmThe problem resides in the fact that you are using the same callback routine for both EnumWindows and EnumChildWindows.  That causes your callback routine to be called recursively for every child window.  That API was _not_ designed to be called recursively (see MSDN.)    EnumChildWindows will list all the children, grandchildren and grandgrand...grandchildren without having to call it for every child.  The fact that it is being called recursively is conceptually incorrect and it is actually surprising that doing so works in some cases (I'm taking your word for it that it worked with Delphi using the code you posted.) Good catch!  I forgot about that detail. ---------------------------------------------------------------------- 440bx · 2021-03-04 00:40 Quote from: Remy Lebeau on March 04, 2021, 12:37:21 amGood catch!  I forgot about that detail. It's a very easy to forget detail because its description (EnumChildWindows) doesn't make that clear at all.  Instead, it is only in the "remarks" section that they point it out. Another interesting comment in the remarks section implies that the API is a "snapshot" type of function, i.e, it builds a window list upfront then verifies that the window hasn't been destroyed before returning it.  The fact that it builds the window list upfront also explains why it won't return any child window that was created during the enumeration. NOTE: disassembly of the API confirms that it is a snapshot type function as the remarks section implies. ---------------------------------------------------------------------- balazsszekely · 2021-03-04 06:53 @440bx EnumChildWindows aside, your demo project is a very nice example of API programming. I like the way you build up everything from scratch(Memu, Listbox, About box, etc.),  it brings me back to the 90' and Visual Studio 6.0. I think every beginner should study your code, before using the power of form designers like Lazarus and Delphi. The only drawback as far as I can see is productivity. When you have a very large project, with hundred or so forms, creating everything with API is time consuming.  Thx for sharing. ---------------------------------------------------------------------- marcov · 2021-03-04 10:30 Quote from: Ash on March 03, 2021, 08:20:38 pmI will be using {$mode delphi} for that particular unit in my project. I don't think this is a 'feature' issue between the two modes. Calling a WinAPI function should behave the same in either mode But passing a callback is different in both modes. Objfpc requires a @ to be added. ---------------------------------------------------------------------- Ash · 2021-03-04 10:50 Quote from: 440bx on March 03, 2021, 08:56:31 pmThat API was _not_ designed to be called recursively (see MSDN.) I already knew about this. Although, in my opinion, if called recursively it shouldn't cause any crashes unless you have so many windows open that you run out of memory. Happy to be corrected about this though. I apologise for the badly simplified snippet of code I provided in my original example. I will provide another snippet with a few extra lines of my code but still omit the unnecessary stuff to keep it legible. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program EnumChildWindows_test; {$mode objfpc} // Crashes in this mode.//{$mode delphi} // Successful in this mode. uses windows; function EnumProc(hWnd: HWND; lParam: LPARAM): BOOL stdcall;begin  if (hWnd <> 0) then  begin    if (lParam = 0) then    begin// This block only gets executed by EnumWindows.       { code here to collect info for parent windows }       EnumChildWindows(hWnd, ENUMWINDOWSPROC(@EnumProc), hWnd);    end    else    begin// This block only gets executed by EnumChildWindows.// No recursive EnumChildWindows calls.       { code here to collect info for child windows }    end;  end;   result := true;end; begin// I pass a zero to lParam and test for it in EnumProc to prevent recursive calls to EnumChildWindows. Yes, it's ugly.  EnumWindows(ENUMWINDOWSPROC(@EnumProc), 0);end. I know this way of doing it is kind of illegible. I wrote it two decades ago. I don't even remember why I did things a certain way two years ago, much less twenty years. If I split the callback functions into two like in your example, it works fine. I'm fine with that. Or i can just use the Delphi compatibility mode. Which is also fine. However. Am I the only person that is curious as to why EnumChildWindows (when used in the way like the code above) crashes with {$mode objfpc}, but it works as intended with {$mode delphi} in FPC as well as in Delphi(7, 2007), c++, c# and VB? I may just be completely wrong about all of this. Please don't let this cat be a victim of its own curiosity. If nobody else is curious or I'm just wrong, then case closed, question solved, close thread. I'm just happy to finally get it working after hours of head scratching last night. If you percieve this as a rant, I assure you it is not. I've been using Lazarus+FPC for over ten years and I love it. You lot that create this stuff are incredible. Cheers. ---------------------------------------------------------------------- balazsszekely · 2021-03-04 11:30 @Ash QuoteAm I the only person that is curious as to why EnumChildWindows (when used in the way like the code above) crashes with {$mode objfpc}, but it works as intended with {$mode delphi} in FPC as well as in Delphi(7, 2007), c++, c# and VB?No I'm also curious, but the thing is my previous example does not crash with {$mode ObjFPC}{$H+}. Please test attached project. ---------------------------------------------------------------------- Ash · 2021-03-04 11:48 Quote from: GetMem on March 04, 2021, 12:30:05 pmNo I'm also curious, but the thing is my previous example does not crash with {$mode ObjFPC}{$H+}. Please test attached project. I have already acknowledged this in my previous post. Quote from: Ash on March 04, 2021, 11:50:04 amIf I split the callback functions into two like in your example, it works fine. I'm just curious as to why {$mode objfpc} can't handle it when "I DID IT MY WAY!" oh no. I have that song in my head now. ---------------------------------------------------------------------- 440bx · 2021-03-04 15:52 Quote from: GetMem on March 04, 2021, 07:53:19 amI think every beginner should study your code, before using the power of form designers like Lazarus and Delphi. Thank you for the kind words. Quote from: GetMem on March 04, 2021, 07:53:19 amThe only drawback as far as I can see is productivity. When you have a very large project, with hundred or so forms, creating everything with API is time consuming.  I know most people here will disagree with me but, in the long run, I think productivity might actually be higher programming directly to the API than using a framework.  The reason I say that is because all frameworks have a number of assumptions or conditions that the code has to meet in order to work with it and, quite often those assumptions or conditions are either not well documented or easy to forget. When I go about designing the user interface of a program, I think about where in my past programs I've already written code that implements the features I want, I grab the pieces I need then proceed to put them together for the new program.  I'm not sure that a framework would make that faster because, at least in my code, I know _exactly_ all the changes needed to make the whole thing work nicely.  IOW, there are no hidden dependencies to cause problems (which require time to solve.) That said, I won't deny being envious sometimes about the ease of dragging one thing or another onto forms and having a program just a few clicks away but, the result doesn't satisfy me for a final result.  As I type this, I'm thinking of .net.  Quote from: GetMem on March 04, 2021, 07:53:19 amThx for sharing. My pleasure. Quote from: Ash on March 04, 2021, 11:50:04 amAlthough, in my opinion, if called recursively it shouldn't cause any crashes unless you have so many windows open that you run out of memory. Happy to be corrected about this though. It's not reasonable to expect a function that was _not_ designed to be called recursively to operate properly when called recursively.  The first reason for that is, a recursive function has to be re-entrant, while a non recursive function does not have to meet that requirement and, if called recursively, the recursive calls will cause its internal data to be corrupted in ways that are hard to predict with consequent results. In the specific case of EnumChildWindows, that API allocates a block of memory in a heap and saves the pointer in a _global_ variable in user32's data segment.  That alone makes it clear that function is not re-entrant, therefore should _not_ be called recursively.  Quote from: Ash on March 04, 2021, 11:50:04 am{$mode objfpc} // Crashes in this mode. //{$mode delphi} // Successful in this mode. I admit to being somewhat curious too regarding the reason why it _seems_ to work in one mode (delphi) and not in the other but, knowing that the code is inherently wrong greatly subtracts from the desire to spend time to find out why. OTH, it shouldn't be that hard to find out but it could be tedious. In your code, place a breakpoint at "if (lParam = 0)" and another one at "result := true" and carefully inspect the stack at each break.  The most likely cause of the problem is the stack getting somehow messed up.  The difference between what happens in one mode and not in the other will probably be visible there. Quote from: Ash on March 04, 2021, 11:50:04 amIf you percieve this as a rant, ... I understand it's not a rant and, I believe curiosity is a good thing but, in general, my desire to satisfy it is much greater if I believe I did everything right than when I know the problem is at least in part caused by my code being incorrect.  In those cases, first I correct my code then, if it still fails, I will go out of my way to satisfy my curiosity to determine why it's failing.