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

VS2010 C#调用C++ DLL文件(图文讲解)

2012-02-02 15:44 399 查看
背景

在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件的时候,通过C#来开发应用软件时,就需要利用DllImport特性进行方法调用。本篇文章将引导你快速理解这个调用的过程。

步骤

1.创建一个CSharpInvokeCPP的解决方案:





2.创建一个C++的动态库项目:





3.在应用程序设置中,选择“DLL”,其他按照默认选项:





最后点击完成,得到如图所示项目:





我们可以看到这里有一些文件,其中dllmain.cpp作为定义DLL应用程序的入口点,它的作用跟exe文件有个main或者WinMain入口函数是一样的,它就是作为DLL的一个入口函数,实际上它是个可选的文件。它是在静态链接时或动态链接时调用LoadLibrary和FreeLibrary时都会被调用。详细内容可以参考(http://blog.csdn.net/benkaoya/archive/2008/06/02/2504781.aspx)。

4.现在我们打开CSharpInvokeCPP.CPPDemo.cpp文件:

现在我们加入以下内容:

viewsourceprint?

01
//CSharpInvokeCPP.CPPDemo.cpp:定义
DLL应用程序的导出函数。
02
//
03
04
#include"stdafx.h"
05
06
extern

"C"
__declspec
(
dllexport
)
int

Add(
int

x,
int

y)
07
{
08
return

x+y;
09
}
10
extern

"C"
__declspec
(
dllexport
)
int

Sub(
int

x,
int

y)
11
{
12
return

x-y;
13
}
14
extern

"C"
__declspec
(
dllexport
)
int

Multiply(
int

x,
int

y)
15
{
16
return

x*y;
17
}
18
extern

"C"
__declspec
(
dllexport
)
int

Divide(
int

x,
int

y)
19
{
20
return

x/y;
21
}
extern"C"包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。而被extern"C"修饰的变量和函数是按照C语言方式编译和连接的。

__declspec(dllexport)的目的是为了将对应的函数放入到DLL动态库中。

extern"C"__declspec(dllexport)加起来的目的是为了使用DllImport调用非托管C++的DLL文件。因为使用DllImport只能调用由C语言函数做成的DLL

5.编译项目程序,最后在Debug目录生成CSharpInvokeCPP.CPPDemo.dll和CSharpInvokeCPP.CPPDemo.lib





我们用反编译工具PEExplorer查看下该DLL里面的方法:





可以发现对外的公共函数上包含这四种“加减乘除”方法。

6.现在来演示下如何利用C#项目来调用非托管C++的DLL,首先创建C#控制台应用程序:





7.在CSharpInvokeCSharp.CSharpDemo项目上新建一个CPPDLL类,编写以下代码:

viewsourceprint?

01
public

class

CPPDLL
02
{
03
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
04
public

static

extern

int

Add(
int

x,
int

y);
05
06
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
07
public

static

extern

int

Sub(
int

x,
int

y);
08
09
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
10
public

static

extern

int

Multiply(
int

x,
int

y);
11
12
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
13
public

static

extern

int

Divide(
int

x,
int

y);
14
}
DllImport作为C#中对C++的DLL类的导入入口特征,并通过staticextern对extern“C”进行对应。

8.另外,记得把CPPDemo中生成的DLL文件拷贝到CSharpDemo的bin目录下,你也可以通过设置【项目属性】->【配置属性】->【常规】中的输出目录:





这样编译项目后,生成的文件就自动输出到CSharpDemo中了。

9.然后在Main入口编写测试代码:

viewsourceprint?

01
static

void

Main(
string
[]args)
02
{
03
int

result=CPPDLL.Add(10,20);
04
Console.WriteLine(
"10+20={0}"
,result);
05
06
result=CPPDLL.Sub(30,12);
07
Console.WriteLine(
"30-12={0}"
,result);
08
09
result=CPPDLL.Multiply(5,4);
10
Console.WriteLine(
"5*4={0}"
,result);
11
12
result=CPPDLL.Divide(30,5);
13
Console.WriteLine(
"30/5={0}"
,result);
14
15
Console.ReadLine();
16
}
运行结果:





方法得到调用。

10.以上的方法只能通过静态方法对于C++中的函数进行调用。那么怎样通过静态方法去调用C++中一个类对象中的方法呢?现在我在CPPDemo项目中添加一个头文件userinfo.h:

viewsourceprint?

01
class

UserInfo{
02
private
:
03
char
*m_Name;
04
int

m_Age;
05
public
:
06
UserInfo(
char
*name,
int

age)
07
{

08
m_Name=name;
09
m_Age=age;
10
}

11
virtual

~UserInfo(){}
12
int

GetAge(){
return

m_Age;}
13
char
*GetName(){
return

m_Name;}
14
};
在CSharpInvokeCPP.CPPDemo.cpp中,添加一些代码:

viewsourceprint?

01
#include"malloc.h"
02
#include"userinfo.h"
03
04
typedef

struct

{
05
char

name[32];
06
int

age;
07
}User;
08
09
UserInfo*userInfo;
10
11
extern

"C"
__declspec
(
dllexport
)User*Create(
char
*
name,
int

age)
12
{
13
User*user=(User*)
malloc
(
sizeof
(User));
14
15
userInfo=
new

UserInfo(name,age);
16
strcpy
(user->name,userInfo->GetName());
17
user->age=userInfo->GetAge();
18
19
return

user;
20
}
这里声明一个结构,包括name和age,这个结构是用于和C#方面的结构作个映射。

注意:代码中的User*是个指针,返回也是一个对象指针,这样做为了防止方法作用域结束后的局部变量的释放。

strcpy是个复制char数组的函数。

11.在CSharpDemo项目中CPPDLL类中补充代码:

viewsourceprint?

01
[DllImport(
"CSharpInvokeCPP.CPPDemo.dll"
)]
02
public

static

extern

IntPtrCreate(
string

name,
int

age);
03
04
[StructLayout(LayoutKind.Sequential)]
05
public

struct

User
06
{
07
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)]
08
public

string

Name;
09
10
public

int

Age;
11
}
其中这里的结构User就和C++中的User对应。

12.在Program.cs中补充代码:

viewsourceprint?

1
IntPtrptr=CPPDLL.Create(
"李平"
,27);
2
<STRONG><FONTcolor=#ff0000>CPPDLL.Useruser=(CPPDLL.User)Marshal.PtrToStructure(ptr,
typeof
(CPPDLL.User));</FONT></STRONG>
3
Console.WriteLine(
"Name:{0},Age:{1}"
,user.Name,user.Age);
注意:红色字体部分,这里结构指针首先转换成IntPtr句柄,然后通过Marshal.PtrToStructrue转换成你所需要的结构。

运行结果:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: