I’m trying to pass an array of objects from my host Delphi to a .NET plugin. I have imported interfaces from my .NET plugin which I implement using THYFakeIDispatch, as suggested in this post:
It works well, but causes a memory leak in the host Delphi app.
First, I start by creating the objects derived from THYFakeIDispatch and adding them to a THYDispatchArray. Then I call the DispatchArrayAsSafeArray function (uHYInterfaceHelpers unit) which I then pass to a function of another plugin interface.
The problem seems to lie in that last part, as I believe it is creating more reference copies which never get released. I call SafeArrayDestroy (VarUtils unit) to release the references on the PVarArray. I removed the call to the plugin interface method and I get no memory leak. If I also remove the SafeArrayDestroy, then I get the same memory leak.
Here’s what the code looks like:
dispatchArray := ListAsDispatchArray(AList);
safeArray := DispatchArrayAsSafeArray(dispatchArray);
try
FPluginInterface.SomeMethod(safeArray); // if I remove this call, no memory leak
finally
SafeArrayDestroy(safeArray); // if I also remove this call, memory leak
end;
I’m not sure I understand your question. Basically, the PVarArray (safeArray variable in example above) needs to be passed to my plugin method. Here are the tests I ran: First test, no memory leak
safeArray := DispatchArrayAsSafeArray(dispatchArray);
SafeArrayDestroy(safeArray);
Second test, memory leak
safeArray := DispatchArrayAsSafeArray(dispatchArray);
FPluginInterface.SomeMethod(safeArray);
OK, I’ve prepared a sample project. Host application is in Delphi and plugin is in .NET.
Pretty straightforward, I add items to a list view and call a method from my plugin that returns a count.
EDIT: I’ve updated the sample to include another function which displays items in a message box, but these items where created on the .NET plugin side. This is the way I was passing arrays before I realized I could create instances on the Delphi host side. This way doesn’t cause any memory leaks.
In managed code external COM interfaces are wrapped into Runtime Callable Wrapper (RCW). Unlike a raw COM interface RCW lifespan is determined by the garbage collector that does not use reference counts.
public int GetCount(IItem[] items) {
int res = items.Length;
foreach (var item in items)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(item);
}
return res;
}
after this, no memory leaks will be in Delphi project
By comparing the code of both projets, I realized I had made a mistake in the Button1Click event handler in the Delphi project:
itemsArray := GetItems;
itemCount := module.GetCount(GetItems); <== should be passing itemsArray!
SafeArrayDestroy(itemsArray);
So, Marshal.ReleaseComObject on the .NET side fixes the issue.
Sorry the code typo and thanks for your help, much appreciated!