我正在为我的C ++应用程序编写一个C#GUI。我通过枚举可用的DLL,逐个动态加载它们,并调用一个名为“getdescstring”的函数来检索插件的细节,该函数将连接的插件描述连接成一个字符串。
这在Windows上完美运行。但是,当我尝试在Linux机器上运行它时(我在Linux Mint 64位(Mono 3.0.6)和Xubuntu 32位(Mono 2.10.8)虚拟机上运行它,g ++ - 4.7 on两者)大多数返回的字符串都被破坏了:
"grown|Barabasi-Albert grown network.|1,000|1.0|1.0|1.0||n|Number of nodes|numeric|k|Degree of neA\0\0…"
如果我从独立的C ++测试程序中调用该函数,它就可以工作。 Valgrind没有内存泄漏或损坏。当我尝试使用Mono通过Valgrind运行整个程序时,它在初始化时崩溃,所以我无法报告。
所以我怀疑Mono和DLL之间存在某些内存损坏,但我无法确定位置。
更新 我的直觉是,调用约定会以某种方式混淆。由于64位程序具有唯一的调用约定,因此Mono可能使用ms_abi,这可能与Unix中的sysv_abi冲突。但是,这些没有调用约定标志,所以即使这是问题,我也无法修复它。我可以在Mono中将CC设置为stdcall,但g ++忽略了64位CPU上的任何CC属性。 否。我尝试在32位虚拟机的两端将约定设置为stdcall,但没有更改
这是在DLL中调用的代码(函数“description”返回一个字符串的std :: vector,但是将std :: vector直接传递给C#似乎非常讨厌)
extern "C" const char* getdescstring()
{
vector<vector<string> > descvec=description();
string descstr;
for(unsigned i=0;i<descvec.size();i++)
{
for(unsigned j=0;j<descvec[i].size();j++)
{
descstr+=descvec[i][j];
descstr+="|";
}
descstr+="|";
}
return descstr.c_str();
}
这是接收函数,它将给定的字符串转换回来:
public static List<List<string>> GetPluginDesc(string plugin)
{
List<List<string>> des = new List<List<string>>();
DllLoadUtils dl;
if (OS == platform.Windows) dl = new DllLoadUtilsWindows();
else dl = new DllLoadUtilsLinux();
IntPtr dllh = dl.LoadLibrary(plugin);
if (dllh == IntPtr.Zero) return null;
IntPtr proc = dl.GetProcAddress(dllh, "getdescstring");
if (proc == IntPtr.Zero) return null;
dsc descr = (dsc)Marshal.GetDelegateForFunctionPointer(proc, typeof(dsc));
String descstr = Marshal.PtrToStringAnsi(descr());
string[] descelem = descstr.Split('|');
List<string> ls = new List<string>();
foreach (string s in descelem)
{
//double pipe means EOL
if (s == "") { des.Add(ls); ls = new List<string>(); }
else ls.Add(s);
}
if (ls.Count > 0) des.Add(ls);
dl.FreeLibrary(dllh);
return des;
}
我也尝试过这种方法(修改DLL中的委托和函数以接受char *和长度作为参数),但结果更糟糕,没有传输合理的数据:
dsc descr = (dsc)Marshal.GetDelegateForFunctionPointer (proc, typeof(dsc));
IntPtr sf = Marshal.AllocHGlobal(1024);
descr (sf,1024);
String descstr = Marshal.PtrToStringAnsi(sf);
Marshal.FreeHGlobal(sf);
我将不胜感激任何帮助。 提前谢谢!
我还在这里复制了DLL加载器和委托声明,如果有帮助的话:
interface DllLoadUtils {
IntPtr LoadLibrary(string fileName);
void FreeLibrary(IntPtr handle);
IntPtr GetProcAddress(IntPtr dllHandle, string name);
}
public class DllLoadUtilsWindows : DllLoadUtils {
void DllLoadUtils.FreeLibrary(IntPtr handle) {
FreeLibrary(handle);
}
IntPtr DllLoadUtils.GetProcAddress(IntPtr dllHandle, string name) {
return GetProcAddress(dllHandle, name);
}
IntPtr DllLoadUtils.LoadLibrary(string fileName) {
return LoadLibrary(fileName);
}
[DllImport("kernel32")]
private static extern IntPtr LoadLibrary(string fileName);
[DllImport("kernel32.dll")]
private static extern int FreeLibrary(IntPtr handle);
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress (IntPtr handle, string procedureName);
}
internal class DllLoadUtilsLinux : DllLoadUtils {
public IntPtr LoadLibrary(string fileName) {
return dlopen(fileName, RTLD_NOW);
}
public void FreeLibrary(IntPtr handle) {
dlclose(handle);
}
public IntPtr GetProcAddress(IntPtr dllHandle, string name) {
// clear previous errors if any
dlerror();
var res = dlsym(dllHandle, name);
var errPtr = dlerror();
if (errPtr != IntPtr.Zero) {
MessageBox.Show("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
}
return res;
}
const int RTLD_NOW = 2;
[DllImport("libdl.so")]
private static extern IntPtr dlopen(String fileName, int flags);
[DllImport("libdl.so")]
private static extern IntPtr dlsym(IntPtr handle, String symbol);
[DllImport("libdl.so")]
private static extern int dlclose(IntPtr handle);
[DllImport("libdl.so")]
private static extern IntPtr dlerror();
}
代表就是:
public delegate IntPtr dsc();
答案 0 :(得分:0)
您将返回descstr.c_str()
函数中堆栈上声明的getdescstring
。函数返回后,它引用的内存无效。尝试制作descstr.c_str()
,使其保持在范围内(例如作为全局变量)。