views:

161

answers:

4

Hi! I'm using Delphi 2009 and get some strange errors using the following code segment:

var
  Str     : AnsiString;
  CharPtr : PAnsiChar;
...
CharPtr := PAnsiChar (Str);
ExecuteInBackgroundThread (
  procedure
  begin
  DoSomething (CharPtr);
  end);

I'm guessing that the string is destructed when falling out of scope and under some timing conditions DoSomething will yield the strangest results. So the first question is: am I right?

Second question is: How can I circumvent the string being destructed? What's the proper way to do this?

Thanks in advance.

A: 

Why not pass the string by value, rather than pointer/reference?

Preet Sangha
not really possible in Delphi.
Smasher
Smasher, see wcoenen's answer.
Rob Kennedy
@Rob Kennedy: That's not really by value isn't it? The important difference is that Str is used inside the anonymous method and therefore gets captured. String and "by value" in Delphi sounds contradictory to me.
Smasher
+2  A: 

Okay, I think I might have figured it out.

I'm using an anonymous method, so the compiler should capture my local variables. Apparently, it does only capture the variables that I actually use in the anonymous method. That means that CharPtr is captured but not SendStr. So, when SendStr falls out of scope it is destructed and CharPtr is now in danger of pointing to some random garbage.

With the following modification

ExecuteInBackgroundThread (
  procedure
  begin
  Log (Str);
  DoSomething (CharPtr);
  end);

everything seems to work fine.

Smasher
The Str reference count might be decremented right after the Log statement rather than at the end of the procedure. Or maybe not, but the point is you don't want to depend on such compiler details.
Wim Coenen
I don't really know but I don't think that captured variables are released before the end of the anonymous method.
Smasher
wcoenen - yes you can depend on such details, otherwise Delphi code would break quite severely - there's a lot of code that typecasts strings to PChar for API calls, relying on the string not going away, even though it isn't used subsequently.
Barry Kelly
smasher - you have the right idea. Capturing variables extends their lifetime. If you don't capture the string, but do capture a pointer to the contents of the string, the string will get freed from out underneath you. So you either capture the string with a do-nothing procedure - such as "procedure Use(const X); begin end;" - or you leave the typecast up to the last minute, or do it in the body of the anonymous method.
Barry Kelly
+6  A: 

So the first question is: am I right?

Most likely, yes. Delphi's AnsiString is reference counted. When Str goes out of scope, the reference count is decremented. If the reference count reaches zero then the memory it occupied may be reused.

Second question is: How can I circumvent the string being destructed? What's the proper way to to this?

By not using pointers, like this:

var
  Str     : AnsiString;
...
ExecuteInBackgroundThread (
  procedure
  begin
  DoSomething (Str);
  end);
Wim Coenen
+3  A: 

Just use:

DoSomething(PAnsiChar(Str));

General rule is simple: do not use PChar until at the very last moment. This way you don't need to think too much about memory management issues (well, mostly).

See also this great article.

Alexander
And how is this going to do any good against the real problem--the string going out of scope?
Loren Pechtel
That's because you use Str inside anonymous method, so now string variable will be captured and not go out of scope ;)
Alexander