搞C#开发的应该经常遇到需要调用C/C++的DLL,对于托管的库还好说,可以直接引用。非托管的就相对麻烦一些,需要DllImport导入,这时候就要参考头文件重写一遍所有的函数,以及函数中的枚举、结构体等。于是,问题就来了,C++中经常用到的指针该怎么传递给C#呢?下面告诉大家C#如何调用C/C++ dll中结构体内float*等指针。
C++ dll的头文件代码如下所示:
/// <summary>
/// 雷达帧数据
/// </summary>
struct FrameData
{
float* points; //点坐标x,y,z循环(x为行走方向,为当前距离值)
int pointsLen; //points指针数组长度
float distance; //当前距离(单位:m)
float circleX; //拟合圆心坐标X(单位:mm)
float circleY; //拟合圆心坐标Y(单位:mm)
};
/// <summary>
/// 绑定显示控件句柄
/// </summary>
/// <param name="hwd">控件句柄</param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
extern "C" _declspec(dllexport) void* _stdcall BindingHandle(HWND hwd, int width, int height);
/// <summary>
/// 传入数据
/// </summary>
/// <param name="frameDatas">帧数据</param>
/// <param name="pointNum">帧总数</param>
/// <returns></returns>
extern "C" _declspec(dllexport) int _stdcall EntryData(FrameData* frameDatas, int pointNum);
/// <summary>
/// 设置管道参数
/// </summary>
/// <param name="pipeDiameter">管道直径</param>
/// <returns></returns>
extern "C" _declspec(dllexport) int _stdcall SetPipePara(int pipeDiameter);
C#需要调用其中的方法时,对于float指针points这个参数,C#是不能够直接用float[]进行传递的,可以使用不安全代码unsafe构造同样的float*指针进行传递。也可以使用IntPtr。
public class PointCloudLib
{
/// <summary>
/// 雷达帧数据
/// </summary>
public unsafe struct FrameData
{
public float* points; //点坐标x,y,z循环(x为行走方向,为当前距离值)
// public IntPtr points;
public int pointsLen; //points数组长度
public float circleX; //拟合圆心坐标X(单位:mm)
public float circleY; //拟合圆心坐标Y(单位:mm)
public float radius; //拟合圆半径(单位:mm)
};
public const string dllPath = "ZYPointCloudLib.dll";
[DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr BindingHandle( IntPtr hwd, int width, int height);
[DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int EntryData(FrameData[] frameDatas, int pointNum);
[DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int SetPipePara(int pipeDiameter);
}
使用指针时,采用如下方法赋值,stackalloc 分配的内存有限:
PointCloudLib.FrameData[] frameDatas = new PointCloudLib.FrameData[lidData.frameDatas.Length];
unsafe
{
for (int i = 0; i < lidData.frameDatas.Length; i++)
{
float* p = stackalloc float[lidData.frameDatas[i].ValidDataCount * 3]; //stackalloc仅在局部变量的初始值设定项中有效,自动释放
frameDatas[i].points = p;
frameDatas[i].pointsLen = lidData.frameDatas[i].ValidDataCount * 3;
List<PointF> CoordinatePoints = DataProcess.DataProcessing(lidData.frameDatas[i].FrameData, 0, lidData.frameDatas[i].ValidDataCount, 0, 360, true);
int index = 0;
for (int j = 0; j < CoordinatePoints.Count; j++)
{
frameDatas[i].points[index] = lidData.frameDatas[i].Distance * 1000;
frameDatas[i].points[index + 1] = CoordinatePoints[j].X;
frameDatas[i].points[index + 2] = CoordinatePoints[j].Y;
index += 3;
}
frameDatas[i].circleX = lidData.frameDatas[i].CircleX;
frameDatas[i].circleY = lidData.frameDatas[i].CircleY;
frameDatas[i].radius = lidData.frameDatas[i].FittingCircleDia / 2;
}
bool ret = PointCloudLib.SetPipePara(lidData.headInfo.PipeSize[0]);
ret = PointCloudLib.EntryData(frameDatas, frameDatas.Length);
}
使用IntPtr时,使用如下方法赋值:
for (int i = 0; i < lidData.frameDatas.Length; i++)
{
List<PointF> CoordinatePoints = DataProcess.DataProcessing(lidData.frameDatas[i].FrameData, 0, lidData.frameDatas[i].ValidDataCount, 0, 360, true);
float[] points = new float[CoordinatePoints.Count * 3];
frameDatas[i].pointsLen = CoordinatePoints.Count * 3;
int index = 0;
for (int j = 0; j < CoordinatePoints.Count; j++)
{
points[index] = lidData.frameDatas[i].Distance * 1000;
points[index + 1] = CoordinatePoints[j].X;
points[index + 2] = CoordinatePoints[j].Y;
index += 3;
}
frameDatas[i].points = Marshal.AllocHGlobal(CoordinatePoints.Count * 3 * sizeof(float));
Marshal.Copy(points, 0, frameDatas[i].points, points.Length);
frameDatas[i].circleX = lidData.frameDatas[i].CircleX;
frameDatas[i].circleY = lidData.frameDatas[i].CircleY;
frameDatas[i].radius = lidData.frameDatas[i].FittingCircleDia / 2;
}
bool ret = PointCloudLib.SetPipePara(lidData.headInfo.PipeSize[0]);
ret = PointCloudLib.EntryData(frameDatas, frameDatas.Length);