Memory leak issue

Hello,

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;

Some more info:

  • Delphi XE4
  • VS 2015, .NET 4.5.2 project
  • Hydra 4

Any help would be appreaciated.

why you can’t use this solution, i.e. call SafeArrayDestroy after DispatchArrayAsSafeArray?

safeArray := DispatchArrayAsSafeArray(dispatchArray);
try
  FPluginInterface.SomeMethod(safeArray);
finally
  SafeArrayDestroy(safeArray);
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);

Third test, memory leak
safeArray := DispatchArrayAsSafeArray(dispatchArray);
try
FPluginInterface.SomeMethod(safeArray);
finally
SafeArrayDestroy(safeArray);
end;

can you create a simple testcase that reproduces this memory leak, pls?
you can attach it here or send directly to support@

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.

SamplePluginModule.zip (1.1 MB)

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.

COM object reference release can be forced by explicitly calling Marshal.ReleaseComObject.

update your code like

        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

Hello,

Thanks for the response. However, I tried this solution, but still get the memory leak.

Maybe it has to do with the version of Delphi, Hydra or .NET I’m using?

Maybe it has to do with the version of Delphi, Hydra or .NET I’m using?
it shouldn’t depend

can you test updated testcase: SamplePluginModule.zip (36.0 KB)?

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!