0

私の C++ アプリケーション用に C# GUI を作成しています。プラグインの詳細を取得するには、利用可能な DLL を列挙し、それらを 1 つずつ動的にロードし、「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 に答える