我有一个C++类,我想在C#中使用它。为此,我试图编写另一个C++dll,用可调用函数(使用"extern C和__declspec(dllexport)")包装这个类(它是另一个库的一部分)。我的想法是保留一个指向我的对象的指针,并将其发送到包装器dll中的函数,然后从那里调用该对象的方法。这看起来很好,但当对象有解构主义者时,问题就会发生。
这是我的C++包装器代码:(设备是我的C++类/对象)
__declspec(dllexport) Status Device_open(Device* di, const char* uri)
{
Device dl;
Status status = dl.open(uri);
di = &dl;
return status;
}
__declspec(dllexport) void Device_Close(Device* di)
{
di->close();
}
这是我的C#包装代码:
[DllImport("Wrapper.dll")]
static extern Status Device_open(ref IntPtr objectHandler, IntPtr uri);
public static Device Open(string uri)
{
IntPtr handle = IntPtr.Zero;
Device_open(ref handle, Marshal.StringToHGlobalAnsi(uri));
return new Device(handle);
}
[DllImport("Wrapper.dll")]
static extern void Device_Close(IntPtr objectHandler);
public void Close()
{
Device_Close(this.Handle);
}
以下是C#应用程序中的测试代码:
Device d = Device.Open(di.URI);
d.Close();
每件事都是好的。这里的问题是,当我请求打开一个新设备时,主C++对象的解构器将被执行,所以我的关闭请求总是返回异常(因为它已经关闭或被破坏);
我能做些什么来防止这种情况发生?!
Device
正在被销毁,因为它超出了Device_open()
函数末尾的作用域。要解决此问题,请使用new
动态分配Device
实例,这样就可以控制dl
的生存期。然后是Device_Close()
函数中的delete dl;
。
请注意,C++函数正在将地址分配给该函数本地的Device*
,调用方不会看到该地址。为了解决这个问题,在C++方面,您可以通过引用传递指针:
__declspec(dllexport) Status Device_open(Device*& di, const char* uri)
或者你可以通过Device**
。
__declspec(dllexport) Status Device_open(Device** di, const char* uri)
然而,我不确定这将如何影响c端。
为了防止任何内存泄漏,如果对dl.open(url)
的调用失败,请确保Device
的new
实例为delete
d:
__declspec(dllexport) Status Device_open(Device*& di, const char* uri)
{
Status status;
try
{
Device* dl = new Device();
status = dl->open(uri);
if (status != OK)
{
delete dl;
}
else
{
di = dl;
}
}
catch (std::bad_alloc const&)
{
status = BAD_ALLOC; // Or equivalent failure reason.
}
return status;
}
__declspec(dllexport) void Device_Close(Device* di)
{
// di->close(); Uncomment if destructor does not close().
delete di;
}