`
webcode
  • 浏览: 5956721 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

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

阅读更多

项目用C#开发,但是有一些希望重用之前的C++代码,于是研究了如何在C#中调用C++的DLL。

C++中的函数声明

view source

print?

1

extern "C" __declspec(dllexport) int __stdcall testfunc(char* astr,int* a);

extern ”C”

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

view source

print?

1

__declspec(dllexport) LONG __stdcall MyFunc(int a, int b);

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

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

__declspec(dllexport)

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

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

view source

print?

1

__declspec(dllexport) void __cdecl Function1(void);

__stdcall

表明被调用方清理堆栈。

C#中的函数声明

view source

print?

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(StringBuilder abuf,ref int a);

8

}

using System.Runtime.InteropServices;

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

[DllImport(“dllfile path”)]

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

view source

print?

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#代码:

view source

print?

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++代码:

view source

print?

01

#include <iostream></iostream>

02

using namespace std;

03

04

extern "C"

05

{

06

_declspec(dllexport) int __stdcall testfunc(int a,float b,double c,char d)

07

{

08

cout

09

return 0;

10

}

11

}

12

2.向DLL传入字符串

C#中使用string定义字符串,将字符串对象名传给DLL。
注意:在DLL中更改字符串的值,C#中的值不会改变。
C#代码:

view source

print?

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="Hello World!";

13

testfunc(a);

14

Console.ReadKey();

15

}

16

}

C++代码:

view source

print?

01

#include <iostream></iostream>

02

using namespace std;

03

04

extern "C"

05

{

06

_declspec(dllexport) int __stdcall testfunc(char* astr)

07

{

08

cout

09

*astr='A';//更改字符串的数据

10

cout

11

return 0;

12

}

13

}

3.DLL传出字符串

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

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(StringBuilder abuf);

09

10

static void Main(string[] args)

11

{

12

StringBuilder abuf=new StringBuilder();

13

abuf.Capacity = 100;//设置字符串最大长度

14

testfunc(abuf);

15

Console.ReadKey();

16

}

17

18

}

C++代码:

view source

print?

01

#include <iostream></iostream>

02

using namespace std;

03

04

extern "C"

05

{

06

_declspec(dllexport) int __stdcall testfunc(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#代码:

view source

print?

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(Point p);

16

17

static void Main(string[] args)

18

{

19

Point p;

20

p.x = 12.34;

21

p.y = 43.21;

22

testfunc(p);

23

Console.ReadKey();

24

}

25

}

C++代码:

view source

print?

01

#include <iostream></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 __stdcall testfunc(Point p)

13

{

14

cout

15

return 0;

16

}

17

}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics