0

我正在为我的 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 没有内存泄漏或损坏。当我尝试通过 Valgrind 使用 Mono 运行整个程序时,它在初始化时崩溃,所以我无法对此进行报告。

所以我怀疑 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();
4

1 回答 1

0

您正在返回descstr.c_str()getdescstring函数的堆栈上声明的内容。一旦函数返回,它所引用的内存就无效了。尝试使它descstr.c_str()保持在范围内(例如作为全局变量)。

于 2013-07-10T19:46:34.840 回答