1. char*(const char*)与string类型对应
1)C#传到C++
C++ :char*
C#: [MarshalAs(UnmanagedType.LPStr)] string
2)c# 传中文字符给c++ 出现乱码问题
解决办法:把string转为byte[] 再放到内存指针即可
byte[] encodedBytes=Encoding.UTF8.GetBytes("string");
IntPtr wordPtr = Marshal.AllocHGlobal(encodedBytes.Length);
Marshal.Copy(encodedBytes, 0, wordPtr, encodedBytes.Length);
3)C#接收C++返回的中文乱码
解决方法:收到的字符编码格式是Ansi,转成utf-8再转string
//C++返回IntPtr data
byte[] byte_copy = new byte[len];
Marshal.Copy(data, byte_copy, 0, (int)len);
string str = System.Text.Encoding.UTF8.GetString(byte_copy);
2. 数组传递注意事项
public static extern bool Render([MarshalAs(UnmanagedType.LPArray, SizeConst =23)]double[] data, [MarshalAs(UnmanagedType.LPArray, SizeConst = 23)]int[] colors);
千万注意,传递数组一定要加上MarshalAs标记,标记为传递数组,而且必须指定传递的数量,如果不标记数量,则每次只传递一个数值!
3.结构体传参
注意事项:1)不能忘记[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct msg_MsgNList
{
public UInt32 n;
public IntPtr item;
}
2)变量对应
//C++
struct Msg:public MsgHeader
{
uint 32 id;
}
//C#
struct Msg
{
public uint 32 id;
public MsgHeader msg_header;//一定要注意这两个变量的顺序,写错了是不行的,真是个大坑!!!!!
}
3)C++与C#传递的struct的Pack要设置
//C++
#pragma pack(push, 1)
typedef struct
{
MsgHeader header;
ID_t n;
} Msg;
//C#
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Msg
{
public MsgHeader header;
public UInt32 n;
}
4.C#调用C++DLL二级指针处理方式
//C++
const char** MGetAudioDevice()
{
const char **AudioDevices =new const char*[2];
for (int i = 0; i<2; i++) {
AudioDevices[i] = new char[128];
}
AudioDevices[0] = "hello";
AudioDevices[0] = "world";
return AudioDevices;
}
//C#
[DllImport("./Voip/VoipDLL/LIBTODLL.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr MGetAudioDevice();
//c#用结构体接受双指针的内容
public struct Node
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public string[] vs;
}
private void btn_login_Click(object sender, EventArgs e)
{
IntPtr ssss = VoIP.MGetAudioDevice();
Node node = (Node)Marshal.PtrToStructure(ssss,typeof(Node));
Console.WriteLine(node.vs[0] + node.vs[1]);
//最后释放掉
Marshal.FreeHGlobal(ssss);
}
5.C++调用C#回调函数–C#侧
//C++
// 传入函数指针
typedef void (*FUNC)(int,char*);
JNAAPI bool processCallback(int a,FUNC func);
//C#
public delegate void CallbackDelegate(int a,string param); //声明委托
// 接口定义
[DllImport("jnalib.dll", EntryPoint = "processCallback")]
public static extern bool processCallback(int a, CallbackDelegate call);
//调用示例
CallbackDelegate myDelegate = new CallbackDelegate(CallbackFunc); //需要传入回调函数名
processCallback(25, myDelegate); //最好声明委托实例为static,防止垃圾回收器的回收
然后我们就可以在CallbackFunc里面编写回调逻辑了,而JNA回调以interface作为参数,两者相差不大.
//C#
// 回调函数
public static void CallbackFunc(int a,string param)
{
Console.WriteLine("参数1: {0}", a);
Console.WriteLine("参数2:{0}", param);
}
6.对“xxx”类型的已垃圾回收委托进行了回调
对“xxx”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。 错误解决一例。
解决方法:keepAlive和设置为static,具体看代码
//出问题的代码
public class Node
{
public void run()
{
IntPtr param = IntPtr.Zero;
Init_callBack init_CallBack = new Init_callBack(node_init);
this._node.setInitCallback(init_CallBack, param);
}
}
//修正后的代码
public class Node
{
static Init_callBack init_CallBack;//关键的一句
public void run()
{
IntPtr param = IntPtr.Zero;
init_CallBack = new Init_callBack(node_init);
this._node.setInitCallback(init_CallBack, param);
GC.KeepAlive(init_CallBack);//关键的一句
}
}