我有一个 C++ MFC 应用程序,它使用 C# COM 包装器。问题是每当我调用包装器内的函数时,我都会遇到内存泄漏。谁能解释一下如何清理 C# COM 包装器中进行的分配。
下面的代码块模仿了我想要做的事情,任何人都可以为我提供引用/正确的方法来传递结构对象/清理内存分配
作为 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 方法的返回类型。这不仅会导致复制语义问题(谁拥有结构体的字段内存等),而且一般来说,它甚至无法根据结构体大小等工作。很麻烦,COM 方法应该返回 32/64 位大小的变量(或无效)。
因此您可以使用 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 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;
}
注意:您还可以使用CComSafeArray
简化SAFEARRAY
编程。
关于c# - 将对象从 C# COM 库传递到 C++ 应用程序时如何修复内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70362365/