2.1.4 C++和C#数据传递 ----class类型传递
1 C++ 动态库输出设定
- 定义一个struct CvPoint
- 定义一个class Region_Blob,描述一个Blob的外轮廓和内孔边缘信息
- 定义extern “C” __declspec(dllexport) 函数类型
1.1 设定一个BD_API宏
Windows环境下,动态库导出,需要指定extern “C” __declspec(dllexport) 修饰符
Windows环境下,动态库导入,需要指定extern “C” __declspec(dllimport) 修饰符
Linux环境下,无需指定修饰符
以下代码可以满足跨平台编译
#pragma once
#ifdef _WIN32
#ifdef BD_STATIC// 静态库
#define BAPI_EXPORTS
#else // 动态库
#ifdef BD_SHARED // 导入库
#define BAPI_EXPORTS __declspec(dllexport)
#else // 输出库
#define BAPI_EXPORTS __declspec(dllimport)
#endif // EXPORT_DLL_IMPORT
#endif // DEBUG
#else
#define BAPI_EXPORTS
#endif
#define BD_API extern "C" BAPI_EXPORTS
#define BD_API_CPLUS BAPI_EXPORTS
/**
*
* @brief
* 如果你在使用Opencv, uchar 是OpenCV 的头文件中被定义过的
* 如果没有使用Opencv,需要自己定义uchar
*/
typedef unsigned char uchar;
1.2 动态链接库头文件设定 dllexport_class.h
#pragma once
#include <bd_config.h>
#include <vector>
/// @brief 在头文件中 尽量不要用using namespace std; 因为有可能和其他头文件中 有些变量或者Enum 同名导致异常
namespace TEST_EXPORT
{
//stp0 定义个一struct CvPoint
/**
* ***********************************************************************************************
* @brief
* CvPoint C++ 中需要顶一个struct 类型
*
* ***********************************************************************************************
*/
struct CvPoint
{
int x;
int y;
};
// stp2 定义一个Region_Blob ,用于描述一个Blob区域,包含外轮廓,内孔等信息
/**
* ***********************************************************************************************
* @brief Region_Blob
*
*
* ***********************************************************************************************
*/
//在C++中,std::vector是一个自管理的容器,它会自动处理内存分配和释放。
//当你在std::vector中添加元素时,它会自动分配足够的内存来存储元素。
//当std::vector被销毁(例如,当它超出作用域,或者它是一个对象的成员并且该对象被销毁)时,
//它会自动销毁所有的元素并释放用于存储元素的内存。因此,你通常不需要担心std::vector会导致内存泄露。
// 但是如果vector中 元素是指针, 它不会自动处理
class BAPI_EXPORTS Region_Blob
{
public:
Region_Blob();
Region_Blob(const std::vector<CvPoint> &outer,const std::vector<CvPoint>& hole);
// ! the destructor
//virtual ~Region_Blob();
// 外轮廓
std::vector<CvPoint> outer_contour;
// 内轮廓
std::vector<CvPoint > hole_contour;
};
}
/// @brief 接口函数定义
// class 类的交互 都是使用 指针类型
// 1 create
// 2 dispose
// 3 setvalue
// 4 getvalue
namespace TEST_EXPORT
{
// create a new region_blob ptr
BD_API Region_Blob* create_new_region_blob();
// destroy a ptr of region_blob
BD_API void destroy_region_blob(Region_Blob* ptr);
// get value
// 获取外轮廓点的点数
BD_API int get_sizeof_outer_contours(Region_Blob* ptr);
// 获取外轮廓点的信息
BD_API int get_outer_contour(Region_Blob* ptr,CvPoint * pts, const int & size);
// 获取孔轮廓点的点数
BD_API int get_sizeof_hole_contours(Region_Blob* ptr);
// 获取孔轮廓点的信息
BD_API int get_hole_contour(Region_Blob* ptr,CvPoint * pts, const int & size);
//setvalue
BD_API void reset(Region_Blob* ptr);
//外轮廓点赋值
BD_API void set_outer_contour(Region_Blob* ptr,CvPoint * pts, const int & size);
//孔轮廓点赋值
BD_API void set_hole_contour(Region_Blob* ptr,CvPoint * pts, const int & size);
}
1.3 动态链接库函数实现 dllexport_class.cpp
#include "dllexport_class.h"
#include<string.h>
#include <iostream>
#include <math.h>
#include <algorithm>
//stp1 Region_Blob的实现
namespace TEST_EXPORT
{
BAPI_EXPORTS Region_Blob::Region_Blob():
outer_contour(),hole_contour()
{ }
BAPI_EXPORTS Region_Blob::Region_Blob(const std::vector<CvPoint> &outer,const std::vector<CvPoint>& hole):
outer_contour(outer),hole_contour(hole)
{ }
}
/// @brief 接口函数定义
// class 类的交互 都是使用 指针类型
// 1 create
// 2 dispose
// 3 setvalue
// 4 getvalue
namespace TEST_EXPORT
{
// create a new region_blob ptr
BD_API Region_Blob* create_new_region_blob()
{
return new Region_Blob();
}
// destroy a ptr of region_blob
BD_API void destroy_region_blob(Region_Blob* ptr)
{
if(ptr==nullptr)return ;
delete ptr;
// delete region_blob时 ,会自动销毁outer_contour|hole_contour;
}
// get value
// 获取外轮廓点的点数
BD_API int get_sizeof_outer_contours(Region_Blob* ptr)
{
if(ptr==nullptr)return 0;
return static_cast<int>(ptr->outer_contour.size());
}
// 获取外轮廓点的信息
BD_API int get_outer_contour(Region_Blob* ptr,CvPoint * pts, const int & size)
{
int nRet=0;// 错误信息
if(ptr==nullptr)return -1;
int sz = size;
if(sz > ptr->outer_contour.size())sz =static_cast<int>(ptr->outer_contour.size());
//total 字节长度 CvPoint有X,Y =2 int,每个int=4 byte
int len = sz * 2 * 4;
memcpy(pts, ptr->outer_contour.data(), len);
return nRet;
}
BD_API int get_sizeof_hole_contours(Region_Blob* ptr)
{
if(ptr==nullptr)return 0;
return static_cast<int>(ptr->hole_contour.size());
}
// 获取孔轮廓点的信息
BD_API int get_hole_contour(Region_Blob* ptr,CvPoint * pts, const int & size)
{
int nRet = 0;// 错误信息
if (ptr == nullptr)return -1;
int sz = size;
if (sz > ptr->hole_contour.size())sz = static_cast<int>(ptr->hole_contour.size());
//total 字节长度 CvPoint有X,Y =2 int,每个int=4 byte
int len = sz * 2 * 4;
memcpy(pts, ptr->hole_contour.data(), len);
return nRet;
}
//setvalue
BD_API void reset(Region_Blob* ptr)
{
if (ptr == nullptr)return ;
ptr->outer_contour.clear();
ptr->hole_contour.clear();
}
//外轮廓点赋值
BD_API void set_outer_contour(Region_Blob* ptr,CvPoint * pts, const int & size)
{
if (ptr == nullptr)return;
reset(ptr);
ptr->outer_contour = std::vector<CvPoint>(pts, pts + size);
}
//孔轮廓点赋值
BD_API void set_hole_contour(Region_Blob* ptr,CvPoint * pts, const int & size)
{
if (ptr == nullptr)return;
reset(ptr);
ptr->hole_contour = std::vector<CvPoint>(pts, pts + size);
}
}
2 C#中动态库导入
- 定义一个抽象类 public abstract class DisposableObject : IDisposable
- 定义抽象类 public abstract class DisposableCvObject : DisposableObject
- 定义pInvoker.cs 用于导入 c++ dll
- 定义封装类wraper.cs ,用于封装一定对应于C++ 的类,管理创建,赋值,销毁
2.1 DisposableObject 类声明 ,请参考OpencvSharp
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace OpenCvSharp
{
/// <summary>
/// Represents a class which manages its own memory.
/// </summary>
public abstract class DisposableObject : IDisposable
{
/// <summary>
/// Gets or sets a handle which allocates using cvSetData.
/// </summary>
protected GCHandle dataHandle;
private volatile int disposeSignaled = 0;
#region Properties
/// <summary>
/// Gets a value indicating whether this instance has been disposed.
/// </summary>
public bool IsDisposed { get; protected set; }
/// <summary>
/// Gets or sets a value indicating whether you permit disposing this instance.
/// </summary>
public bool IsEnabledDispose { get; set; }
/// <summary>
/// Gets or sets a memory address allocated by AllocMemory.
/// </summary>
protected IntPtr AllocatedMemory { get; set; }
/// <summary>
/// Gets or sets the byte length of the allocated memory
/// </summary>
protected long AllocatedMemorySize { get; set; }
#endregion
#region Init and Dispossal
/// <summary>
/// Default constructor
/// </summary>
protected DisposableObject()
: this(true)
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="isEnabledDispose">true if you permit disposing this class by GC</param>
protected DisposableObject(bool isEnabledDispose)
{
IsDisposed = false;
IsEnabledDispose = isEnabledDispose;
AllocatedMemory = IntPtr.Zero;
AllocatedMemorySize = 0;
}
/// <summary>
/// Releases the resources
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the resources
/// </summary>
/// <param name="disposing">
/// If disposing equals true, the method has been called directly or indirectly by a user's code. Managed and unmanaged resources can be disposed.
/// If false, the method has been called by the runtime from inside the finalizer and you should not reference other objects. Only unmanaged resources can be disposed.
/// </param>
private void Dispose(bool disposing)
{
#pragma warning disable 420
// http://stackoverflow.com/questions/425132/a-reference-to-a-volatile-field-will-not-be-treated-as-volatile-implications
if (Interlocked.Exchange(ref disposeSignaled, 1) != 0)
{
return;
}
IsDisposed = true;
if (IsEnabledDispose)
{
if (disposing)
{
DisposeManaged();
}
DisposeUnmanaged();
}
}
/// <summary>
/// Destructor
/// </summary>
~DisposableObject()
{
Dispose(false);
}
/// <summary>
/// Releases managed resources
/// </summary>
protected virtual void DisposeManaged()
{
}
/// <summary>
/// Releases unmanaged resources
/// </summary>
protected virtual void DisposeUnmanaged()
{
if (dataHandle.IsAllocated)
{
dataHandle.Free();
}
if (AllocatedMemorySize > 0)
{
GC.RemoveMemoryPressure(AllocatedMemorySize);
AllocatedMemorySize = 0;
}
if (AllocatedMemory != IntPtr.Zero)
{
Marshal.FreeHGlobal(AllocatedMemory);
AllocatedMemory = IntPtr.Zero;
}
}
#endregion
#region Methods
/// <summary>
/// Pins the object to be allocated by cvSetData.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
protected internal GCHandle AllocGCHandle(object obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
if (dataHandle.IsAllocated)
dataHandle.Free();
dataHandle = GCHandle.Alloc(obj, GCHandleType.Pinned);
return dataHandle;
}
/// <summary>
/// Allocates the specified size of memory.
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
protected IntPtr AllocMemory(int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
if (AllocatedMemory != IntPtr.Zero)
Marshal.FreeHGlobal(AllocatedMemory);
AllocatedMemory = Marshal.AllocHGlobal(size);
NotifyMemoryPressure(size);
return AllocatedMemory;
}
/// <summary>
/// Notifies the allocated size of memory.
/// </summary>
/// <param name="size"></param>
protected void NotifyMemoryPressure(long size)
{
// マルチスレッド動作時にロックがかかるらしい。いったん廃止
if (!IsEnabledDispose)
return;
if (size == 0)
return;
if (size <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
if (AllocatedMemorySize > 0)
GC.RemoveMemoryPressure(AllocatedMemorySize);
AllocatedMemorySize = size;
GC.AddMemoryPressure(size);
}
/// <summary>
/// If this object is disposed, then ObjectDisposedException is thrown.
/// </summary>
public void ThrowIfDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
#endregion
}
}
2.2 disposableobject.cs类声明 ,请参考OpencvSharp
用于管理销毁 非托管的 指针
using System;
namespace OpenCvSharp
{ /// <summary>
/// DisposableObject + ICvPtrHolder
/// </summary>
public abstract class DisposableCvObject : DisposableObject
{
/// <summary>
/// Data pointer
/// </summary>
protected IntPtr ptr;
#region Init and Dispose
/// <summary>
/// Default constructor
/// </summary>
protected DisposableCvObject()
: this(true)
{
}
protected DisposableCvObject(IntPtr ptr)
: this(ptr, true)
{
}
/// <summary>
///
/// </summary>
/// <param name="isEnabledDispose"></param>
protected DisposableCvObject(bool isEnabledDispose)
: this(IntPtr.Zero, isEnabledDispose)
{
}
/// <summary>
///
/// </summary>
/// <param name="ptr"></param>
/// <param name="isEnabledDispose"></param>
protected DisposableCvObject(IntPtr ptr, bool isEnabledDispose)
: base(isEnabledDispose)
{
this.ptr = ptr;
}
/// <summary>
/// releases unmanaged resources
/// </summary>
protected override void DisposeUnmanaged()
{
ptr = IntPtr.Zero;
base.DisposeUnmanaged();
}
#endregion
/// <summary>
/// Native pointer of OpenCV structure
/// </summary>
public IntPtr CvPtr
{
get
{
ThrowIfDisposed();
return ptr;
}
}
}
}
2.3 通过DllImport 申明C++ dll 中的函数原型 文件pinvoke.cs
注意:输出struct数组数据时,使用[out ] CvPoint[],或者 ref CvPoint 方式传递地址
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace dllimport_class
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct CvPoint
{
public int x;
public int y;
public CvPoint(int _x, int _y)
{
x = _x; y = _y;
}
public string toString()
{
string str = "(" + x.ToString() + "," + y.ToString() + ")";
return str;
}
}
public static partial class Nativemethods_bd
{
public const string DllExtern = "dllexport_classd.dll";
public const string Version = "400";
// create a new region_blob ptr
[DllImport(DllExtern, ExactSpelling = true)]
public static extern IntPtr create_new_region_blob();
// destroy a ptr of region_blob
[DllImport(DllExtern, ExactSpelling = true)]
public static extern void destroy_region_blob(IntPtr ptr);
// get value
// 获取外轮廓点的点数
[DllImport(DllExtern, ExactSpelling = true)]
public static extern int get_sizeof_outer_contours(IntPtr ptr);
// 获取外轮廓点的信息
[DllImport(DllExtern, ExactSpelling = true)]
// 或者使用 关键字[In,Out] CvPoint [] pts,进行struct数组输出
public static extern int get_outer_contour(IntPtr ptr, ref CvPoint pts, int size );
// 获取孔轮廓点的点数
[DllImport(DllExtern, ExactSpelling = true)]
public static extern int get_sizeof_hole_contours(IntPtr ptr);
// 获取孔轮廓点的信息
[DllImport(DllExtern, ExactSpelling = true)]
// 或者传递[In,Out] CvPoint [] pts_out
// 或者传递ref CvPoint pts_out[0]
public static extern int get_hole_contour(IntPtr ptr, ref CvPoint pts, int size);
//setvalue
[DllImport(DllExtern, ExactSpelling = true)]
public static extern void reset(IntPtr ptr);
//外轮廓点赋值
[DllImport(DllExtern, ExactSpelling = true)]
public static extern void set_outer_contour(IntPtr ptr, CvPoint[] pts, int size );
//孔轮廓点赋值
[DllImport(DllExtern, ExactSpelling = true)]
public static extern void set_hole_contour(IntPtr ptr, CvPoint[] pts, int size);
}
}
2.4 wrapper.cs 封装对应C++ 中类,进行非托管的内存的管理,赋值,销毁
//wrapper 对pInvoke函数进行进一步的封装
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;
namespace dllimport_class
{
public class RegionBlob_Wrapper : DisposableCvObject
{
private bool dispose = true;
public List<CvPoint> outer;
public List<CvPoint> hole;
public RegionBlob_Wrapper()
{
ptr = Nativemethods_bd.create_new_region_blob();
outer = new List<CvPoint>();
hole = new List<CvPoint>();
}
public RegionBlob_Wrapper(List<CvPoint> _outer, List<CvPoint> _hole)
{
ptr = Nativemethods_bd.create_new_region_blob();
outer = _outer;
hole = _hole;
}
public RegionBlob_Wrapper(IntPtr _ptr)
{
this.ptr = _ptr;
get_region_pts();
}
public RegionBlob_Wrapper(IntPtr _ptr, bool _dispose = true)
{
this.ptr = _ptr;
get_region_pts();
dispose = _dispose;
}
/// <summary>
/// Releases unmanaged resources
/// </summary>
protected override void DisposeUnmanaged()
{
// 目的,防止 引用一个外部创建的 C++ ptr
if (dispose)
Nativemethods_bd.destroy_region_blob(ptr);
base.DisposeUnmanaged();
}
public void get_region_pts()
{
if (ptr == IntPtr.Zero) return;
try
{
// get outer pts
int size = Nativemethods_bd.get_sizeof_outer_contours(ptr);
CvPoint[] pts_outer = new CvPoint[size];
Nativemethods_bd.get_outer_contour(ptr, pts_outer, size);
outer.Clear();
outer.AddRange(pts_outer);
// get hole pts
int size1 = Nativemethods_bd.get_sizeof_hole_contours(ptr);
CvPoint[] pts_hole= new CvPoint[size];
Nativemethods_bd.get_outer_contour(ptr, pts_hole, size);
hole.Clear();
hole.AddRange(pts_hole);
// 销毁数组
Array.Clear(pts_outer,0, pts_outer.Length);
Array.Clear(pts_hole, 0, pts_hole.Length);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return ;
}
private void set_region_pts( )
{
if (ptr == IntPtr.Zero) return;
try
{
// get outer pts
int size = outer.Count;
if (size > 0)
{
CvPoint[] pts_outer = outer.ToArray() ;
Nativemethods_bd.set_outer_contour(ptr, pts_outer, size);
Array.Clear(pts_outer, 0, pts_outer.Length);
}
// get hole pts
int size1 = hole.Count;
if (size1 > 0)
{
CvPoint[] pts_hole = hole.ToArray();
Nativemethods_bd.set_outer_contour(ptr, pts_hole, size);
// 销毁数组
Array.Clear(pts_hole, 0, pts_hole.Length);
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return;
}
}
}
2.5 main 函数中调用函数 文件 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace dllimport_class
{
class Program
{
static void Main(string[] args)
{
// 创建outer
List<CvPoint> outer = new List<CvPoint>();
outer.Add(new CvPoint(1,1));
outer.Add(new CvPoint(2, 2));
outer.Add(new CvPoint(3, 3));
outer.Add(new CvPoint(1, 1));
List<CvPoint> hole = new List<CvPoint>();
/// 使用using ,IDE 会自动帮我们销毁变量
using (var region = new RegionBlob_Wrapper(outer, hole))
{
region.get_region_pts();
for (int i=0;i<region.outer.Count;i++)
{
Console.WriteLine("class import test ,outer pt_{0}={1}\t",i, region.outer[i].toString());
}
}
Console.ReadKey();
}
}
}
3 结果
class import test ,outer pt_0=(1,1)
class import test ,outer pt_1=(2,2)
class import test ,outer pt_2=(3,3)
class import test ,outer pt_3=(1,1)