Board index » cppbuilder » read of address in comctl32.dll when subclassing TListView

read of address in comctl32.dll when subclassing TListView


2007-10-22 07:41:12 AM
cppbuilder81
Hi there
To work arround the memory bug in TListView BCB5
I am trying to subclass the TCustomListView.
but it is not going very well I get an AV READ
of address in comctl32.dll when moving the
selected item vith the arrows also somtimes when
I click an item.
My my simple test code is below if somebody
can see what I'm doing wrong or if I don't do
something that I should do.
I will very much apreciate help.
Thanks in advance
Asger
header file:
class PACKAGE TAjReportView : public TCustomListView
{
private:
unsigned FItemCount;
protected:
void __fastcall setItemCount(unsigned Value);
void __fastcall CNNotify(TMessage &Msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(CN_NOTIFY, TMessage, CNNotify)
END_MESSAGE_MAP(TCustomListView)
public:
__fastcall TAjReportView(TComponent* Owner);
__published:
__property Align;
.....
}
Cpp file:
__fastcall TAjReportView::TAjReportView(TComponent* Owner)
: TCustomListView(Owner)
, FItemCount(0)
{
OwnerData = true;
ViewStyle = vsReport;
GridLines = true;
TabStop = true;
HideSelection = false;
RowSelect = true;
}
//---------------------------------------------------------
void __fastcall TAjReportView::setItemCount(unsigned Value)
{
if(Value == FItemCount)return;
FItemCount = Value;
ListView_SetItemCountEx(Handle, Value,
LVSICF_NOINVALIDATEALL);
}
//---------------------------------------------------------
char *Cap = "Carl Demion Nielson";
char *Sub1 = "Lovers Lane";
char *Sub2 = "69";
char *Sub3 = "London";
char *Sub4 = "Great Britain";
//---------------------------------------------------------
void __fastcall TAjReportView::CNNotify(TMessage &Msg)
{
LPNMHDR lpNmHdr = (LPNMHDR)Msg.LParam;
if(lpNmHdr->code != LVN_GETDISPINFO)
TCustomListView::Dispatch(&Msg);
LV_DISPINFO *LvDispInfo = (LV_DISPINFO*)(Msg.LParam);
int SubIndex;
if(LvDispInfo->item.mask&LVIF_TEXT)
{
//int Max = LvDispInfo->item.cchTextMax;
SubIndex = LvDispInfo->item.iSubItem;
if(SubIndex)
{
switch(SubIndex)
{
case 1: LvDispInfo->item.pszText = Sub1;break;
case 2: LvDispInfo->item.pszText = Sub2;break;
case 3: LvDispInfo->item.pszText = Sub3;break;
case 4: LvDispInfo->item.pszText = Sub4;break;
}
}else{
LvDispInfo->item.pszText = Cap;
}
}
if((LvDispInfo->item.mask&LVIF_IMAGE))
{
if(SubIndex)
LvDispInfo->item.iImage = -1;
else LvDispInfo->item.iImage = 0;
}
Msg.Result = false;
}
//-----------------------------------------------------------
Setting up the listview in the Form1 file:
void __fastcall TForm1::FormShow(TObject *Sender)
{
ReportView = new TAjReportView(this);
ReportView->Parent = this;
ReportView->Align = alClient;
ReportView->SmallImages = ImageList;
TListColumn* NewColumn = ReportView->Columns->Add();
NewColumn->Caption = "Name";
NewColumn->Width = 300;
NewColumn->AutoSize = true;
NewColumn = ReportView->Columns->Add();
NewColumn->Caption = "Address";
NewColumn->Width = 160;
NewColumn = ReportView->Columns->Add();
NewColumn->Caption = "Nr.";
NewColumn->Width = 40;
NewColumn = ReportView->Columns->Add();
NewColumn->Caption = "City";
NewColumn->Width = 160;
NewColumn = ReportView->Columns->Add();
NewColumn->Caption = "Country";
NewColumn->Width = 160;
//ListView1->Items->Count = 10000;
Timer1->Enabled = true;
}
//----------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
ReportView->ItemCount = 5000;
Timer1->Enabled = false;
}
//----------------------------------------------------------
 
 

Re:read of address in comctl32.dll when subclassing TListView

Hi again
I also get some rather bizare values in the LV_ITEM
struct when I scroll e.g.
iSubItem = 1237364
cchTextMax = 26
and this is when mask contains LVIF_TEXT
and if i do like they do in the TCustomListView source
setting pszText[0] = '\0'; on all messages with
iSubItem>ColCount I get an AV write of address.
I have just rebootet the PC so there should be nothing
wrong from previous AV's.
Kind regards
Asger
 

Re:read of address in comctl32.dll when subclassing TListView

"Asger Jørgensen" < XXXX@XXXXX.COM >wrote in message
Quote
To work arround the memory bug in TListView BCB5
What memory bug are you referring to exactly?
Quote
unsigned FItemCount;
ListView controls do not use unsigned values. Use a signed value instead.
int FItemCount;
Quote
void __fastcall TAjReportView::setItemCount(unsigned Value)
{
if(Value == FItemCount)return;
FItemCount = Value;
ListView_SetItemCountEx(Handle, Value, LVSICF_NOINVALIDATEALL);
}
void __fastcall TAjReportView::setItemCount(int Value)
{
if( Value < 0 )
Value = 0;
if( Value != FItemCount )
{
FItemCount = Value;
ListView_SetItemCountEx(Handle, Value, LVSICF_NOINVALIDATEALL);
}
}
Quote
if(lpNmHdr->code != LVN_GETDISPINFO)
TCustomListView::Dispatch(&Msg);
You are not exiting from the method, so you are processing all notifications
as if they were LVN_GETDISPINFO, which would be very bad.
if( lpNmHdr->code != LVN_GETDISPINFO )
{
TCustomListView::Dispatch(&Msg);
return; // <-- add this!
}
Quote
if(SubIndex)
{
switch(SubIndex)
{
case 1: LvDispInfo->item.pszText = Sub1;break;
case 2: LvDispInfo->item.pszText = Sub2;break;
case 3: LvDispInfo->item.pszText = Sub3;break;
case 4: LvDispInfo->item.pszText = Sub4;break;
}
}else{
LvDispInfo->item.pszText = Cap;
}
The pszText member is a buffer that the OS preallocates before issuing the
LVN_GETDISPINFO notification. You are reassigning that pointer to point at
a new memory address, losing the OS's buffer. When the notification
returns, the OS is then trying to free that memory, which will crash. You
need to copy your character data into the OS's original buffer instead. The
cchTextMax member tells you how large the buffer is. For example:
if( SubIndex )
{
switch( SubIndex )
{
case 1:
lstrcpyn(LvDispInfo->item.pszText, Sub1,
LvDispInfo->item.cchTextMax);
break;
case 2:
lstrcpyn(LvDispInfo->item.pszText, Sub2,
LvDispInfo->item.cchTextMax);
break;
case 3:
lstrcpyn(LvDispInfo->item.pszText, Sub3,
LvDispInfo->item.cchTextMax);
break;
case 4:
lstrcpyn(LvDispInfo->item.pszText, Sub4,
LvDispInfo->item.cchTextMax);
break;
}
}
else
lstrcpyn(LvDispInfo->item.pszText, Cap,
LvDispInfo->item.cchTextMax);
Gambit
 

{smallsort}

Re:read of address in comctl32.dll when subclassing TListView

Hi Remy
Am I glad that You are back..?
YES!!!
In article <471cd82e$ XXXX@XXXXX.COM >
XXXX@XXXXX.COM
Remy Lebeau (TeamB) says:
Quote

"Asger Jørgensen" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>To work arround the memory bug in TListView BCB5

What memory bug are you referring to exactly?
The huge consumbtion of memory when scrolling mentioned
in my thread in Component.using
**Memory leak in Virtual Listview**
And described in this old message:
borland.public.cppbuilder.vcl.components.using
Fra: "Roland Fuchs" < XXXX@XXXXX.COM >
Dato: Mon, 8 Jul 2002 17:14:08 +0200
Emne: Must rebuild the VCL
Something about the TSubItems class and a buggi Clear method.
Quote
ListView controls do not use unsigned values. Use a signed value instead..

int FItemCount;
I'll do that thanks.
Quote
You are not exiting from the method, so you are processing all notifications
as if they were LVN_GETDISPINFO, which would be very bad.

if( lpNmHdr->code != LVN_GETDISPINFO )
{
TCustomListView::Dispatch(&Msg);
return; // <-- add this!
}
Thanks now everything works, Yes!
Why didn't I see that my self ? <shaking my head>
Quote
The pszText member is a buffer that the OS preallocates before issuing the
LVN_GETDISPINFO notification. You are reassigning that pointer to point at
a new memory address, losing the OS's buffer. When the notification
returns, the OS is then trying to free that memory, which will crash. You
need to copy your character data into the OS's original buffer instead. The
cchTextMax member tells you how large the buffer is. For example:
According to the win32 helpfile for LV_DISPINFO:
If the structure is receiving item text, the pszText
and cchTextMax members specify the address and size
of a buffer.
You can either copy text to the buffer
****
or assign the address of a string to the pszText member.
****
In the latter case, you must not change or delete the string
until the corresponding item text is deleted or two additional
LVN_GETDISPINFO messages have been sent.
So unless the helpfile is wrong it looks ok, right ?
And no I do not intend to use AnsiString.c_str()
I supose the OS checks if the pointer is the same
when it return and if not free it's own buffer.
I have tested a list with 10,000 items, done a lot
of scrolling and it gives no errors.
Thanks for all Your help
Happy regards
Asger
 

Re:read of address in comctl32.dll when subclassing TListView

"Asger Jørgensen" < XXXX@XXXXX.COM >wrote in message
Quote
Am I glad that You are back..?
Did I go somewhere?
Quote
Something about the TSubItems class and a buggi Clear method.
Not buggy - missing. TSubItems does not override Clear() in BCB 5, so
TSubItem's internal list of indexes keeps growing.
Quote
According to the win32 helpfile for LV_DISPINFO:
If the structure is receiving item text, the pszText
and cchTextMax members specify the address and size
of a buffer.
You can either copy text to the buffer
Which is the same thing I said to do, and is what the VCL does internally.
Quote
or assign the address of a string to the pszText member.

In the latter case, you must not change or delete the string
until the corresponding item text is deleted or two additional
LVN_GETDISPINFO messages have been sent.
I would not suggest relying on that behavior.
Gambit
 

Re:read of address in comctl32.dll when subclassing TListView

Hi Remy
In article <471d0e31$ XXXX@XXXXX.COM >
XXXX@XXXXX.COM
Remy Lebeau (TeamB) says:
Quote

"Asger Jørgensen" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>Am I glad that You are back..?

Did I go somewhere?
Dont think so, it was just me being impaisient.
Looking at the same code all yesterday, - even more
head-shaking.
You probably just had a well earned week-end.;-)
Quote

Which is the same thing I said to do, and is what the
VCL does internally.
Yes I've seen that and I started by doing the same
untill I stumbled over those lines in the help file.
It would of course have been more releiable if they
had been in the LV_ITEM text, but then again that
structure is used for more then just LVN_GETDISPINFO.
And it does state the same at the msdn web.
msdn2.microsoft.com/en-us/library/ms942096.aspx
Quote

I would not suggest relying on that behavior.
Is there a reason to why not ?
Thanks again
Kind regards
Asger