上一章讲述:C#调用C++封装的dll库函数,这里继续说明;
3、Marshal在C#中的应用(void *指针到IntPtr的转化),void *指针到IntPtr的复杂转化;
(1)C++定义结构体,在这里结构体A变得复杂一点,如果它内部包含一个指向另一个结构体B的指针
struct A
{
wchar_t osdbuffer[100];
unsigned short ix;
unsigned short iy;、
B *pB;
};
struct B
{
wchar_t title[20];
};
(2)C#结构体定义
定义和上述点2类似;
(3)C# 调用:在C#中你要做的也就稍微复杂一点,也就是说你不但要为A分配内存,也要为B分配内存;
B s_b = new B();
//赋值省略
int lenght1 = Marshal.SizeOf(s_b);
IntPtr pB= Marshal.AllocHGlobal(lenght1);
Marshal.StructureToPtr(s_b, pB, true);
A s_a = new A();
s_a.pB = pB;
//其他赋值
int lenght2 = Marshal.SizeOf(s_a);
IntPtr pA= Marshal.AllocHGlobal(lenght2);
Marshal.StructureToPtr(s_a, pA, true);
int type = 1;
int ret = SetConfig( type, pA);
Marshal.FreeHGlobal(pB);
Marshal.FreeHGlobal(pA);
万变不离其宗,只要掌握了原理,不管void *指针传递的参数有多么复杂,都可以搞定。
4、Char* 数据注意事项
const char* 直接换成string
char*做形参或返回值,需要换成IntPtr
char*做形参并想要获取char*内容,使用ref IntPtr无用。可以将该char*改为返回值获得。
在C++中,char*做形参传入,并在C#中获取该形参改变后的数据,则要使用对应类型为: StringBulider,而不是ref string或ref StringBuilder;
在C++中char* 参数的赋值要注意(sendbuff :传入的形参,char * aa = "aaaaaa"; strcpy(sendbuff, aa); 函数执行完成 sendbuff 数据才会改变)
测试验证:char*做形参,改变参数后C#获取数据,可用ref byte dataname;函数中写出 ref byte data[0];
5、Marshal (指针操作):方法集合,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。
主要用于:获取内存块中的数据;下面讲述常用的几个函数:
(1)从进程中,分配非托管内存:AllocHGlobal
AllocHGlobal(Int32) 通过使用指定的字节数,从进程的非托管内存中分配内存。
AllocHGlobal(IntPtr) 通过使用指向指定字节数的指针,从进程的非托管内存中分配内存。
(2)释放非托管内存:FreeHGlobal
IntPtr hglobal = Marshal.AllocHGlobal(100);
Marshal.FreeHGlobal(hglobal);
(3)Marshal.copy()方法用来在托管对象(数组)和非托管对象(IntPtr)之间进行内容的复制;该函数有很多的重载。具体的可查看微软相关文档
Copy(Single[], Int32, IntPtr, Int32) 将数据从一维托管单精度浮点数数组复制到非托管内存指针。
Copy(IntPtr[], Int32, IntPtr, Int32) 将数据从一维托管的 IntPtr 数组复制到非托管内存指针。
Copy(IntPtr, Single[], Int32, Int32) 将数据从非托管内存指针复制到托管单精度浮点数数组。
Copy(IntPtr, Int64[], Int32, Int32) 将数据从非托管内存指针复制到托管 64 位带符号整数数组。
Copy(IntPtr, Int32[], Int32, Int32) 将数据从非托管内存指针复制到托管 32 位带符号整数数组。
Copy(IntPtr, Int16[], Int32, Int32) 将数据从非托管内存指针复制到托管 16 位带符号整数数组。
Copy(IntPtr, Double[], Int32, Int32) 将数据从非托管内存指针复制到托管双精度浮点数数组。
Copy(IntPtr, IntPtr[], Int32, Int32) 将数据从非托管的内存指针复制到托管的 IntPtr 数组。
Copy(IntPtr, Byte[], Int32, Int32) 将数据从非托管内存指针复制到托管 8 位无符号整数数组。 常用
Copy(Int64[], Int32, IntPtr, Int32) 将数据从一维托管 64 位带符号整数数组复制到非托管内存指针。
Copy(Int32[], Int32, IntPtr, Int32) 将数据从一维托管 32 位带符号整数数组复制到非托管内存指针。
Copy(Int16[], Int32, IntPtr, Int32) 将数据从一维托管 16 位带符号整数数组复制到非托管内存指针。
Copy(IntPtr, Char[], Int32, Int32) 将数据从非托管内存指针复制到托管字符数组。
Copy(Double[], Int32, IntPtr, Int32) 将数据从一维托管双精度浮点数数组复制到非托管内存指针。
Copy(Char[], Int32, IntPtr, Int32) 将数据从一维托管字符数组复制到非托管内存指针。
Copy(Byte[], Int32, IntPtr, Int32) 将数据从一维托管 8 位无符号整数数组复制到非托管内存指针。
简单示例
IntPtr Temp = IntPtr.Zero;
int Width = srcBitmap.Width;
int Height = srcBitmap.Height;
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, Width, Height);
System.Drawing.Imaging.BitmapData srcBmData = srcBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
//创建Bitmap
System.Drawing.Bitmap dstBitmap = new System.Drawing.Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData dstBmData = dstBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
//位图中第一个像素数据的地址
IntPtr SrcPtr = srcBmData.Scan0;
IntPtr DstPtr = dstBmData.Scan0;
byte[] srcValues = new byte[src_bytes];
//复制SrcPtr所指的内存信息存放到byte数组中
Marshal.Copy(SrcPtr, srcValues, 0, src_bytes);
//将byte数组拷贝到位图中
Marshal.Copy(srcValues, 0, DstPtr, src_bytes);
(4)C# struct class 在Marshal.SizeOf 的区别:字节对齐问题
6、C++含回调函数,C#使用委托;下面是简单示例:
(1)dll声明和实现
typedef int (*pfCallBack)(int a, char b[]);//回调函数定义
pfCallBack CallBackfun = NULL;
extern "C" __declspec(dllexport) void SetCB(int (*pfCallBack)(int a, char b[]))
{
CallBackfun = pfCallBack;
}
char ch[100];
extern "C" __declspec(dllexport) void callTheCB()
{
int a = 4;
memset(ch, '\0', 100);
strcpy(ch, "aabbcc");
CallBackfun(a, ch);
}
(2)C# 定义和实现
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]//
public delegate int callBackHandler(int a, [MarshalAs(UnmanagedType.LPStr)]StringBuilder b);
callBackHandler fun;//
[DllImport("xxx.dll", CallingConvention =CallingConvention.Cdecl)]
public static extern unsafe void SetCB(callBackHandler fun1);
[DllImport("xxx.dll", CallingConvention =CallingConvention.Cdecl)]
public static extern unsafe void callTheCB();
int localFun(int a, [MarshalAs(UnmanagedType.LPStr)]StringBuilder b)
{
MessageBox.Show(b.ToString());
return 0;
}
fun = new callBackHandler(localFun);
SetCB(fun);
callTheCB();