使用swig director将字符串(const char *)参数从C ++传递给C#时,我面临内存泄漏。我在swig论坛中发现了类似question的一些有价值的建议,然而,缺少一组正确的类型图(即使在当前的swig版本2.0.11中,该问题仍未得到解决)。
经过几天的谷歌搜索和调查swig代码后,我终于编写了一组解决问题的字体图。
我希望这个问题和发布的答案会有所帮助。
答案 0 :(得分:2)
以下是char *的一组字体图,它为我完成了这项工作。
我的想法是将char *作为IntPtr传递给C#,然后使用InteropServices.Marshal.StringToHGlobalAnsi()
将其转换为C#字符串(C#字符串将被GC销毁)。类似地,当将字符串从C#传递给C ++时,我们将其转换为具有InteropServices.Marshal.StringToHGlobalAnsi()
方法的IntPtr(并确保在调用返回后最终销毁该对象)。
// Alternative char * typemaps.
%pragma(csharp) imclasscode=%{
public class SWIGStringMarshal : IDisposable {
public readonly HandleRef swigCPtr;
public SWIGStringMarshal(string str) {
swigCPtr = new HandleRef(this, System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(str));
}
public virtual void Dispose() {
System.Runtime.InteropServices.Marshal.FreeHGlobal(swigCPtr.Handle);
GC.SuppressFinalize(this);
}
~SWIGStringMarshal()
{
Dispose();
}
}
%}
%typemap(ctype) char* "char *"
%typemap(cstype) char* "string"
// Passing char* as an IntPtr to C# and then convert it to a C# string.
%typemap(imtype, out="IntPtr") char * "HandleRef"
%typemap(in) char* %{$1 = ($1_ltype)$input; %}
%typemap(out) char* %{$result = $1; %}
%typemap(csin) char* "new $imclassname.SWIGStringMarshal($csinput).swigCPtr"
%typemap(csout, excode=SWIGEXCODE) char *{
string ret = System.Runtime.InteropServices.Marshal.PtrToStringAnsi($imcall);$excode
return ret;
}
%typemap(csvarin, excode=SWIGEXCODE2) char * %{
set {
$imcall;$excode
} %}
%typemap(csvarout, excode=SWIGEXCODE2) char * %{
get {
string ret = System.Runtime.InteropServices.Marshal.PtrToStringAnsi($imcall);$excode
return ret;
} %}
%typemap(directorout) char* %{$result = ($1_ltype)$input; %}
%typemap(csdirectorout) char * "$cscall"
%typemap(directorin) char *
%{
$input = (char*)$1;
%}
%typemap(csdirectorin) char* "System.Runtime.InteropServices.Marshal.PtrToStringAnsi($iminput)";
PS。我在Windows 32/64以及Linux 64操作系统上测试了这些类型图。
答案 1 :(得分:0)
这是一个已知的错误,几年前在SWIG org中已有报道,请参见 https://github.com/swig/swig/issues/283和 https://github.com/swig/swig/issues/998
此错误尚未在最新版本中修复(直到现在是4.0.2版)。
该代码的错误在swig \ Lib \ csharp \ std_string.i中。有了这个错误,当使用字符串回调时,它将在我的应用程序的C ++中生成如下函数:
void SwigDirector_MyCode::LogDebug(std::string const &message) {
char * jmessage = 0 ;
if (!swig_callbackLogDebug) {
throw Swig::DirectorPureVirtualException("SDS::ISDSLogger::LogDebug");
} else {
jmessage = SWIG_csharp_string_callback((&message)->c_str());
swig_callbackLogDebug(jmessage);
}
}
LogDebug应该从C ++代码中调用。
在代码中,jmessage是新分配的内存。您可以验证它是否在调用之后更改了jmessage中的第一个字符,并且可以看到(&message)-> c_str()未被更改。
有想法说应该调用free()或delete来释放从SWIG_csharp_string_callback()创建的内存。
但这对我不起作用。我正在使用SWIG从C ++生成C#包装器。
我的解决方案是在我的项目.i文件中为std :: string const&类型定义新的typemap。
%typemap(directorin) const std::string & %{ $input = const_cast<char *>($1.c_str()); %}
所以我生成的代码变为:
void SwigDirector_MyCode::LogDebug(std::string const &message) {
char * jmessage = 0 ;
if (!swig_callbackLogDebug) {
throw Swig::DirectorPureVirtualException("MyCode::LogDebug");
} else {
jmessage = const_cast<char *>((&message)->c_str());
swig_callbackLogDebug(jmessage);
}
}
在此代码中,不再分配jmessage。
它已在VS2017的Debian 9 mono和Windows 7上进行了测试。