2012-01-19 34 views
5

我有一個本地DLL下面的函數頭:元帥爲unsigned char *從DLL返回功能,在C#

unsigned char* Version_String() 

我想從C#項目稱呼它,我已經試過下面的調用(如在其他類似的問題在這裏找到):

[DllImport("BSL430.dll", CharSet=CharSet.Ansi)] 
public extern static UIntPtr Version_String(); 

,我不斷收到以下異常:

試圖讀取或寫入PROT記憶。這通常表明其他內存已損壞。

下一個嘗試是下面的,我也得到了同樣的異常:

[DllImport("BSL430.dll", CharSet=CharSet.Ansi)] 
[return : MarshalAs(UnmanagedType.LPStr)] 
public extern static string Version_String(); 

我似乎無法來解決這個問題。任何幫助將不勝感激!

編輯:

我不能在這裏給DLL的代碼,因爲它屬於的NDA,但功能我打電話看起來像這樣:

unsigned char versionString[50]; 

__declspec(dllexport) unsigned char* Version_String() 
{ 
    if(check_hardware_stuff()) 
    { 
     strcpy((char *) versionString, "version_string_bla_bla"); 
     versionString[5] = stuff; 
    } 
    else if (other_check()) 
    { 
     //will return empty string, that should be filled with '\0' 
    } 
    else 
    { 
     strcpy((char *) versionString, "ERROR"); 
    } 
    return versionString; 
} 

我不是特別喜歡DLL的實現,但我需要「實際上」使用它。 無論我如何處理返回值,只要我嘗試撥打VersionString(),就會得到異常。

+0

也許你正在使用錯誤的[調用約定](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention.aspx)? – svick

+0

你的DLL是用MultiByte字符集選項編譯的嗎? –

+1

使用調試器找出本機代碼崩潰的位置。項目+屬性,調試,勾選「啓用非託管代碼調試」。在C源代碼文件中設置函數的斷點。 –

回答

8

更新

已經看到了更新的問題,不同的意見,和你的本地函數的代碼,它很可能是當你調用check_hardware_stuff()的異常。調試很簡單。我會用這樣一個替換你的功能:

unsigned char versionString [50];

__declspec(dllexport) unsigned char* Version_String() 
{ 
    strcpy(versionString, "testing"); 
    return versionString; 
} 

如果仍然失敗,那麼我的猜測是,錯誤發生在你的DLL的DllMain提高。通過將上面的函數放入一個純粹的vanilla DLL中來進行調試,該DLL不會做任何其他事情

原來的答覆

調用約定是最明顯的問題。您的本機代碼很可能使用cdecl,但p/invoke默認值爲stdcall。更改您的P/Invoke簽名是這樣的:

[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)] 
public extern static IntPtr Version_String(); 

您可以放心地忽略CharSet參數,因爲沒有一個參數有文本,因爲你是治療的返回值作爲一個指針。

編輯: Hans在評論中正確指出,由於沒有參數,調用約定不匹配並不重要。所以這不是問題。

致電Marshal.PtrToStringAnsi轉換爲.net字符串。

string version = Marshal.PtrToStringAnsi(Version_String()); 

由於PtrToStringAnsi期待一個IntPtr參數我會建議你使用IntPtr你P的返回類型/調用簽名。

這一切都假定您的本機函數返回的內存在原生DLL中被分配和釋放。如果它是堆分配,並且你期望調用者釋放它,那麼你有一個小問題。你如何從C#中釋放內存,因爲你沒有訪問本地DLL的堆?

簡單的解決方案是使用共享的COM堆來分配內存。請致電CoTaskMemAlloc爲字符串分配緩衝區。然後聲明返回值的類型爲string,並且p/invoke編組將使用COM分配器釋放。

[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)] 
public extern static string Version_String(); 
... 
string version = Version_String(); 

當然,這隻適用於您返回希望調用方釋放的堆分配內存。

+0

感謝您的及時回覆。我嘗試了代碼,但我仍然遇到相同的例外情況。我看了一下DLL代碼,並從我可以告訴該字符串被全局聲明爲固定大小的數組:'unsigned char versionString [50];'。據我所知,在這種情況下不需要進一步分配,對嗎? – andreiscurei

+1

它不能是CallingConvention,函數不會帶任何參數。 –

+0

你在什麼時候得到例外?在調用DLL時,或者在使用返回的值進行操作時? –