0

In an C++ unmamaged project an external(*) managed DLL is used via COM Callable Wrapper (CCW) (Registered with RegAsm). But there is a memory leak: Managed memory was never cleaned up.

Background: The C++ project is an old-grown 32 bit program. It loads several DLLs, a few of them are managed DLLs (written in C#).

In an minimal example the .Net DLL works fine. Maybe the GC got confused with using managed Code with different ways?

How do I force an Garbage Collection of the CCW?

The following code in an existing Wrapper did not work:

    __declspec(dllexport) void ExecuteCcwGcWrapper(void* ComObject)
    {
        IntPtr ptr(ComObject);
        Object^  obj = System::Runtime::InteropServices::Marshal::GetObjectForIUnknown(ptr);
        try {
            System::Runtime::InteropServices::Marshal::FinalReleaseComObject(obj);
        }
        catch (ArgumentException^ e) {
            System::Diagnostics::Debug::WriteLine(e->Message);
        }
        catch (ArgumentNullException^ e) {
            System::Diagnostics::Debug::WriteLine(e->Message);
        }
        catch (Exception^ e) {
            System::Diagnostics::Debug::WriteLine(e->Message);
        }
    }

ArgumentException in "FinalReleaseComObject" -> No COM Object. In Debugger, the object "obj" looks fine. Structure, data, all OK.

The following code will run, but have no effect:

    __declspec(dllexport) void ExecuteCcwGcWrapper(void* ComObject)
    {
        IntPtr ptr(ComObject);
        while (System::Runtime::InteropServices::Marshal::Release(ptr) > 0);
        //System::GC::AddMemoryPressure(0x7FFFFFFF);
        System::GC::Collect();
        //System::GC::WaitForFullGCComplete();
        System::GC::WaitForPendingFinalizers();
    }

(* I don't have access to the sources)

Ralph Erdt
  • 537
  • 4
  • 16
  • Hmya, one programmer's "memory leak" is another programmer's feature. The GC collects when it deems it necessary, this is not the kind of runtime environment where it thinks it is going to be necessary very often. That puts a big emphasis on getting `using` and Dispose() correct. Observe the collection counters in Perfmon.exe to get the lay of the land. And keep in mind that Marshal.FinalReleaseComObject() is very dangerous, you can easily commit suicide and destroy the object while you still have an interface pointer to it. Dangling pointers are nasty bugs to diagnose. – Hans Passant Sep 18 '17 at 09:18

1 Answers1

0

As discussed in an german forum CCW GC did not work:

System::GC::Collect(System::GC::MaxGeneration, System::GCCollectionMode::Forced, true);
System::GC::WaitForPendingFinalizers();
System::GC::Collect(System::GC::MaxGeneration, System::GCCollectionMode::Forced, true);

will do the trick.

A dirt solution: GC::GetTotalMemory.

Ralph Erdt
  • 537
  • 4
  • 16