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

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

背景

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

步骤

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

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

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

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

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

(https://www.360docs.net/doc/3310635322.html,/benkaoya/archive/2008/06/02/2504781.aspx)。

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

现在我们加入以下内容:

view sourceprint?

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

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

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

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

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

view sourceprint?

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类的导入入口特征,并通过static extern对extern “C”进行对应。

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

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

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

view sourceprint?

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:

view sourceprint?

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 中,添加一些代码:

view sourceprint?

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 类中补充代码:

view sourceprint?

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

02 public static extern IntPtr Create(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 中补充代码:

view sourceprint?

1 IntPtr ptr = CPPDLL.Create("李平", 27);

2 https://www.360docs.net/doc/3310635322.html,er user =

(https://www.360docs.net/doc/3310635322.html,er)Marshal.PtrToStructure(ptr,typeof (https://www.360docs.net/doc/3310635322.html,er));

3 Console.WriteLine("Name: {0}, Age: {1}", https://www.360docs.net/doc/3310635322.html,, user.Age);

注意:红色字体部分,这里结构指针首先转换成IntPtr 句柄,然后通过

Marshal.PtrToStructrue 转换成你所需要的结构。

运行结果:

相关主题
相关文档
最新文档