在從Emgu學(xué)習(xí)C#調(diào)用C++庫(kù)(1)中,挑了一個(gè)Emgu的“第一”函數(shù)分析了一下。雖然還是有些許不解,但大意還是略懂些了蹬屹。百看不如一練,今天就試著從我的C#中調(diào)用一下我的C庫(kù)吧白华。
要調(diào)用的是我在寫的一個(gè)OCR庫(kù)慨默。為了不過(guò)多地泄露“機(jī)密”(拿不出手),就只拿其中的一個(gè)函數(shù)接口作為例子吧弧腥。就也舉一個(gè)創(chuàng)建實(shí)例的例子厦取。我的C接口是這么寫的:
/** 創(chuàng)建OCR實(shí)例
@param[in] reader_type OCR的類型,\ref READER_TYPE
@param[in] ocr_info 與識(shí)別器有關(guān)的參數(shù)
@return OCR的句柄
*/
GGAPI(READER_HANDLE) ggCreateReader(int reader_type, const OCRRelatedInfo_t* ocr_info);
三點(diǎn)說(shuō)明:
- reader_type實(shí)際上是一個(gè)枚舉類型管搪。
typedef enum {
GG_ACCOUNT_READER = 1,
GG_MONEY_READER = 2,
GG_RMBSN_READER = 3
}READER_TYPE;
- OCRRelatedInfo_t是自己定義的一個(gè)結(jié)構(gòu)體虾攻。
//! 賬號(hào)圖像信息
typedef struct tagOCRRelatedInfo
{
int x; //!< ROI起點(diǎn)x坐標(biāo)
int y; //!< ROI起點(diǎn)y坐標(biāo)
int width; //!< 寬度
int height; //!< 高度
int percent; //!< 如果ROI為所占的百分比,則可以設(shè)置此參量為分母
int bits; //!< 個(gè)數(shù)
int min_h; //!< 字符最小高度
int max_h; //!< 字符最大高度
int min_w; //!< 字符最小寬度
int max_w; //!< 字符最大寬度
char rec_file[256]; //!< 識(shí)別相關(guān)的文件存儲(chǔ)的位置
int image_width; //!< 配置結(jié)構(gòu)體項(xiàng)時(shí)對(duì)應(yīng)的圖像的寬度
int image_height; //!< 配置結(jié)構(gòu)體項(xiàng)時(shí)對(duì)應(yīng)的圖像的高度
char resv[120]; //!< 保留字節(jié)
}OCRRelatedInfo_t;
-
READER_HANDLE
實(shí)際上是一個(gè)指針的值更鲁,但被定義為long型
#define READER_HANDLE long
首先來(lái)看如何轉(zhuǎn)換枚舉類型霎箍。仿照IPL_DEPTH
,我也來(lái)寫一個(gè)enum:
namespace OCRAlgWrapper.OCREnum
{
public enum READER_TYPE
{
GG_ACCOUNT_READER = 1,
GG_MONEY_READER = 2,
GG_RMBSN_READER = 3
}
}
然后來(lái)定義自定義的結(jié)構(gòu)體:
namespace OCRAlgWrapper
{
/// <summary>
/// Managed structure equivalent to OCRRelatedInfo_t
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct OCRRelatedInfo
{
public int RoiX;
public int RoiY;
public int RoiW;
public int RoiH;
public int RoiPercent;
public int CharBits;
public int CharMinH;
public int CharMaxH;
public int CharMinW;
public int CharMaxW;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
public string RecFileName;
public int ImageWidth;
public int ImageHeight;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=120)]
public string resv;
}
}
自定義結(jié)構(gòu)體的封裝在(1)中并未提及岁经。官網(wǎng)中講的很詳細(xì):
可以為傳遞到非托管函數(shù)或從非托管函數(shù)返回的結(jié)構(gòu)和類的字段指定自定義封送處理屬性朋沮。通過(guò)向結(jié)構(gòu)或類的字段中添加MarshalAs
屬性可以做到這一點(diǎn)。還必須使用StructLayout
屬性設(shè)置結(jié)構(gòu)的布局缀壤,還可以控制字符串成員的默認(rèn)封送處理,并設(shè)置默認(rèn)封裝大小纠亚。
請(qǐng)考慮下面的 C 結(jié)構(gòu):
typedef struct tagLOGFONT
{
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;
在 C# 中塘慕,可以使用StructLayout
和MarshalAs
屬性描述前面的結(jié)構(gòu),如下所示:
// logfont.cs
// compile with: /target:module
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public class LOGFONT
{
public const int LF_FACESIZE = 32;
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public int lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)]
public string lfFaceName;
}
然后即可將該結(jié)構(gòu)用在 C# 代碼中蒂胞,如下所示:
// pinvoke.cs
// compile with: /addmodule:logfont.netmodule
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateFontIndirect(
[In, MarshalAs(UnmanagedType.LPStruct)]
LOGFONT lplf // characteristics
);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(
IntPtr handle
);
public static void Main()
{
LOGFONT lf = new LOGFONT();
lf.lfHeight = 9;
lf.lfFaceName = "Arial";
IntPtr handle = CreateFontIndirect(lf);
if (IntPtr.Zero == handle)
{
Console.WriteLine("Can't creates a logical font.");
}
else
{
if (IntPtr.Size == 4)
Console.WriteLine("{0:X}", handle.ToInt32());
else
Console.WriteLine("{0:X}", handle.ToInt64());
// Delete the logical font created.
if (!DeleteObject(handle))
Console.WriteLine("Can't delete the logical font");
}
}
}
運(yùn)行示例
C30A0AE5
在前面的示例中图呢,CreateFontIndirect 方法使用了一個(gè) LOGFONT 類型的參數(shù)。MarshalAs 和 In 屬性用于限定此參數(shù)。程序?qū)⒂纱朔椒ǚ祷氐臄?shù)值顯示為十六進(jìn)制大寫字符串蛤织。
好了赴叹,準(zhǔn)備工作到此結(jié)束,下面就要來(lái)寫C#中的接口函數(shù)了:
namespace OCRAlgWrapper
{
public class OCRInvoke
{
[DllImport("gg_universal_ocr.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr ggCreateReader(OCREnum.READER_TYPE readerType, ref OCRRelatedInfo ocrInfo);
}
}
其中指蚜,結(jié)構(gòu)體的指針被寫成了ref參數(shù)乞巧。而由于返回的其實(shí)就是指針,因此用IntPtr來(lái)表示摊鸡。
最后绽媒,就可以在主函數(shù)中調(diào)用了:
IntPtr ocrHandle = OCRInvoke.ggCreateReader(READER_TYPE.GG_RMBSN_READER, ref m_ocrInfo);
到此,C#就可以調(diào)用C++的函數(shù)了免猾,到這里是辕,故事似乎就應(yīng)該結(jié)束了。但是還差一點(diǎn)點(diǎn):當(dāng)我調(diào)試的時(shí)候猎提,竟然F11
不進(jìn)去获三,無(wú)法調(diào)試C++的代碼!不能調(diào)試當(dāng)然不行锨苏。而“知其要者疙教,一言而終;不知其要蚓炬,流散無(wú)窮松逊。”
事實(shí)上肯夏,我們只需要選擇項(xiàng)目屬性中的調(diào)試標(biāo)簽经宏,并勾選“啟用本機(jī)代碼調(diào)試”就可以了。