Recent

Author Topic: WinAPI | EnumChildWindows causes SIGSEGV/SIGILL  (Read 1802 times)

GetMem

  • Hero Member
  • *****
  • Posts: 4017
Re: WinAPI | EnumChildWindows causes SIGSEGV/SIGILL
« Reply #15 on: March 04, 2021, 07:53:19 am »
@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

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 9283
  • FPC developer.
Re: WinAPI | EnumChildWindows causes SIGSEGV/SIGILL
« Reply #16 on: March 04, 2021, 11:30:59 am »
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 passing a callback is different in both modes. Objfpc requires a @ to be added.

Ash

  • New member
  • *
  • Posts: 6
Re: WinAPI | EnumChildWindows causes SIGSEGV/SIGILL
« Reply #17 on: March 04, 2021, 11:50:04 am »
That 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][+][-]
  1. program EnumChildWindows_test;
  2.  
  3. {$mode objfpc} // Crashes in this mode.
  4. //{$mode delphi} // Successful in this mode.
  5.  
  6. uses windows;
  7.  
  8. function EnumProc(hWnd: HWND; lParam: LPARAM): BOOL stdcall;
  9. begin
  10.   if (hWnd <> 0) then
  11.   begin
  12.     if (lParam = 0) then
  13.     begin
  14. // This block only gets executed by EnumWindows.
  15.  
  16.       { code here to collect info for parent windows }
  17.  
  18.       EnumChildWindows(hWnd, ENUMWINDOWSPROC(@EnumProc), hWnd);
  19.     end
  20.     else
  21.     begin
  22. // This block only gets executed by EnumChildWindows.
  23. // No recursive EnumChildWindows calls.
  24.  
  25.       { code here to collect info for child windows }
  26.     end;
  27.   end;
  28.  
  29.   result := true;
  30. end;
  31.  
  32. begin
  33. // I pass a zero to lParam and test for it in EnumProc to prevent recursive calls to EnumChildWindows. Yes, it's ugly.
  34.   EnumWindows(ENUMWINDOWSPROC(@EnumProc), 0);
  35. end.
  36.  
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.
« Last Edit: March 04, 2021, 12:04:17 pm by Ash »

GetMem

  • Hero Member
  • *****
  • Posts: 4017
Re: WinAPI | EnumChildWindows causes SIGSEGV/SIGILL
« Reply #18 on: March 04, 2021, 12:30:05 pm »
@Ash
Quote
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?
No I'm also curious, but the thing is my previous example does not crash with {$mode ObjFPC}{$H+}. Please test attached project.   

Ash

  • New member
  • *
  • Posts: 6
Re: WinAPI | EnumChildWindows causes SIGSEGV/SIGILL
« Reply #19 on: March 04, 2021, 12:48:36 pm »
No 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.
If 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. :D

440bx

  • Hero Member
  • *****
  • Posts: 2310
Re: WinAPI | EnumChildWindows causes SIGSEGV/SIGILL
« Reply #20 on: March 04, 2021, 04:52:18 pm »
I think every beginner should study your code, before using the power of form designers like Lazarus and Delphi.
Thank you for the kind words.

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. 
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. 

Thx for sharing.
My pleasure.



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.
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. 

{$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.

If 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. :)

FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

 

TinyPortal © 2005-2018