?
Solved

Access violation TStringList

Posted on 2003-02-18
12
Medium Priority
?
1,655 Views
Last Modified: 2012-06-22
Trying to figure out why this doesn't work:

cdromList := GetCDROMDrives;


where GetCDROMDrives returns a TStringList;

There is some confusion on my part as to how to create, assing, and free a TStringList properly.


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


unit installpoint;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TFormInstall = class(TForm)
    LabelInstallInstructions: TLabel;
    ButtonInstall: TButton;
    procedure ButtonInstallClick(Sender: TObject);
    function GetCDROMDrives: TStringList;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormInstall: TFormInstall;

implementation

{$R *.DFM}

procedure TFormInstall.ButtonInstallClick(Sender: TObject);
var
  cdromList : TStringList;
begin
    cdromList := TStringList.Create;

    cdromList.Clear;

    cdromList := GetCDROMDrives;

    ShowMessage(cdromList[0]);
end;

function TFormInstall.GetCDROMDrives: TStringList;
var
  DriveBits: set of 0..25;
  Drives,DriveNum: integer;
  DriveLetter: string;
  list : TStringList;
begin
  list := TStringList.Create;
  list.Clear;
  Drives := GetLogicalDrives;
  if Drives <> 0 then
  begin
    integer(DriveBits) := Drives;
    for DriveNum := 0 to 25 do
    begin
      if (DriveNum in DriveBits) then
      begin
        DriveLetter := char(DriveNum+Ord('A'))+':';
        if GetDriveType(PChar(DriveLetter)) = DRIVE_CDROM then
          list.Add(DriveLetter);
      end;
    end;
  end;

  GetCDROMDrives := list;

  list.Free;

end;

end.
0
Comment
Question by:Tom Knowlton
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
  • 2
  • +4
12 Comments
 
LVL 17

Expert Comment

by:geobul
ID: 7978376
Hi,

The following is a simple example how this could be done:

function GetSL:TStringList;
begin
  result := TStringList.Create; // create the StringList
  result.Add('aaa');
  result.Add('bbb');
  result.Add('ccc');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
begin
  sl := GetSL; // 'Create' is inside the function
  ComboBox1.Items := sl;
  sl.Free; // 'Free' is outside the function
end;

Regards, Geo
0
 
LVL 1

Expert Comment

by:petervullings
ID: 7978447
If you want the main procedure to manage the creation and freeing, try something like:

procedure TFormInstall.ButtonInstallClick(Sender: TObject);
var
 cdromList : TStringList;
begin
   cdromList := TStringList.Create;
   GetCDROMDrives(cdromList)
   ShowMessage(cdromList[0]);
end;

procedure TFormInstall.GetCDROMDrives( list : TStringList );
var
 DriveBits: set of 0..25;
 Drives,DriveNum: integer;
 DriveLetter: string;
 list : TStringList;
begin
 {check that list is created}
 if not assigned(list) then exit;
 list.Clear;
 Drives := GetLogicalDrives;
 if Drives <> 0 then
 begin
   integer(DriveBits) := Drives;
   for DriveNum := 0 to 25 do
   begin
     if (DriveNum in DriveBits) then
     begin
       DriveLetter := char(DriveNum+Ord('A'))+':';
       if GetDriveType(PChar(DriveLetter)) = DRIVE_CDROM then
         list.Add(DriveLetter);
     end;
   end;
 end;
end;

Thats it.

Points to remember are that when an object is passed between functions, you are actually passing the object itself - not a copy of it.  To understand this you need to realise that 'list' is actually pointer to the TStringList, but Delphi makes it so you dont really have to concern yourself with this.

With a string:

var
  str : string;
begin
  str := 'aaa';
  doSomething( str );
  showMessage(str); { will still show 'aaa' }
end;

procedure doSomething( str : string );
begin
  str := 'bbb';
end;

With an object (e.g String List )
var
  str : TStringList;
begin
  str := TStringList.create;
  str.add('aaa');
  doSomething( str );
  showMessage(str[0]); { will now show 'bbb' }
  str.free;
end;

procedure doSomething( str : TStringList);
begin
  str[0] := 'bbb';
end;
 
Hope it helps,
Pea
0
 
LVL 1

Expert Comment

by:keashF
ID: 7978732
The reason why your code fails is the last line in getCDRomDrives:

list.free;

Ater passing the reference of list to getcdromdrive, you destroy the list and with it the result -> so you'll have a nil pointer as result.

check out this:

function test:TStringList;
var list:TStringList;
begin
list:=TStringList.create;
list.add('one');
list.add('two');
result:=list;
list.free;    // <-- this is the error
end;

procedure TForm1.Button1Click(Sender: TObject);
var l : TStringList;
begin
   l:=test;
   showmessage(l[0]);
end;

remove the errornous line and it will work, however i suggest you use petervullings approach.

cu
keashF
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Expert Comment

by:petervullings
ID: 7978887
If you do go with keashF's approach, remember to 'create' the string list only once - so if it is being created within the function 'getCDRomDrives', dont create it in the 'ButtonInstallClick' procedure. And, yes, don't 'free' it before you try to use it!
0
 
LVL 1

Expert Comment

by:petervullings
ID: 7978895
p.s. It is good practice to free the object in the same function that created it...
0
 
LVL 2

Expert Comment

by:BorlandMan
ID: 7979381

A simple way to make your code work here is to add the Stringlist as a param in your GetCDRoms and use the parameter in your GetCDROMDrives() work code. Once you exit that procedure , the method which called it will have the Stringlist with the correct values in it.

hth,
J


procedure TFormInstall.ButtonInstallClick(Sender: TObject);
var
 cdromList : TStringList;
begin
   cdromList := TStringList.Create;
   cdromList.Clear;

   GetCDROMDrives (cdRomList);
   ShowMessage(cdromList[0]);
end;

procedure TFormInstall.GetCDROMDrives(ACdRomList:  
                                   TStringList);
var
 DriveBits: set of 0..25;
 Drives,DriveNum: integer;
 DriveLetter: string;
begin
 list.Clear;
 Drives := GetLogicalDrives;
 if Drives <> 0 then
 begin
   integer(DriveBits) := Drives;
   for DriveNum := 0 to 25 do
   begin
     if (DriveNum in DriveBits) then
     begin
       DriveLetter := char(DriveNum+Ord('A'))+':';
       if GetDriveType(PChar(DriveLetter)) = DRIVE_CDROM then
         ACdRomList.Add(DriveLetter);
     end;
   end;
 end;

end;

end.
0
 
LVL 3

Expert Comment

by:sfock
ID: 7980672
hi guys, good comments, but

petervullings

>when an object is passed between functions, you are actually passing the object itself - not a copy of it.
it's not the obect itself but a reference to the object itself

keashF
> you destroy the list and with it the result -> so you'll have a nil pointer as result.
the reference will not be nil it will still point to the obejects former address, but the object will be destructed
and
>remove the errornous line and it will work,
yes the exception will be gone but you produce memory leaks

BorlandMan
what is different in your solution to petervullings solution?

knowlton
i admit to use petervullings solution.

0
 
LVL 17

Accepted Solution

by:
geobul earned 400 total points
ID: 7981043
Hi,

You all will produce memory leak suggesting more or less:
--Quote
procedure TFormInstall.ButtonInstallClick(Sender: TObject);
var
cdromList : TStringList;
begin
  cdromList := TStringList.Create;
  GetCDROMDrives(cdromList)
  ShowMessage(cdromList[0]);
end;
--End of quote

Reason: cdromList is a local variable inside the procedure which runs out of scope when the procedure ends up its execution. Without freeing cdromList at the end of the function you've got a memory leak.

So, add cdromList.Free; at the end. Usage of try..finally is recommended:
...
cdromList := TStringList.Create;
try
  // do something with the list
finally
  cdromList.Free;
end;
...

I agree that passing a stringlist as a parameter is more clearer but in my example I followed the definitions in the question and I have no memory leak and no AV.

Regards, Geo
0
 
LVL 5

Author Comment

by:Tom Knowlton
ID: 7983080
Thank you everyone.
0
 
LVL 2

Expert Comment

by:BorlandMan
ID: 8005790
uh,
sorry I must've missed it.  I didn't mean to repeat an existing solution.... but was going to indicate that you need to free the stringlist, which I obviously forgot to do to - (yep here's an excuse) I was in a hurry to get it done, because It was late and had to pack for a holiday.. I guess that's what I get for trying to rush

sorry,
thanks,
J
0
 

Expert Comment

by:joined
ID: 8563209
You can try EurekaLog (www.eurekalog.com).

EurekaLog is an add-in tool that gives to your application (GUI, Console, Web, etc.) the ability to catch every exception, and generates a detailed log of call stack (with unit, class, method and line #), showing and sending it back to you via email.

--
Best regards...

Fabio Dell'Aria.
0
 
LVL 5

Author Comment

by:Tom Knowlton
ID: 8565119
Thanks joined.  The issue was resolved satisfactorily some time ago, but I appreciate the info.

Tom
0

Featured Post

On Demand Webinar: Networking for the Cloud Era

Ready to improve network connectivity? Watch this webinar to learn how SD-WANs and a one-click instant connect tool can boost provisions, deployment, and management of your cloud connection.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …
In this video, Percona Solution Engineer Rick Golba discuss how (and why) you implement high availability in a database environment. To discuss how Percona Consulting can help with your design and architecture needs for your database and infrastr…
Suggested Courses
Course of the Month8 days, 20 hours left to enroll

764 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question