2013-07-10 2 views
0

저는 C++ 응용 프로그램 용 C# GUI를 작성하고 있습니다. 사용 가능한 DLL을 열거하고, 하나씩 동적으로로드하고, 문자열에 연결된 플러그인 설명을 반환하는 "getdescstring"이라는 함수를 호출하여 플러그인의 세부 정보를 검색합니다.C++ DLL에서 모노 (C#)로 메모리 손상 전달 문자열

이것은 Windows에서 완벽하게 작동합니다. 그러나 리눅스에서 실행하려고 할 때 (Linux Mint 64 비트 (Mono 3.0.6) 및 Xubuntu 32 비트 (Mon 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와 충돌 할 수 있습니다. 그러나 이것들에 대한 호출 규칙 플래그가 없으므로 문제가 있더라도 해결할 수는 없습니다. CC를 Mono에서 stdcall로 설정할 수 있지만 g ++은 64 비트 CPU에서 CC 속성을 무시합니다. 아니요. 32 비트 가상 컴퓨터에서 양쪽 끝을 stdcall로 설정하려고 시도했지만 변경하지 않았습니다.

다음은 DLL에서 호출 된 코드입니다 (함수 "description"은 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에서 함수는 문자 * 및 매개 변수로 길이)에 동의합니다,하지만 결과는 더 악화했다 :

dsc descr = (dsc)Marshal.GetDelegateForFunctionPointer (proc, typeof(dsc)); 
IntPtr sf = Marshal.AllocHGlobal(1024);  
descr (sf,1024); 
String descstr = Marshal.PtrToStringAnsi(sf); 
Marshal.FreeHGlobal(sf); 

난 어떤 도움을 주셔서 감사합니다. 미리 감사드립니다.

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(); 
}  

를 대리자가 단순히 : 다음에 선언 당신은 descstr.c_str()을 반환하는

public delegate IntPtr dsc(); 

답변

0

가 도울 수 있다면 나는 또한, 여기에 DLL 로더 및 대리자 선언을 복사 한 스택을 getdescstring 함수에 추가하십시오. 함수가 리턴하자마자 참조하는 메모리는 유효하지 않습니다. descstr.c_str()을 범위 (예 : 글로벌 변수)로 유지하도록하십시오.

+0

입력 해 주셔서 감사합니다. 다음 코드를 작성했습니다 : 나는 그것이 못 생겼다는 것을 알고 있으며, 빨리 시도하고 싶습니다. 'char desc [2000]; extern "C"const char * getdescstring() { \t 벡터 <벡터 > descvec = 설명(); \t 문자열 descstr; 위한 \t (부호 I = 0; I)는 (descvec.size를 <; 내가 ++) 용 \t { \t \t (부호 J = 0; J

+0

'strncpy'는 문자열을 종료하지 않습니다. 사용하기 전에'memset'ting을 바이너리 0으로 시도하거나 그렇지 않으면 바이너리 0을 복사 된 문자열의 끝에 추가하십시오. – edtheprogrammerguy

+0

이것이 문제라면 끝 부분에 많은 쓰레기가있는 문자열 자체를 완전히 보았어야 했습니까? 그러나 쓰레기가 아닌 경우 반으로 줄입니다. 나는 그것을 시도했지만 결과는 같았다. –