2.1.4 C++和C#数据传递 ----class类型传递

2.1.4 C++和C#数据传递 ----class类型传递

1 C++ 动态库输出设定

  1. 定义一个struct CvPoint
  2. 定义一个class Region_Blob,描述一个Blob的外轮廓和内孔边缘信息
  3. 定义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#中动态库导入

  1. 定义一个抽象类 public abstract class DisposableObject : IDisposable
  2. 定义抽象类 public abstract class DisposableCvObject : DisposableObject
  3. 定义pInvoker.cs 用于导入 c++ dll
  4. 定义封装类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)
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

V言微语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值