C# 调用 C++

  最近搞了个小程序需要用到FFT,可是找来找去都没有一种C#下可靠地FFT代码,而且速度也不能令人满意。发现MIT做过一个很好的C++类库(http://www.fftw.org/)可以比较好的解决FFT的问题而且运算速度号称世界最快的(虽然未经验证,不过的确很快)。于是研究起在C#中调用C++的dll。

1. 在 C#中调用C++首先要包装,将C++中的方法包装成C#下的方法,下面这个不是我写的,但过程都是这个,"libfftw3-3.dll" 文件名"fftw_malloc"c++函数名,其他参数可以参照VS的帮助选择。记得 using System.Runtime.InteropServices ,包装后的方法必须是静态的,至于如何使用静态函数就不用多说了吧。

 1 /// <summary>
2 /// Contains the Basic Interface FFTW functions for double-precision (double) operations
3 /// </summary>
4 public class FFT
5 {
6   /// <summary>
7   /// Allocates FFTW-optimized unmanaged memory
8   /// </summary>
9   /// <param name="length">Amount to allocate, in bytes</param>
10   /// <returns>Pointer to allocated memory</returns>
11   [DllImport("libfftw3-3.dll",
12   EntryPoint = "fftw_malloc",
13   ExactSpelling = true,
14   CallingConvention = CallingConvention.Cdecl)]
15   public static extern IntPtr malloc(int length);
16 }

2,紧接着关键的问题来了,参数怎么设置?C++大量使用指针而在C#中没有指针。别急,看到上面个例子中的和函数返回值 IntPtr (using System)了吗?其实这个就是C#中的地址数据类型。调用中简单数据类型(如 int等)都可以直接使用的,少数(如string)要特殊处理都很容易,对于复杂的数据类型(类,数组...)就要用到这个 IntPtr 了。

   对于C++出现任何类型的指针在C#中都用 IntPtr 代替,但是如何使用呢?这里还涉及到一个代理 GCHandle (using System.Runtime.InteropServices)他可以帮我们在地址和对象之间进行转换,我举个例子。

1 IntPtr plan; 
2
3 double[] fin = new double[nx * ny * 2];
4 GCHandle hin = GCHandle.Alloc(fin, GCHandleType.Pinned);
5
6 plan = FFT.dft_2d(nx, ny, hin.AddrOfPinnedObject(), hin.AddrOfPinnedObject(), fftw_direction.Forward, fftw_flags.Estimate);

  dft_2d 原型:后面两个参数是枚举类型

1 public static extern IntPtr dft_2d(int nx, int ny, IntPtr input, IntPtr output, fftw_direction direction, fftw_flags flags);

 FFT.dft_2d 就是封装好的目标函数,我们就是要调用这个函数。fin是一个复杂对象,hin是我们的代理,Alloc操作就是将对象和内存绑定起来交给系统代理。hin.AddrOfPinnedObject()就是fin对应的地址(IntPtr类型)我们把它当做指针传给C++,这样C++会调用fin数据同时如果C++在函数内修改了hin代理的数据C#这边的数据也会随之变化,用C++的人很熟悉这种通过传入指针获得输出结果的方式吧。GCHandle 还有一个重要的属性Target,C#中可以直接通过这个属性获得代理的对象,这样我们就可以把对象换指针也可以把指针换成对象。

   简单说就是1.通过 GCHandle hin = GCHandle.Alloc(fin, GCHandleType.Pinned)绑定对象后通过hin.AddrOfPinnedObject()获得IntPtr型的地址给C++用

                 2.通过 hin.Target 获得对象给C#用

    注意:1.Alloc已经将对象绑定到内存了,如果之后再修改 fin 的对象 比如在第五行加入 fin = new double[2]对hin是无影响的,所以赋值操作一定要在Alloc之前执行。记得要为被绑定的对象赋初值,不然C++会报空指针的(new 一下不是很累吧!)。

 3 是不是很简单,不过我还是要在多说几句。既然用了C++和代理那么不要忘记释放内存哦,这点C#程序员很容易搞忘了。C++类库一般提供释放方法,GCHandle有Free()函数,谨记之!!!

   祝各位,学习进步,工作愉快,身体健康!

原文地址:https://www.cnblogs.com/GhostZCH/p/2437675.html