Board index » delphi » Access Violation with virtual ListView

Access Violation with virtual ListView

I have been using a ListView. When the selection shifts to a different list
item, I need to do something using the newly-selected item (or rather, with
its index value).  When using the ListView in non-virtual mode, I was able
to do this using the ListView's OnChange handler:

procedure TForm1.ListView1Change(Sender: TObject;
  Item: TListItem; Change: TItemChange);
begin
if (Change = ctState) and (Item.Selected)
then do_something (Item.Index)
end;

Now that I have changed the ListView to work in virtual mode (OwnerData =
true), I get an access violation with this OnChange handler. When I change
the selected item, e.g. by pressing the down-arrow key, my OnChange handler
above is executed, apparently successfully, and only then is the access
violation raised.

If I temporarily comment out the call to "do_something", the access
violation still occurs.  If I further comment out the reference to
Item.Selected, so that the body of the handler consists only of
  if (Change = ctState)
  then
the violation does *not* occur (but of course my procedure does nothing.)

Perhaps the strangest thing is that if I ignore the violation (by pressing
F9 in the IDE to make the program continue, as well as a couple of OK
buttons), the results are perfect.

Another strange thing is this.  Suppose I am moving the selection from item
3 to item 4. I expected the handler to be called twice, first for item 3,
and then
for item 4.  The test "Item.Selected" is included to make the handler do
nothing when called for deselecting item 3, but to do something for
selecting item 4 - remember that the OnChange handler is called *after* the
change has been made.

With non-virtual operation, the handler is actually called 3 times.
First, for item 3 selected, not focused, change = ctState
Second, for item 3 not selected, not focused, change = ctState
Third, for item 4 selected, focused, change = ctState

With virtual operation, the handler is called twice:
First, for item 3 selected, not focused, change = ctState, then violation
Second, for item 4 selected, focused, change = ctState.
I had expected selected to be false on the first call.  But adding "and
(Item.Focused)" to the condition in the handler, to prevent it calling
do_something on the first call, does not prevent the violation before the
second call.

I have also tried adding an "OnChanging" handler, to check the values of
Selected and Focused before the change, but it is never executed in virtual
mode - another mystery.

Any ideas about what is causing the access violation, and how to get rid of
it?

Thanks,
Ciarn Duibhn.

 

Re:Access Violation with virtual ListView


Got it!

I said before, when moving selection from item 3 to item 4 (e.g.) of the
ListView:

Quote
> With non-virtual operation, the [OnChange] handler is actually called 3
times.
> First, for item 3 selected, not focused, change = ctState
> Second, for item 3 not selected, not focused, change = ctState
> Third, for item 4 selected, focused, change = ctState

> With virtual operation, the [OnChange] handler is called twice:
> First, for item 3 selected, not focused, change = ctState, then violation
> Second, for item 4 selected, focused, change = ctState.

I now know that, in virtual operation too, there are three calls to the
OnChange handler.  The one I was missing, which happens between the other
two, and which evidently caused the violation, has change = ctState and...
Item = nil! (Did they just put it in to make life hard for us??!)

The following OnChange handler now works as required:

procedure TForm1.ListView1Change
(Sender: TObject; Item: TListItem; Change: TItemChange);
begin
if (Change = ctState) and (Item <> nil) and (Item.Selected) and
(Item.Focused)
then do_something (Item.Index)
end;

I have also noticed that the value of Item.Index (and possibly of Item as a
whole) can change unexpectedly within the handler, e.g. I had the following
code in the handler at one stage, just after the "begin":

case Change of
  ctState: ShowMessage ('ctState');
  ctText: ShowMessage ('ctText');
  ctImage: ShowMessage ('ctImage')
  end;
if Item = nil
then ShowMessage ('OnChange for null item')
else begin
     if Item.Selected
     then ShowMessage ('OnChange for item '+inttostr(Item.Index) + '
selected')
     else ShowMessage ('OnChange for item '+inttostr(Item.Index) + ' not
selected');
     if Item.Focused
     then ShowMessage ('OnChange for item '+inttostr(Item.Index) + '
focused')
     else ShowMessage ('OnChange for item '+inttostr(Item.Index) + ' not
focused')
     end;

and it could happen that the item index shown on the "selected" message
differed from that shown on the "focused" message, although both messages
must have come from the same call to the handler.

However, I am not going to worry about that, as the program is now working.

Ciarn Duibhn.

Re:Access Violation with virtual ListView


"Ciarn Duibhn" <cia...@oduibhin.freeserve.co.uk> skrev i melding
news:abp46v$7pu$1@news6.svr.pol.co.uk...
[...]

Quote
> I have also noticed that the value of Item.Index (and possibly of Item as
a
> whole) can change unexpectedly within the handler, e.g. I had the
following
> code in the handler at one stage, just after the "begin":

> case Change of
>   ctState: ShowMessage ('ctState');
>   ctText: ShowMessage ('ctText');
>   ctImage: ShowMessage ('ctImage')
>   end;
> if Item = nil
> then ShowMessage ('OnChange for null item')
> else begin
>      if Item.Selected
>      then ShowMessage ('OnChange for item '+inttostr(Item.Index) + '
> selected')
>      else ShowMessage ('OnChange for item '+inttostr(Item.Index) + ' not
> selected');
>      if Item.Focused
>      then ShowMessage ('OnChange for item '+inttostr(Item.Index) + '
> focused')
>      else ShowMessage ('OnChange for item '+inttostr(Item.Index) + ' not
> focused')
>      end;

> and it could happen that the item index shown on the "selected" message
> differed from that shown on the "focused" message, although both messages
> must have come from the same call to the handler.

I would believe the reason is that your ShowMessage actually changes the
app's focus...

--
Bj?rge S?ther
bjorge@hahaha_itte.no

Re:Access Violation with virtual ListView


In article <abp46v$7p...@news6.svr.pol.co.uk>, "Ciarn Duibhn"

Quote
<cia...@oduibhin.freeserve.co.uk> writes:
>if (Change = ctState) and (Item <> nil) and (Item.Selected) and
>(Item.Focused)
>then do_something (Item.Index)
>end;

It usually does not matter whether boolean expression evaluation is
"short-circuit" or "complete". But is this case above it is essential that
evaluation is "short circuit" (otherwise Item could be nil and nil.Selected
would then AV).

IMO such cases should for safety be specifically tagged with a {$BOOLEVAL OFF}
compiler directive before the expression, even though one normally uses that
default compiler setting.

This then protects against an inadvertant non-default setting elsewhere, and
also flags such sensitivity of the code.

Alan Lloyd
alangll...@aol.com

Other Threads