C#与C++的混合编程 之二 使用非托管dll 导出标准函数

C#与C++的混合编程 之二  使用非托管dll 导出标准函数




前面我们介绍了C#与C++混合编程的几种方式,并介绍了用CLR封装非托管dll的调用方法,下面我们介绍一下C#调用非托管C++导出标准C接口的方式。


(1)建立生成dll的工程


打开VS,选择"新建项目"-“VC++”-"Win32"-"Win32项目",工程的名字叫"MyNativeDll",配置如下图所示,因为我有可能用到MFC的类,所以我就勾选了“MFC”的选项,在此需要注意的是,如果你新建时没有勾选MFC,但在后面却想动用MFC的内容,就会遇到“MFC apps must not #include ”的Error,只是在工程的配置里修改是根本没有用的,必做要重建工程。



(2)实现dll导出的函数


新建好工程后,在VS的“解决方案资源管理器”中可以看到如下图的目录。



在工程里添加几个文件,Define.h,CFunction.h,CFunction.cpp,其内容如下所示:


//Define.h 用于导入dll的宏定义。

1
2
3
4
5
6
7
//Define.h
///////////////////////////////////////////
//////////////////////////////////////////
#ifndef _DEFINE_H_
#define _DEFINE_H_
#define _EXTERN_C_  extern "C"  _declspec(dllexport)
#endif


//CFunction.h 函数定义,这里我特意定义了一组结构,我的用意稍后再讲。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//CFunction.h
////////////////////////////////////////////
///////////////////////////////////////////
#ifndef _C_FUNCTION_H_
#define _C_FUNCTION_H_
#include "Define.h"
#include
#include
struct  SystemTime
{
     int  year;
     int  month;
     int  day;
     int  hour;
     int  minute;
     int  second;
     int  millsecond;
     SystemTime & operator= (SystemTime st)
     {
         this ->year = st.year;
         this ->month = st.month;
         this ->day = st.day;
         this ->hour = st.hour;
         this ->minute = st.minute;
         this ->second = st.second;
         this ->millsecond = st.millsecond;
         return  * this ;
     }
};
_EXTERN_C_  int  add( int  x,  int  y);
_EXTERN_C_  int  sub( int  x,  int  y);
_EXTERN_C_  int  testChar( char  * src,  char  * res,  int  nCount);
_EXTERN_C_  int  testStruct(SystemTime & stSrc, SystemTime & stRes);
#endif //_C_FUNCTION_H_

//CFunction.cpp dll函数的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//CFunction.cpp
////////////////////////////////////////////
////////////////////////////////////////////
#include "stdafx.h"
#include "CFunction.h"
#include
int  add( int  x,  int  y)
{
     return  x + y;
}
int  sub( int  x,  int  y)
{
     return  x - y;
}
int  testChar( char  * src,  char  * res,  int  nCount)
{
     memcpy (res, src,  sizeof ( char ) * nCount);
     return  1;
}
int  testStruct(SystemTime & stSrc, SystemTime & stRes)
{
     stRes = stSrc;
     return  1;
}

添加好代码之后,选择“生成”的选项,因在工程目录下的Debug文件就已经存在我们所需要的MyNativeDll.dll文件,一起的还有lib的静态库文件(稍后要用到),及其他相关的调试文件,至此,我们已经成功的生成了native C++的动态链接库,我只能说,这是相当简单的第一步在而已。


(3)在C#工程下使用生成的dll


新建一个C#的窗口工程,工程命名为“DllTest”,在新建的窗体工程中添加一个CFunction.cs的类,这个类主要是用于导出上面dll里的函数,废话不多说,直接贴代码:


//CFunction.cs dll的函数接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Threading.Tasks;
using  System.Runtime.InteropServices;
namespace  DllTest
{
     [StructLayout(LayoutKind.Sequential)]
     public  struct  SystemTime
     {
         public  int  year;
         public  int  month;
         public  int  day;
         public  int  hour;
         public  int  minute;
         public  int  second;
         public  int  millsecond;
         public  SystemTime(DateTime dt)
         {
             this .year = dt.Year;
             this .month = dt.Month;
             this .day = dt.Day;
             this .hour = dt.Hour;
             this .minute = dt.Minute;
             this .second = dt.Second;
             this .millsecond = dt.Millisecond;
         }
         public  override  string  ToString()
         {
             return  this .year.ToString() +  "-"  this .month.ToString() +  "-"  + this .day.ToString() +  "  "
                 this .hour.ToString() +  ":"  this .minute.ToString() +  "-"  + this .second.ToString() +  "-"
                 this .millsecond.ToString();
         }
     };
     public  class  CFunction
     {
         [DllImport( "MyNativeDll.dll" , CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
         public  extern  static  int  add( int  x,  int  y);
         [DllImport( "MyNativeDll.dll" , CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
         public  extern  static  int  sub( int  x,  int  y);
         [DllImport( "MyNativeDll.dll" , CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
         public  extern  static  int  testChar( ref  byte  src,  ref  byte  res,  int  nCount);
         [DllImport( "MyNativeDll.dll" , CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
         public  extern  static  int  testStruct( ref  SystemTime stSrc,  ref  SystemTime stRes);
     }
}

上面的做法相当是作了一个CFunction的静态类而已。然后在Form1.cs窗体里直接写测试代码,我是直接写在Form1的初始化函数里。

//Form1.cs 在C#的窗体初始化函数添加测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Diagnostics;
using  System.Drawing;
using  System.Linq;
using  System.Text;
using  System.Threading.Tasks;
using  System.Windows.Forms;
namespace  DllTest
{
     public  partial  class  Form1 : Form
     {
         public  Form1()
         {
             InitializeComponent();
             int  a = CFunction.add(100, 50);
             int  b = CFunction.sub(100, 50);
             Debug.WriteLine( "add = "  + a.ToString() +  "  b = "  + b.ToString());
             Debug.WriteLine( "\r\n" );
             string  src =  "123456" ;
             byte [] srcBytes = System.Text.Encoding.ASCII.GetBytes(src);
             byte [] resBytes =  new  byte [100];
             a = CFunction.testChar( ref  srcBytes[0],  ref  resBytes[0], src.Length);
             string  res = (System.Text.Encoding.ASCII.GetString(resBytes, 0, resBytes.Length)).TrimEnd();
             Debug.WriteLine(res.ToString());
             Debug.WriteLine( "\r\n" );
             SystemTime stSrc =  new  SystemTime(DateTime.Now);
             SystemTime stRes =  new  SystemTime();
             a = CFunction.testStruct( ref  stSrc,  ref  stRes);
             Debug.WriteLine(stRes.ToString());
             Debug.WriteLine( "\r\n" );
         }
     }
}

在你进行调试之前,务必记得要将在第二步生成的MyNativeDll.dll拷贝至C#工程下的bin\Debug\目录下,然后点击“调试”,看输出窗口,应该会有东西输出的,我就不贴出来了。


(4)总结


1)在书写dll导出函数时,变量的传递是关键,建议使用C++的基本类型,如int,float,double等,因为C#下指针的概念很纠结,在C++下的引用符“&”,在C#中则使用ref的标识,需要紧记的一点是,C#与C++的类型并不全然通用(结构对齐问题),注意做变换。像上面的testChar函数,原本string(C#)对应的是char*(C++),但可能由于各种Unicode或多字节的关系,我是没法返回正确的值,于是我采用了byte的传入类型。

2)观察我写的结构,在C++下使用的结构体,在C#必须要重新定义一次,使用  [StructLayout(LayoutKind.Sequential)] 的标识用于结构的对齐,如果你变量中使用了string这样的类型,还需要使用MarshalAs这样的方法支定义其长度——才可以跟char *相对应;

3)函数的返回值别用什么string了,最好使用ref的引用方法回传回来。

4)指针的参数的传递在C#下使用IntPtr类型作转换,这我先不细说,网上相关文章还是不少的。



示例源码连接:http://download.csdn.net/detail/fang1192801693/9309655


你可能感兴趣的:(C#与C++的混合编程 之二 使用非托管dll 导出标准函数)