在C++中反射调用.NET的方法(一)

为什么要在C++中调用.NET

一般情况下,我们常常会在.NET程序中调用C/C++的程序,使用P/Invoke方式进行调用,在编写代码代码的时候,首先要导入DLL文件,然后在根据C/C++的头文件编写特殊的C#平台调用代码,例如像下面这个样子:

 [DllImport("Interop.dll",EntryPoint = "Multiply",CharSet = CharSet.Ansi)]
 static extern int Multiply(int factorA, int factorB);

详细的过程,可以参考之前我这篇文章:《C#调用C和C++函数的一点区别》

有时候,我们也会有在C++中调用.NET的需求,比如我们在维护一个大型的C++应用程序,它年代久远,现在需要增加一些新功能,而这些功能在.NET中已经有了,只需要调用它即可,如果为了方便想要用.NET重写这个C++应用程序是不太现实的,幸好,C++/CLI提供了一个简便的方案使得可以在C++中直接编写.NET程序,所以C++/CLI代表托管和本地编程的结合,可以在托管代码中直接使用本地代码,也可以反过来,这样结合了C++本地代码的高效性和.NET代码的强大性,看起来是非常有潜力的。

使用C++/CLI进行.NET编程

要进行C++/CLI编程,只需要进行下面的步骤:

1,添加.NET程序集的应用;

2,修改C++项目属性,配置属性->公共语言运行时支持-公共语言运行时支持(/clr)

然而,为了保持C++与.NET应用程序的独立性,要求不能将.NET的DLL文件放到C++的应用程序目录下,因此上述步骤1不可行,需要在C++代码中使用反射来调用.NET。

注意,本文说的C++反射调用,不是对C++自身进行封装的反射功能,而是在C++/CLI代码中反射调用.NET代码,原理上跟你在.NET应用中反射调用另外一个.NET的程序集一个道理。

首先,我们建立一个名字叫CppNetTest的解决方案,添加3个项目:

1,CppConsoleTest---一个C++控制台项目,在项目中更改属性支持CLR;

2,NetApp--一个.NET控制台应用程序,作为对比示例代码,方便编写C++/CLI代码参考;

3,NetLib--一个.NET类库程序集,它将被1和2项目进行反射调用。

我们先在NetLib项目写一个简单的.NET 类,这个类的方法内部没有复杂的业务逻辑代码,仅仅用来供反射调用测试:

namespace NetLib
{
  public class User
  {
    static List<IUserInfo> UserDb = new List<IUserInfo>();
    public int GetUserID(string IdString)
    {
      int result = 0;
      int.TryParse(IdString, out result);
      return result;
    }
    public DateTime GetUserBirthday(int userId)
    {
      return new DateTime(1980, 1, 1);
    }
    public IUserInfo GetUserByID(int userId)
    {
      IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>();
      userinfo.ID = userId;
      userinfo.Name = "姓名_" + userId;
      userinfo.Birthday = new DateTime(1980, 1, 1);
      return userinfo;
    }
    //返回List或者数组,不影响 C++调用
    public List<IUserInfo> GetUsers(string likeName)
    {
      List<IUserInfo> users = new List<NetLib.IUserInfo>();
      for (int i = 0; i < 10; i++)
      {
        IUserInfo userinfo = GetUserByID(i);
        userinfo.Name += likeName;
        users.Add(userinfo);
      }
      //return users.ToArray();
      return users;
    }
    public bool SaveUsers(IList<IUserInfo> users)
    {
      UserDb.AddRange(users);
      return true;
    }
    public IUserInfo CreateUserObject()
    {
      return EntityBuilder.CreateEntity<IUserInfo>();
    }
    public bool SaveUsers2(IEnumerable<Object> para)
    {
      var users = from u in para
            select u as IUserInfo;
      return SaveUsers (users.ToList());
    }
  }
}

在CppConsoleTest项目的头文件中,添加一个 UserProxy.h 的C++头文件,在文件中添加下面的命名空间:

using namespace System;
using namespace System::Reflection;
using namespace Runtime::InteropServices;
using namespace System::Collections;

这样我们就可以使用反射相关的类型了。

在UserProxy类中,先编写我们需要的构造函数:

public ref class UserProxy
  {
  private:
    String^ assemblyFile; //"..\\NetLib\\bin\\Debug\\NetLib.dll"
    Object^ dotnetObject;
    Type^ entityBuilderType;
    String^ className = "NetLib.User";
    EntityHelper^ helper;

  public:
    UserProxy(String^ assemblyFile)
    {
      this->assemblyFile = assemblyFile;
      Assembly^ ass = Assembly::LoadFrom(this->assemblyFile);
      this->dotnetObject = ass->CreateInstance(className);
      String^ sodPath = System::IO::Path::Combine(System::IO::Path::GetDirectoryName(this->assemblyFile), "PWMIS.Core.dll");
      /*Assembly^ ass_sod = Assembly::LoadFrom(sodPath);
      this->entityBuilderType = ass_sod->GetType("PWMIS.DataMap.Entity.EntityBuilder");*/
      helper = gcnew EntityHelper(sodPath);
    }
}

注意我们的 C++/CLI的类必须是“引用”类型,所以需要加关键字 ref,即:

public ref class UserProxy{}

所有的.NET引用类型,在使用的时候,都必须在类型名字后加 ^ 符号,例如下面定一个.NET字符串类型变量:

String^ assemblyFile; 

带^符号的变量,在C++/CLI中称为 “句柄”对象,用来跟C++本地代码的“指针”相区别。

在C++中,类的成员用 -> 符号调用,命名空间或者类的静态成员,用::调用,例如上面的构造函数中的代码:

Assembly^ ass = Assembly::LoadFrom(this->assemblyFile);

注意:在本例中需要.NET类库项目引用 PDF.NET SOD框架,在项目的“管理Nuget程序包”里面搜索 PDF.NET.SOD.Core 添加此引用即可。
学会了这些C++的基础语法,那么编写C++/CLI代码就没有主要的障碍了。

在C++/CLI中使用反射

反射调用第一个.NET类的方法

下面的方法,将会反射调用 User类的一个最简单的方法 :

public int GetUserID(string IdString){}

该方法只有一个一个参数和一个简单的返回值,下面是C++/CLI的反射调用代码:

int GetUserID(String^ iDstring)
{
  MethodInfo^ method = this->dotnetObject->GetType()->GetMethod("GetUserID", BindingFlags::Public | BindingFlags::Instance);
  Func<String^, int>^ fun = (Func<String^, int>^)Delegate::CreateDelegate(Func<String^, int>::typeid, this->dotnetObject, method);
  int result = fun(iDstring);

  return result;
}

注意这里创建了一个 Func<String,int>的委托方法,使用委托能够简化我们的反射调用并且有时候还能够提高效率,在这段代码中,有1个要注意的地方:

Func<String^, int>::typeid

这是C++/CLI特殊的语法,表示获取“句柄”类型的类型ID,实际上它的结果就Type对象,等同于C#的
typeof(Func<string,int>)

PS:非常遗憾的是,typeid方式,没法得到下面类型的类型值:
typeof(Func<,>),这给我们在动态构造泛型对象的时候造成了很大的困惑。

再看一个简单方法的反射:

DateTime GetUserBirthday(int userId)
    {
      MethodInfo^ method = dotnetObject->GetType()->GetMethod("GetUserBirthday", BindingFlags::Public | BindingFlags::Instance);
      Func<int, DateTime>^ fun = (Func<int, DateTime>^)Delegate::CreateDelegate(Func<int, DateTime>::typeid, this->dotnetObject, method);
      DateTime result = fun(userId);
      return result;
    }

注意:由于DateTime是值类型,因此在进行类型申明的时候,不需要加^符号,仅需要对Func委托加上^句柄标记。

有了这2个简单的方法,我们来看看如何调用这个.NET方法“代理类”:

 NetLibProxy::UserProxy^ proxy = gcnew NetLibProxy::UserProxy("..\\NetLib\\bin\\Debug\\NetLib.dll");
  int result= proxy->GetUserID("123456");
  DateTime date = proxy->GetUserBirthday(result);
  System::Console::WriteLine("C++/CLI .Net Proxy Class Call Test Result:\r\n UserID={0},\r\n Birthday={1}",
    result,date.ToShortDateString());

OK,第一个C++/CLI代码调用成功,而且还是反射调用的,心情小激动一下。

有关C++/CLI的反射,委托的详细资料,可以参考MSDN的介绍:

https://msdn.microsoft.com/zh-cn/library/2x8kf7zx.aspx 使用 C++ 互操作(隐式 PInvoke)
https://msdn.microsoft.com/zh-CN/library/213x8e7w.aspx 泛型委托

在下一篇,我们将继续探究C++/CLI 反射调用.NET中可能遇到"深坑",因此仅打算吧本篇文章作为一个“入门”,免得大家心生恐惧,错过了挑战艰险的机会。

以上所述是小编给大家介绍的在C++中反射调用.NET的方法(一),希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

(0)

相关推荐

  • C++ 反射机制详解及实例代码

    C++ 反射机制 一.前言: Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.然而C++是不支持反射机制,虽然C++有RTTI(运行时类型识别).但是想要实现C++对象序列化,序列化就是存储到磁盘上,将对象变成一定格式的二

  • 在C++中反射调用.NET的方法(二)

    反射调用返回复杂对象的.NET方法 定义数据接口 上一篇在C++中反射调用.NET(一)中,我们简单的介绍了如何使用C++/CLI并且初步使用了反射调用.NET程序集的简单方法,今天我们看看如何在C++与.NET程序集之间传递复杂对象. 先看看.NET程序集的一个返回对象的方法: public IUserInfo GetUserByID(int userId) { IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>(); user

  • 在C++中反射调用.NET的方法(三)

    在.NET与C++之间传输集合数据 上一篇<在C++中反射调用.NET(二)>中,我们尝试了反射调用一个返回DTO对象的.NET方法,今天来看看如何在.NET与C++之间传输集合数据. 使用非泛型集合的委托方法 先看看.NET类中的一个返回列表数据的方法: //返回List或者数组,不影响 C++调用 public List<IUserInfo> GetUsers(string likeName) { List<IUserInfo> users = new List&l

  • 在C++中反射调用.NET的方法(一)

    为什么要在C++中调用.NET 一般情况下,我们常常会在.NET程序中调用C/C++的程序,使用P/Invoke方式进行调用,在编写代码代码的时候,首先要导入DLL文件,然后在根据C/C++的头文件编写特殊的C#平台调用代码,例如像下面这个样子: [DllImport("Interop.dll",EntryPoint = "Multiply",CharSet = CharSet.Ansi)] static extern int Multiply(int factor

  • php中动态调用函数的方法

    本文实例讲述了php中动态调用函数的方法.分享给大家供大家参考.具体分析如下: php中你可以动态调用函数,分为以下步骤: 1. 定义一个函数 2. 将函数名(字符串)赋值给一个变量 3. 使用变量名代替函数名动态调用函数 详细代码如下所示: <?php function addition ($a, $b){ echo ($a + $b), "\n"; } $result = "addition"; $result (3,6); ?> 希望本文所述对大家

  • C#中API调用的多种方法

    介绍 API( Application Programming Interface ),我想大家不会陌生,它是我们Windows编程的常客,虽然基于.Net平台的C#有了强大的类库,但是,我们还是不能否认API在Windows编程中的重要性.大多数的编程语言都支持API编程,而.Net平台中的MFC(Microsoft Foundation Class Library)构架本身就封装了大部分的API. 做为程序员,我们需要了解API从字面上了解便是编程接口,因此,做为开发者,需要了解的只是API

  • Node.js中安全调用系统命令的方法(避免注入安全漏洞)

    在这篇文章中,我们将学习正确使用Node.js调用系统命令的方法,以避免常见的命令行注入漏洞. 我们经常使用的调用命令的方法是最简单的child_process.exec.它有很一个简单的使用模式;通过传入一段字符串命令,并把一个错误或命令处理结果回传至回调函数中. 这里是你通过child_process.exec调用系统命令一个非常典型的例子. 复制代码 代码如下: child_process.exec('ls', function (err, data) {     console.log(

  • ubuntu中python调用C/C++方法之动态链接库详解

    安装boost python调用C/C++的方法有很多,本文使用boost.python.考虑到后期有好多在boost上的开发工作,所以boost一并安装了,Boost库分为两个部分来使用,一是直接使用对应的头文件,二是需要编译安装相应的库才可以使用. 具体安装方法可以参考:https://www.jb51.net/article/150380.htm 这里使用: sudo apt-get install libboost-all-dev 服务端 序列化后发送 main.cpp: #includ

  • 解析如何使用反射调用类型成员 方法,字段,属性

    如下所示: 复制代码 代码如下: class Program    {        static void Main(string[] args)        {            Type t = typeof(Test);            object result;            Test tc =new Test();            Console.WriteLine("Invoke a static method");            t.

  • Android利用反射机制调用截屏方法和获取屏幕宽高的方法

    想要在应用中进行截屏,可以直接调用 View 的 getDrawingCache 方法,但是这个方法截图的话是没有状态栏的,想要整屏截图就要自己来实现了. 还有一个方法可以调用系统隐藏的 screenshot 方法,来进行截屏,这种方法截图是整屏的. 通过调用 SurfaceControl.screenshot() / Surface.screenshot() 截屏,在 API Level 大于 17 使用 SurfaceControl ,小于等于 17 使用 Surface,但是 screen

  • C#高效反射调用方法类实例详解

    C#高效反射调用方法类 1.创建一个业务类(HomeService),在类下创建3个方法 2.正常方式调用类的方法 3.反射方式调用类的方法 4.调用代码 5.调用结果 6.Service类方法代码 内容扩展: 1.正常方式调用类的方法 /// <summary> /// 正常调用类的方法(parm1) /// </summary> /// <returns></returns> public string GetNormalMethod_2() { Hom

随机推荐