views:

183

answers:

2

Do I need to allocate memory when performing a Delphi string copy?

I've a function which posts a Windows message to another form in my application. It looks something like this:

// Note:  PThreadMessage = ^TThreadMessage; TThreadMessage = String;

function PostMyMessage( aStr : string );
var
  gMsgPtr : PThreadMessage;
  gStrLen : Integer;
begin
  New(gMsgPtr);
  gStrLen := StrLen(PWideChar(aMsg));
  gMsgPtr^ := Copy(aMsg, 0, gStrLen);
  PostMessage(ParentHandle, WM_LOGFILE, aLevel, Integer(gMsgPtr));

  // Prevent Delphi from freeing this memory before consumed.
  LParam(gMsgPtr) := 0;
end;
A: 

You don't need to allocate memory to copy strings, but you don't want to pass a pointer to a Delphi string in this situation. You want to pass a PChar, (AKA a C string,) and you do have to allocate memory for that.

Mason Wheeler
+2  A: 

You don't need to allocate any memory. In fact, you don't even need to call Copy. A simple string assignment is sufficient; the reference count will be tracked correctly acorss multiple threads. You also don't need to clear gMsgPtr; since it's not a variable of type string, the compiler won't insert any cleanup code for it.

begin
  New(gMsgPtr);
  gMsgPtr^ := aMsg;
  PostMessage(ParentHandle, wm_LogFile, aLevel, LParam(gMsgPtr));
end;

If you want to call Copy anyway, you don't need to calculate the length first. It will automatically clamp the index parameters to the largest allowed range. When you want to ask for "rest of the string," you can simply pass MaxInt as the third parameter and Copy will know what to do. It likewise does that for the second parameter, automatically increasing any value to be at least 1, the lowest valid character index.

Rob Kennedy
@Rob: I must be missing something. The assignment of aMsg to gMsgPtr increments the reference count of the string. After executing PostMessage, the reference count gets decremented. Isn't it possible that when the methodhandler for the message finally get's executed, the string passed in LParam is no longer pointing to valid memory (aka has been freed)?
Lieven
@Lieven, upon entry to this function, the reference count must be at least 1 (for the reference held in `aMsg`). The assignment to `gMsgPtr^` adds a reference, so now it's at least 2. When this function returns, the reference in `aMsg` disappears, but the count is still at least 1 because of `gMsgPtr^`. When the message handler runs, it will call `Dispose(PThreadMessage(lParam))` and the reference count will be decremented again, possibly freeing the string. The code shown here doesn't free `gMsgPtr`; ownership of that memory is transferred to the message's recipient.
Rob Kennedy
I see. I didn't think through the fact that the variable was declared as a PThreadMessage io TThreadMessage. Thank you for the explanation.
Lieven