最近接手一个项目,主要是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]
结构指针 ----结构指针
函数指针 ---- 委托