将对象从 C# COM 库传递到 C++ 应用程序时如何修复内存泄漏

How to fix memory leak when passing object from C# COM library to a C++ application

我有一个使用 C# COM 包装器的 C++ MFC 应用程序。问题是每当我在包装器中调用一个函数时,我都会遇到内存泄漏。任何人都可以解释如何清理在 C# COM 包装器中进行的分配。

下面的代码块模仿了我试图做的事情,谁能给我提供 references/rightway 来传递结构对象/清理内存分配

作为 COM 公开的 C# 包装器

using System;
using System.Runtime.InteropServices;


namespace ManagedLib
{
    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct comstructure
    {
        
        public string[] m_strName;
         
        public UInt32[] m_nEventCategory;
    }


    [Guid("4BC57FAB-ABB8-4b93-A0BC-2FD3D5312CA8")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [ComVisible(true)]
    public interface ITest
    {
        
        comstructure  TestBool();

         
    }

    [Guid("A7A5C4C9-F4DA-4CD3-8D01-F7F42512ED04")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]    
    public class Test : ITest
    {
        public comstructure TestBool( )
        {
            comstructure testvar = new comstructure();

            testvar.m_strName = new string[100000];
            testvar.m_nEventCategory = new UInt32[100000];
            return testvar;
        }
                 
    }
}

C++代码


#include <iostream>
#include <afx.h>
#include <afxwin.h>         
#include <afxext.h>         
#include <afxdtctl.h>   
#include "windows.h"
#include "psapi.h"
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>          
#endif // _AFX_NO_AFXCMN_SUPPORT

#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS  
#include <atlbase.h>
#import "..\comlibrary\bin\Debug\comlibrary.tlb"
comlibrary::ITest* obj;
class mleak
{

public:


   void leakmemory()
   {
       comlibrary::comstructure v2;
       v2 = obj->TestBool();

       
   }
};
   int main()
   {


       CoInitializeEx(nullptr, COINIT_MULTITHREADED);
       CLSID clsid;
       HRESULT hResult = ::CLSIDFromProgID(L"ManagedLib.Test", &clsid);
       hResult = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
           __uuidof(comlibrary::ITest), (void**)&obj);
       std::cout << hResult;
       if (FAILED(hResult))
       {
           std::cout << "COM import failed!\n";
       }
        

       mleak m1;
       for (int i = 0; i < 600; i++)
       {
            
           m1.leakmemory();
           Sleep(100);
       }

       return 0;
   }

显然,如果内存已分配且没有其他人释放它,您应该释放它。在这里,分配的内存是 .NET 的 string[]uint[],在本地世界中表示为 SAFEARRAY*

但是,长话短说:您不能真正将结构用作 COM 方法的 return 类型。它不仅会导致复制语义问题(谁拥有结构的字段内存等),而且通常,它甚至不会根据结构大小等工作。很多麻烦,COM 方法应该 return 32/64位大小的变量(或 void)。

所以您可以使用 COM 对象而不是结构来解决这个问题。例如:

[ComVisible(true)]
public interface IOther
{
    string[] Names { get; set; }
    uint[] EventCategories { get; set; }
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class Other : IOther
{
    public string[] Names { get; set; }
    public uint[] EventCategories { get; set; }
}

[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITest
{
    Other TestOther();
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class Test : ITest
{
    public Other TestOther()
    {
        var other = new Other();
        other.Names = new string[100000];
        other.EventCategories = new UInt32[100000];
        return other;
    }
}

在 C++ 方面:

#include "windows.h"
#import "..\ManagedLib\bin\Debug\ManagedLib.tlb"

using namespace ManagedLib;

int main()
{
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    {
        ITestPtr test; // see 
        auto hr = test.CreateInstance(__uuidof(Test));
        if (SUCCEEDED(hr))
        {
            IOtherPtr other(test->TestOther());
            auto names = other->Names;

            // do what you want with safe array here
            // but in the end, make sure you destroy it
            SafeArrayDestroy(names);
        }
    }
    CoUninitialize();
    return 0;
}

注意:您也可以使用 CComSafeArray 来简化 SAFEARRAY 编程。