您的位置:首页 > 编程语言 > C语言/C++

【转】C#程序调用非托管C++ DLL文件的方法

2011-03-13 17:40 447 查看
08年写的一篇文章,当时项目用C#开发,但是有一些希望重用之前的C++代码,于是研究了如何在C#中调用C++的DLL。

C++中的函数声明

viewsourceprint?

1
extern
"C"
__declspec
(
dllexport
)
int
__stdcalltestfunc(
char
*astr,
int
*a);
extern”C”

  通常来说,C++编译器可能会改变函数和变量的名字,从而导致严重的链接程序问题。例如,假设使用C++编写一个DLL,当创建DLL时,Microsoft的编译器就会改变函数的名字。函数名将被设置一个前导下划线,再加上一个@符号的前缀,后随一个数字,表示作为参数传递给函数的字节数。例如,下面的函数是作为DLL的输出节中的_MyFunc@8输出的:

viewsourceprint?

1
__declspec
(
dllexport
)
LONG
__stdcallMyFunc(
int
a,
int
b);
  如果用另一个供应商的工具创建了一个可执行模块,它将设法链接到一个名叫MyFunc的函数,该函数在Microsoft编译器已有的DLL中并不存在,因此链接将失败。

  使用extern“C”关键字可以使编译器按照C语言的方式编译DLL文件,即编译时不改变函数名。

__declspec(dllexport)

  在32位编译器版本中,可以使用__declspec(dllexport)关键字从DLL导出数据、函数、类或类成员函数。__declspec(dllexport)会将导出指令添加到对象文件中,因此不需要使用.def文件。

  若要导出函数,__declspec(dllexport)关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:

viewsourceprint?

1
__declspec
(
dllexport
)
void
__cdeclFunction1(
void
);
__stdcall

  表明被调用方清理堆栈。

C#中的函数声明

viewsourceprint?

1
using
System.Runtime.InteropServices;
2
3
4
public
class
Program
5
{
6
[DllImport(
@"E:\Projects\testdll\debug\testdll.dll"
)]
7
public
static
extern
int
testfunc(StringBuilderabuf,
ref
int
a);
8
}
usingSystem.Runtime.InteropServices;

  System.Runtime.InteropServices命名空间提供各种各样支持COMinterop及平台调用服务的成员,使程序可以与非托管代码进行交互操作。

[DllImport(“dllfilepath”)]

  代码中DllImport关键字作用是告诉编译器入口点在哪里,并将打包函数捆绑在这个类中。在声明的时候还可以添加几个属性:

viewsourceprint?

1
[DllImport(
"MyDLL.dll"
,
2
EntryPoint=
"mySum"
,
3
CharSet=CharSet.Auto,
4
CallingConvention=CallingConvention.StdCall)]
  EntryPoint:指定要调用的DLL入口点。默认入口点名称是托管方法的名称。
  CharSet:控制名称重整和封送String参数的方式(默认是UNICODE)
  CallingConvention指示入口点的函数调用约定(默认WINAPI)

  注意:必须在标记为”static”和”extern”的方法上指定”DllImport”属性。

数据传递方法

1.基本数据类型的传递

  函数参数和返回值可以是C#和C++的各种基本数据类型,如int,float,double,char(注意不是char*)等。
  示例:
  C#代码:

viewsourceprint?

01
using
System;
02
using
System.Text;
03
using
System.Runtime.InteropServices;
04
05
class
Program
06
{
07
[DllImport(
@"E:\Projects\testdll\debug\testdll.dll"
)]
08
public
static
extern
int
testfunc(
int
a,
float
b,
double
c,
char
d);
09
10
static
void
Main(
string
[]args)
11
{
12
int
a=1;
13
float
b=12;
14
double
c=12.34;
15
char
d=
'A'
;
16
testfunc(a,b,c,d);
17
Console.ReadKey();
18
}
19
}
  C++代码:

viewsourceprint?

01
<PRE
class
=brush:cpp>#include<iostream>
02
using
namespace
std;
03
04
extern
"C"
05
{
06
_declspec(
dllexport
)
int
__stdcalltestfunc(
int
a,
float
b,
double
c,
char
d)
07
{
08
cout<<a<<
","
<<b<<
","
<<c<<
","
<<d<<endl;
09
return
0;
10
}
11
}
12
</PRE>

2.向DLL传入字符串

  C#中使用string定义字符串,将字符串对象名传给DLL。
  注意:在DLL中更改字符串的值,C#中的值也会改变。
  缺点:无法改变字符串的长度,建议使用第3种方法。
  C#代码:

viewsourceprint?

01
using
System;
02
using
System.Text;
03
using
System.Runtime.InteropServices;
04
05
class
Program
06
{
07
[DllImport(
@"E:\Projects\testdll\debug\testdll.dll"
)]
08
public
static
extern
int
testfunc(
string
a);
09
10
static
void
Main(
string
[]args)
11
{
12
string
a=
"HelloWorld!"
;
13
testfunc(a);
14
Console.ReadKey();
15
}
16
}
  C++代码:

viewsourceprint?

01
#include<iostream>
02
using
namespace
std;
03
04
extern
"C"
05
{
06
_declspec(
dllexport
)
int
__stdcalltestfunc(
char
*astr)
07
{
08
cout<<astr<<endl;
09
*astr=
'A'
;
//更改字符串的数据
10
cout<<astr<<endl;
11
return
0;
12
}
13
}

3.DLL传出字符串

  C#中使用StringBuilder对象创建变长数组,并设置StringBuilder的Capacity为数组最大长度。将此对象名传递给DLL,使用char*接收。
  C#代码:

viewsourceprint?

01
using
System;
02
using
System.Text;
03
using
System.Runtime.InteropServices;
04
05
class
Program
06
{
07
[DllImport(
@"E:\Projects\testdll\debug\testdll.dll"
)]
08
public
static
extern
int
testfunc(StringBuilderabuf);
09
10
static
void
Main(
string
[]args)
11
{
12
StringBuilderabuf=
new
StringBuilder();
13
abuf.Capacity=100;
//设置字符串最大长度
14
testfunc(abuf);
15
Console.ReadKey();
16
}
17
18
}
  C++代码:

viewsourceprint?

01
#include<iostream>
02
using
namespace
std;
03
04
extern
"C"
05
{
06
_declspec(
dllexport
)
int
__stdcalltestfunc(
char
*astr)
07
{
08
*astr++=
'a'
;
09
*astr++=
'b'
;
//C#中abuf随astr改变
10
*astr=
'\0'
;
11
12
return
0;
13
}
14
}

4.DLL传递结构体(需要在C#中重新定义,不推荐使用)

  C#中使用StructLayout重新定义需要使用的结构体。
  注意:在DLL改变结构体成员的值,C#中随之改变。
  C#代码:

viewsourceprint?

01
using
System;
02
using
System.Text;
03
using
System.Runtime.InteropServices;
04
05
[StructLayout(LayoutKind.Sequential)]
06
public
struct
Point
07
{
08
public
double
x;
09
public
double
y;
10
}
11
12
class
Program
13
{
14
[DllImport(
@"E:\Projects\testdll\debug\testdll.dll"
)]
15
public
static
extern
int
testfunc(Pointp);
16
17
static
void
Main(
string
[]args)
18
{
19
Pointp;
20
p.x=12.34;
21
p.y=43.21;
22
testfunc(p);
23
Console.ReadKey();
24
}
25
}
C++代码:

viewsourceprint?

01
#include<iostream>
02
using
namespace
std;
03
04
struct
Point
05
{
06
double
x;
07
double
y;
08
};
09
10
extern
"C"
11
{
12
_declspec(
dllexport
)
int
__stdcalltestfunc(Pointp)
13
{
14
cout<<p.x<<
","
<<p.y<<endl;
15
return
0;
16
}
17
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: