包装C++对象以用于C#PInvoke时的析构函数执行



我有一个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)的调用失败,请确保Devicenew实例为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;
}

最新更新