Export to GitHub

mustangpeakeasylistview - issue #6

IncrementalSearch crashes the project if compiled with 64-Bit in Delphi XE2


Posted on Aug 11, 2012 by Grumpy Bird

What steps will reproduce the problem?

  1. Use custom item classes based on "TEasyItemVirtual"
  2. Pre-Initialize the listview IncrementalSearch properties before adding items
  3. When using the keyboard cursor keys to change selections, the application crashes (regardless of view style, Thumbnail or Report). This does not happen when using the mouse.

What is the expected output? What do you see instead?

Incremental Search speaks for itself, but when I compile my application as 64-bit, the project crashes out entirely, requiring me to use CTRL+F2 to reset the running application.

What version of the product are you using? On what operating system?

EasyListview.pas says version 2.1.0, but in order to clarify, I'm using the SVN trunk that I downloaded maybe 3 days before reporting this issue. I'm using Windows 7 Ultimate x64.

Please provide any additional information below.

The following code is a portion of a "procedure InitListView(AELV:TEasyListView)" from my application:

AELV.IncrementalSearch.Direction := eisdForward; AELV.IncrementalSearch.Enabled := True; AELV.IncrementalSearch.ItemType := eisiAll; AELV.IncrementalSearch.ResetTime := 500; AELV.IncrementalSearch.StartType := eissLastHit;

The code with InitListView also initializes varies properties for "GroupFont", "Font", "PaintInfoGroup", "Selection", "BackGround", "PaintInfoItem" and "CellSizes", and each have been tested while debugging this problem. The crash issue occurs only when compiled in 64-Bits, and IncrementalSearch.Enabled is set to TRUE.

Comment #1

Posted on Oct 11, 2012 by Happy Cat

Hi Peter, I had the same problem in my current delphi project using the 64 bit compiler (and, as you, only when compiling to target platform Win64).

My configuration: Delphi XE2, 64 bit target selected Unit: EasyListview.pas Version 2.1.0 Windows 7 Ultimate x64 1. I'm using the class (component) TEasyListview (TCustomEasyListview) 2. IncrementalSearch.Enabled = True 3. The program crashed after pressing a key

Fortunately, IncrementalSearch.ResetTime was set to 2000 (2 seconds) in my project. If it would not, maybe I had not recognized that the exception does NOT occur WHEN PRESSING a key (500 ms is very short to recognize), but rather AFTER NOT PRESSING some more keys (like when you really do an incremental search).

So I looked for the code which is executed when the incremental search "times out" and found a method "TimerProc". I set a breakpoint in EasyListview.pas on the first code line of the method "TimerProc" and ran the debugger.

Now I pressed a key (but no more keys), and as expected, after the timeout (2 seconds), the method "TimerProc" was called:

The TEasyIncrementalSearchManager.TimerProc is called indirectly from the Timer via a "CallbackStub" object because windows API callback functions cannot be methods.

  • this TimerProc method calls ResetSearch
  • in ResetSearch, the EndTimer method is called

As you see, in the EndTimer method it is checked if the incremental search timer is running. If yes, the timer is to be destroyed by KillTimer(...). A few lines below, the TimerStub object variable is set to nil. I'm not so familiar with 'interfaces' (FTimerStub is an instance of ICallbackStub) but it seems when the (instance) variable is set to nil, the "TimerStub" object is destroyed. Can you confirm that?

This seems to be the problem. If the ICallbackStub object is destroyed, we also destroy our "environment" (virtual space) we need to call (enter and return from) an object method (TimerProc) as a callback procedure (needed by windows API). In result, returning from the TimerProc method, we are "landing in nirvana" and therefore the delphi debugger raises an access violation but cannot display some senseful information (like the call stack).

For detailed explanation of how the "callback stub" mechanism is implemented, see here: http://stackoverflow.com/questions/8283490/how-to-turn-a-method-to-a-callback-procedure-in-64bit-delphi-xe2

If you like to reproduce my debugging, set a breakpoint in method "TimerProc":

procedure TEasyIncrementalSearchManager.TimerProc(Window: HWnd; uMsg: UINT; idEvent: UINT; dwTime: DWORD); // // The callback for the Timer. A callback is not effected by any blocked message // handler taking their sweet time. // begin ResetSearch; end; // <---- * set breakpoint here, exeption is raised when leaving the procedure *

Run the program and press a key. After incremental search timeout (ResetTime property) and continuing debugging, you will see when exiting the TimerProc method, the exception is raised.

To fix it, I changed the following methods:

  • TEasyIncrementalSearchManager.StartTimer
  • TEasyIncrementalSearchManager.EndTimer
  • TEasyIncrementalSearchManager.Destroy

Here the modified code:

procedure TEasyIncrementalSearchManager.StartTimer; // // Starts the Timer for the search // begin if Enabled and not(eissTimerRunning in State) then begin if not Assigned(TimerStub) then // 10/11/2012 ti - create timer stub only once TimerStub := TCallbackStub.Create(Self, @TEasyIncrementalSearchManager.TimerProc, 4); hTimer := SetTimer(0, 0, ResetTime, TimerStub.StubPointer); Include(FState, eissTimerRunning); end end;

procedure TEasyIncrementalSearchManager.EndTimer; // // Kills and destroys the Timer // begin if eissTimerRunning in State then begin if KillTimer(0, hTimer) then begin hTimer := 0; Exclude(FState, eissTimerRunning); //TimerStub := nil; // 10/11/2012 ti - DO NOT destroy TimerStub here, rather in destructor "Destroy" end else Exception.Create('Can not Destroy Incremental Search Timer'); end end;

destructor TEasyIncrementalSearchManager.Destroy; begin EndTimer; TimerStub := nil; // 10/11/2012 ti - TimerStub no longer needed, destroy it inherited end;

After these changes, the program should run without an exception in Delphi XE2 (64 Bit).

Regards,

Tom I.

Comment #2

Posted on Jul 9, 2013 by Happy Lion

Same problem (and same solution :) in my project. Please add this code to SVN.

Status: New

Labels:
Type-Defect Priority-Medium