C# 调用C++封装的dll库函数的方法(二)

上一章讲述: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();

猜你喜欢

转载自blog.csdn.net/BYH371256/article/details/120413962
今日推荐