最近接手一個項目,主要是vc6的dll 給c#調用,就是為了用現成的dll,免得重新用c#開發
主要涉及參數傳遞和內存釋放的問題。
vc6部分==================
頭文件部分
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) char* getStringValue();
#ifdef __cplusplus
}
#endif
源文件部分
char* getStringValue()
{
....
//有人說要 static char *strdata = new char[size];
char *strdata = new char[size];
strncpy(strdata,"若干字符",size);
stardata[size-1] = '\0';
return strdata;
}
c#部分,一直不順利 用了 string 也用了 StringBuilder
以下是錯誤的
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { StringBuilder data = test.getNavigaTreeListValue(); Console.WriteLine("result: " + data); Console.ReadLine(); test.deleteGlobalDataPoint(data); } } class test { [DllImport("utwebcommlib.dll")] public static extern StringBuilder getNavigaTreeListValue( ); [DllImport("utwebcommlib.dll")] public static extern bool deleteGlobalDataPoint( StringBuilder buffer); } }
在運行時候總是遇到 讀取內存越界類似的錯誤 總以為getStringValue函數返回new的指針是不是有什么問題
但是這個東西在vs2008下是好的。到了vs2010就報異常。最后百度,在最后附上了解決方法的連接。
以下的才是正確的
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { IntPtr intPtr = test.getNavigaTreeListValue(); string data = Marshal.PtrToStringAnsi(intPtr); Console.WriteLine("result: " + data); Console.ReadLine(); test.deleteGlobalDataPoint(intPtr ); } } class test { [DllImport("utwebcommlib.dll")] public static extern IntPtr getNavigaTreeListValue(); [DllImport("utwebcommlib.dll")] public static extern bool deleteGlobalDataPoint(IntPtr buffer); [DllImport("utwebcommlib.dll")] public static extern int AddValue(); } }
非常感謝 。http://blog.csdn.net/shilogic/article/details/7027360
1:C# 調用 返回 字符串 C++ native dll 函數 的注意事項:
a:C++ DLL的返回值,安全的做法是分配一個全局 char 數組,把要返回的 char * 復制到這個 char 數組中,
char buff[255];
const char* __stdcall ReturnString()
{
strcpy(buff,"xxxxxxxxxxxxxxx");
return buff;
}
b:C# 收到 字符串后,需要 Marshal
[DllImport("VC.dll", EntryPoint = "ReturnString", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr ReturnString();
//調用VCDLL的代碼...
IntPtr intPtr = ReturnString();
string str = Marshal.PtrToStringAnsi(intPtr);
...
因為 C++ 返回的是 char* ,是個指針,所以c# 要用 IntPtr 來接回。
Marshal.PtrToStringAnsi MSDN上的解釋:將非托管 ANSI 字符串中第一個空值(空值就是\0)之前的所有字符復制到托管 String。將每個 ANSI 字符擴展為 Unicode 字符。
2:用參數傳遞,即C++dll 函數的參數 定義為 char*,而C#傳遞 StringBuilder 給 c++
a:c# 創建一個 StringBuilder,並初始化 capacity后傳給C++
[DllImport("VC.dll", EntryPoint = "ProcessString", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Winapi)]
public static extern void ProcessString(StringBuilder str);
//調用VCDLL的代碼...
StringBuilder str = new StringBuilder(255); //255 是 capacity
ProcessString(str);
MessageBox.Show(str); //不需要Marshal,直接使用
...
b:C++ DLL函數
const char* __stdcall ProcessString(char* str)
{
//str 是 c# 創建的 StringBuilder,長度是255
strcpy(str,"xxxxxxxxxxxxxxx");
return buff;
}
其他的請參考msdn中的c++與c#的類型轉換 對應關系如下:
C++ ---- C#
傳入的char* ----string
傳出的char* ---- StringBuilder(預分配空間)
short ----short
char ---- byte
char[n] ---- fixed byte[n]
結構指針 ----結構指針
函數指針 ---- 委托