Note that there are some explanatory texts on larger screens.

plurals
  1. POmemory corruption passing string from c++ dll to mono (C#)
    primarykey
    data
    text
    <p>I am writing a C# GUI for a C++ application of mine. I retrieve the details of a plugin by enumerating the available DLLs, dynamically loading them one by one, and calling a function called "getdescstring" which returns the plugin description concatenated into a string.</p> <p>This works flawlessly on Windows. However, when I try to run it on a Linux box (I have tried it on a Linux Mint 64-bit (Mono 3.0.6), and a Xubuntu 32-bit (Mono 2.10.8) virtual machine, g++-4.7 on both) most of the returned strings are corrupted like this:</p> <pre><code>"grown|Barabasi-Albert grown network.|1,000|1.0|1.0|1.0||n|Number of nodes|numeric|k|Degree of neA\0\0…" </code></pre> <p>If I call the function from a standalone C++ test program, it works. No memory leak or corruption with Valgrind. When I try to run the whole program with Mono through Valgrind, it crashes on initialization, so I can't report on that.</p> <p>So I suspect some memory corruption somewhere between Mono and the DLL, but I cannot identify the location.</p> <p><strong>Update:</strong> My intuition is that calling conventions get mixed up somehow. Since 64-bit programs have unique calling conventions, maybe Mono uses ms_abi, which may conflict with sysv_abi in Unix. However, there are no calling convention flags for these, so even if this it the problem, I cannot fix it. I can set the CC to stdcall in Mono, but g++ ignores any CC attibute on 64-bit CPUs. <strong>No. I tried setting the conventions to stdcall at both ends in a 32-bit virtual machine, but no change</strong></p> <p>Here is the code called in the DLL (the function "description" returns a std::vector of strings, but passing a std::vector directly to C# seemed quite nasty)</p> <pre><code>extern "C" const char* getdescstring() { vector&lt;vector&lt;string&gt; &gt; descvec=description(); string descstr; for(unsigned i=0;i&lt;descvec.size();i++) { for(unsigned j=0;j&lt;descvec[i].size();j++) { descstr+=descvec[i][j]; descstr+="|"; } descstr+="|"; } return descstr.c_str(); } </code></pre> <p>And this is the receiving function which transforms the given string back:</p> <pre><code>public static List&lt;List&lt;string&gt;&gt; GetPluginDesc(string plugin) { List&lt;List&lt;string&gt;&gt; des = new List&lt;List&lt;string&gt;&gt;(); 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&lt;string&gt; ls = new List&lt;string&gt;(); foreach (string s in descelem) { //double pipe means EOL if (s == "") { des.Add(ls); ls = new List&lt;string&gt;(); } else ls.Add(s); } if (ls.Count &gt; 0) des.Add(ls); dl.FreeLibrary(dllh); return des; } </code></pre> <p>I have also tried this approach (modifying the delegate and the function in the DLL to accept a char* and a length as a parameter), but the results were even worse, no sensible data was transmitted:</p> <pre><code>dsc descr = (dsc)Marshal.GetDelegateForFunctionPointer (proc, typeof(dsc)); IntPtr sf = Marshal.AllocHGlobal(1024); descr (sf,1024); String descstr = Marshal.PtrToStringAnsi(sf); Marshal.FreeHGlobal(sf); </code></pre> <p>I would appreciate any help. Thanks in advance!</p> <p>I have also copied the DLL loaders and the delegate declaration here, if it can help:</p> <pre><code>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(); } </code></pre> <p>the delegate is simply:</p> <pre><code>public delegate IntPtr dsc(); </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload