简体   繁体   English

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

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

I have a C++ MFC application which consumes C# COM wrapper.我有一个 C++ MFC 应用程序,它消耗 C# COM 包装。 The issue is whenever I invoke a function inside wrapper, I am experiencing memory leak.问题是每当我在包装器内调用 function 时,我都会遇到 memory 泄漏。 Can anyone explain how to clean up the allocations that are made within the C# COM wrapper.谁能解释如何清理在 C# COM 包装器中进行的分配。

Below code blocks mimic what I was trying to do, can anyone provide me the references/rightway to pass the structure object/ clean up the memory allocation下面的代码块模仿了我试图做的事情,任何人都可以提供参考/传递结构对象的正确方法/清理 memory 分配

C# wrapper exposed as COM C# 包装器暴露为 COM

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++ code 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;
   }

You should release memory if it's allocated and no-one else frees it, obviously.你应该释放 memory 如果它被分配并且没有其他人释放它,显然。 Here, the allocated memory is the .NET's string[] and uint[] which are represented as SAFEARRAY* in the native world.这里,分配的 memory 是 .NET 的string[]uint[] ,在原生世界中表示为SAFEARRAY*

But, long story short: you can't really use structs as return type for COM methods.但是,长话短说:你不能真正使用结构作为 COM 方法的返回类型。 It's causes not only copy semantics issues (who owns struct's field memory, etc.), but in general, it won't even work depending on struct size, etc. lots of trouble, COM methods should return 32/64 bit-sized variables (or void).它不仅会导致复制语义问题(谁拥有结构的字段 memory 等),而且通常,它甚至不会根据结构大小等工作。很多麻烦,COM 方法应该返回 32/64 位大小的变量(或无效)。

So you can fix this using COM objects instead of structs.因此,您可以使用 COM 对象而不是结构来解决此问题。 For example:例如:

[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;
    }
}

and in C++ side:在 C++ 侧:

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

using namespace ManagedLib;

int main()
{
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    {
        ITestPtr test; // see https://stackoverflow.com/a/16382024/403671
        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;
}

Note: you can also use CComSafeArray to ease SAFEARRAY programming.注意:您也可以使用CComSafeArray来简化 SAFEARRAY 编程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM