I have a function that returns an OleVariant
that contains an IDispatch
reference counted object:
class function TGrobber.Make: OleVariant;
begin
Result := (TGrobber.Create as IDispatch);
// Reference count of IDispatch inside Result at this point is 1
end;
And the end of the function, the RefCount of the object inside the OleVariant
is correctly 1
.
But when the OleVariant
is returned to the caller, the contained object suddenly has a reference count of 2
:
var
grobber: OleVariant;
begin
grobber := TGrobber.Make; // function that returns an OleVariant
// Reference count of IDispatch inside OleVariant is now 2 (wrong)
end;
This is because after Delphi calls the Make function, it calls VarCopy:
Project2.dpr.147: grobber := TGrobber.Make;
004DA79D 8D55E0 lea edx,[ebp-$20]
004DA7A0 A1DC614D00 mov eax,[$004d61dc]
004DA7A5 E8CAC0FFFF call TGrobber.Make <== Creates the object (refcount 1)
004DA7AA 8D55E0 lea edx,[ebp-$20]
004DA7AD B800444E00 mov eax,$004e4400
004DA7B2 E8B128F5FF call @VarCopy <== Adds an extra reference, loses the old reference
Which means:
- the compiler is adding an extra reference to the returned variable
- while not releasing the old reference
- or alternatively not calling
@VarCopy
at all
How can i ensure that Delphi either:
- destroys the old
OleVariant
after it makes a copy - not make a copy of the
OleVariant
, since it doesn't need to make a copy
CodePudding user response:
The compiler does not lose the "old" reference. The result of TGrobber.Make()
is first stored in a hidden local variable (located at [ebp-$20]
), which is then assigned to your grobber
variable (located at $004e4400
), hence the copy. So the refcount being 2 is correct. Both variables will be finalized, decrementing the refcount by 2, when they go out of scope when the containing routine exits.
Your code is effectively doing the equivalent of this:
var
hidden: OleVariant;
grobber: OleVariant;
begin
TGrobber.Make(hidden); // function that returns an OleVariant via a hidden var parameter
grobber := hidden; // VarCopy(grobber, hidden) is called here
...
end; // VarClear(grobber) and VarClear(hidden) are called here...