Visual Studio 2010调用非C
创始人
2024-07-24 17:21:25
0

背景

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

步骤

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

image

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

image

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

image

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

image

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

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

现在我们加入以下内容:

  1. // CSharpInvokeCPP.CPPDemo.cpp : 定义 DLL 应用程序的导出函数。   
  2.  //   
  3. #include "stdafx.h"   
  4.  extern "C" __declspec(dllexport) int Add(int x, int y)   
  5.  {   
  6.  return x + y;   
  7. }   
  8.  extern "C" __declspec(dllexport) int Sub(int x, int y)   
  9.  {   
  10.  return x - y;   
  11.  }   
  12.  extern "C" __declspec(dllexport) int Multiply(int x, int y)   
  13. {   
  14.  return x * y;   
  15. }   
  16.  extern "C" __declspec(dllexport) int Divide(int x, int y)   
  17.  {   
  18. return x / y;   
  19. }   
  20.  

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

image

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

image

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

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

image

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

  1.  public class CPPDLL   
  2.  {   
  3.  [DllImport("CSharpInvokeCPP.CPPDemo.dll")]   
  4. public static extern int Add(int x, int y);   
  5.  [DllImport("CSharpInvokeCPP.CPPDemo.dll")]   
  6. public static extern int Sub(int x, int y);   
  7. [DllImport("CSharpInvokeCPP.CPPDemo.dll")]   
  8.  public static extern int Multiply(int x, int y);   
  9. [DllImport("CSharpInvokeCPP.CPPDemo.dll")]   
  10.  public static extern int Divide(int x, int y);   
  11.  }  

DllImport作为C#中对C++的DLL类的导入入口特征,并通过static extern对extern “C”进行对应。

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

image

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

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

  1.  static void Main(string[] args)   
  2. {   
  3.  int result = CPPDLL.Add(10, 20);   
  4.  Console.WriteLine("10 + 20 = {0}", result);   
  5. result = CPPDLL.Sub(30, 12);   
  6.  Console.WriteLine("30 - 12 = {0}", result);   
  7. result = CPPDLL.Multiply(5, 4);   
  8.  Console.WriteLine("5 * 4 = {0}", result);   
  9.  result = CPPDLL.Divide(30, 5);   
  10.  Console.WriteLine("30 / 5 = {0}", result);   
  11.  Console.ReadLine();   
  12.  }  

运行结果:

image

方法得到调用。
 

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

  1.  class UserInfo {   
  2. private:   
  3. char* m_Name;   
  4.  int m_Age;   
  5.  public:   
  6. UserInfo(char* name, int age)   
  7.  {   
  8.  m_Name = name;   
  9.  m_Age = age;   
  10.  }   
  11.  virtual ~UserInfo(){ }   
  12. int GetAge() { return m_Age; }   
  13. char* GetName() { return m_Name; }   
  14.  };  

在CSharpInvokeCPP.CPPDemo.cpp中,添加一些代码:

  1.  #include "malloc.h"   
  2.  #include "userinfo.h"   
  3. typedef struct {   
  4. char name[32];   
  5.  int age;   
  6. } User;   
  7.  UserInfo* userInfo;   
  8. extern "C" __declspec(dllexport) User* Create(char* name, int age)   
  9.  {   
  10.  User* user = (User*)malloc(sizeof(User));   
  11.  userInfo = new UserInfo(name, age);   
  12. strcpy(user->name, userInfo->GetName());   
  13.  user->age = userInfo->GetAge();   
  14. return user;   
  15.  }  

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

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

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

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

  1. [DllImport("CSharpInvokeCPP.CPPDemo.dll")]   
  2. public static extern IntPtr Create(string name, int age);   
  3.  [StructLayout(LayoutKind.Sequential)]   
  4.  public struct User   
  5.  {   
  6. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]   
  7. public string Name;   
  8. public int Age;   
  9.  }  

其中这里的结构User就和C++中的User对应。

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

  1.  IntPtr ptr = CPPDLL.Create("李平", 27);   
  2.  CPPDLL.User user = (CPPDLL.User)Marshal.PtrToStructure(ptr, typeof(CPPDLL.User));   
  3. Console.WriteLine("Name: {0}, Age: {1}", user.Name, user.Age);  

注意:红色字体部分,这里结构指针首先转换成IntPtr句柄,然后通过Marshal.PtrToStructrue转换成你所需要的结构。

运行结果:

image 

原文链接:http://www.cnblogs.com/liping13599168/archive/2011/03/31/2000320.html

【编辑推荐】

  1. VS.NET 2010已经发布了beta2版本 新功能
  2. 初步了解Visual Studio 2010 Professional专业版
  3. Visual Studio 2010中使用MTLM管理测试工具
  4. Visual Studio 2010 SP1公测版已经发布
  5. Visual Studio 2010中敏捷开发流程模板的应用 
     

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...