開發環境:
操作系統:windows 7
IDE:Microsoft Visual Studio Professional 2015
SWIG: 3.0.12
swig的介紹
詳細介紹可看官網,一下貼出官網上的原話:
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of target languages including common scripting languages such as Javascript, Perl, PHP, Python, Tcl and Ruby. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), D, Go language, Java including Android, Lua, Modula-3, OCAML, Octave, Scilab and R. Also several interpreted and compiled Scheme implementations (Guile, MzScheme/Racket, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG is typically used to parse C/C++ interfaces and generate the ‘glue code’ required for the above target languages to call into the C/C++ code. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG is free software and the code that SWIG generates is compatible with both commercial and non-commercial projects.
以下是我的理解(注:以下這段話並不是上段話的翻譯):
Swig主要是為了將c++/c中的代碼所實現的功能移植到別的語言上。一般我們要將c++/c移植到別的語言上,基本操作是將c++中的實現代碼轉變成dll,然后再供別的語言調用,這樣安全性高,且易於調用。但是因為各種語言不相同,如果不借助swig,自己去封裝。在調用dll時就會有很多類型(包括基本類型,結構體和類)轉換需要注意,比如c#調c++ dll,可能得自己造一個相對應與c++的結構體和類才能成功調用。但是如果我們用了swig,這些我們都不用考慮,我們只需要考慮swig給我們的接口類型是什么,然后我們只要按照swig給的接口類型傳入參數,就OK了。說白了就是swig代替了我們利用c#去重新定義dll中所需要傳入的參數類型(如結構體或類)這個工作。
swig實現步驟
swig的安裝:
進入如下網址:http://www.swig.org/download.html
由於我是windows系統,所以安裝如下版本:
下載后,將其解壓至制定目錄,如:D:\SDK\swigwin-3.0.12
1、建立如下c++項目(應用程序類型選擇dll類型)和c#項目,還沒實現代碼,都是空項目。主要有.h .cpp .idl .cs文件(具體這四個文件怎么創建我就不贅述了,想必能找到這篇博文就說明已具備了創建此文件的能力)。
2、填寫代碼:
(1) c++_file.idl:
(網上說的都是.i文件,其實就是vs里的.idl文件)
%module cppdll %{ /* 在包裝代碼中包含頭文件 */ #include "c++_file.h" %} /* 解析頭文件生成包裝代碼 */ %include "c++_file.h"
(2) c++_file.cpp
#include "c++_file.h" int CPlusPlusClass::Add(int a, int b) { return a + b; }
(3)c++_file.h
#pragma once class CPlusPlusClass { public: // 測試對C++模塊中定義的Class的調用 int Add(int a, int b); };
(4)Program.cs
CPlusPlusClass fromCPlusPlus = new CPlusPlusClass(); Console.WriteLine("1+2=" + fromCPlusPlus.Add(1,2)); Console.ReadKey();
3、build .idl接口文件。在.cpp所在目錄下用命令框輸入
D:\SDK\swigwin-3.0.12\swig.exe -csharp -c++ c++_file.idl
回車后會發現當前文件夾中出現以下幾個文件:
4、分別在c++項目和c#項目中引用各自需要的文件,引用完后,應該和如下圖一樣。
5、在生成dll之前,先將.idl文件的屬性改為如下圖所示:
6、生成c++ dll:重新生成 c++項目即可,在debug文件夾中會發現有一個c++_project.dll文件,說明生成成功。
7、運行c#項目,將c#項目設為啟動項。將c++_project.dll該名為.idl文件中的module名(cppdll.dll),並放入bin/debug/中.如下圖所示:
8、然后直接運行可得如下結果:
swig需要注意的幾點:
- 裝swig時需注意:windows安裝swigwin版本的,否則會找不到swig命令。
- “cppPINVOKE”的類型初始值設定項引發異常。原因:未找到所要調用的dll文件,需要手動將dll文件名改為module名。
- 報錯:“cppPINVOKE”的類型初始值設定項引發異常。解決方案:生成dll的工程平台要與c#的活動解決方案平台保持一致,否則就會報此錯誤。(注:有的時候vs上面顯示的並不是你真正的所在平台,你必須在屬性頁面查看,尤其是csharp項目平台)
- 報錯‘possibly a missing semicolon’,原因:可能由於.h文本里面嵌套着.h文本。解決方案:https://stackoverflow.com/questions/28523496/syntax-error-when-include-opencv-core-hpp-in-swig
- Error: Syntax error in inputs(3)。解決方案:在.idl文件中還得include本身.h文件里包含的那些.h文件。
- Syntax error in inputs(3),在引別的.h文件時把__declspec(dllexport)前綴給去了。
- C++中添加庫目錄,調用的不一定是庫目錄里的dll,但是放在工程項目中的release下的dll,一定能被調用。
- 如果生成dll時調用了別的dll的時候記得把dll一起拷到bin目錄下。
難點。
Intptr參數的轉換:
Intptr是c#中的句柄,有點類似於c++指針,但又不是一個東西,指針指向的是一個地址,但是Intptr只是某個資源的編號。
這個也是最不可控的一點,我在這上面花費了很大的精力。我准備是利用swig將c++實現的人臉識別代碼用c#來調用,因為c#做殼子挺方便的。但是我通過swig調用的時候,他傳入的圖像參數類型是intptr的,但是我是bitmap類型的。然后我遇到了一下棘手的問題:
- 我用GetHbitmap()函數將bitmap變為intptr類型,但發現c++不可訪問,
最后我把bitmap先轉為byte[]類型,然后再轉成inptr類型就成功了。
最后我也把我解決這個問題的代碼貼出來吧:
(1)BitmapExtensions.cs:
internal static class BitmapExtensions { /// <summary> /// 將圖像轉換為RGB圖像 /// </summary> /// <param name="self"></param> /// <returns></returns> public static Bitmap ConvertToRgb24(this Bitmap self) { if (self.PixelFormat != PixelFormat.Format24bppRgb) { var convertImage = new Bitmap(self.Width, self.Height, PixelFormat.Format24bppRgb); using (var g = Graphics.FromImage(self)) { g.DrawImage(self, 0, 0); } return convertImage; } return self; } /// <summary> /// 獲取位圖數據的像素數據 /// </summary> /// <param name="self"></param> /// <param name="useNativePixelFormat"></param> /// <param name="pixelSize"></param> /// <returns></returns> public static byte[] GetBitmapData(this Bitmap self, out int pixelSize, bool useNativePixelFormat = false) { var rect = new Rectangle(0, 0, self.Width, self.Height); var bmpData = self.LockBits(rect, ImageLockMode.ReadOnly, useNativePixelFormat ? self.PixelFormat : PixelFormat.Format24bppRgb); var dataPtr = bmpData.Scan0; var bytesCount = Math.Abs(bmpData.Stride) * self.Height; var data = new byte[bytesCount]; Marshal.Copy(dataPtr, data, 0, bytesCount); self.UnlockBits(bmpData); pixelSize = bmpData.Stride / bmpData.Width; return data; } }
外部實現代碼:
Bitmap gray = new Bitmap(image.Width, image.Height, PixelFormat.Format8bppIndexed); gray = new Grayscale(0.2125, 0.7154, 0.0721).Apply(image); int pixelSize; var imageData = BitmapExtensions.GetBitmapData(gray,out pixelSize,true); var pImageData = Marshal.AllocHGlobal(imageData.Length); //未釋放 Marshal.Copy(imageData, 0, pImageData, imageData.Length);
待參考網址:
http://www.swig.org/ (官網)
http://blog.csdn.net/lee353086/article/details/40707305 (建立一個示例)
http://www.technical-recipes.com/2013/getting-started-with-swig-interfacing-between-c-and-c-visual-studio-projects/ (建立一個示例)
http://www.swig.org/Doc1.3/CSharp.html#csharp_directors_example (講解swig與c#)
良心教程(建議英語好的可以看這個教程,官網教程太含糊了):
http://www.jenkinssoftware.com/raknet/manual/swigtutorial.html
https://blog.csdn.net/liu14lang/article/details/78882539