Reflection calls the.NET method in C++

  • 2020-05-12 02:57:46
  • OfStack

Why call.NET in C++

In general, we will often call C/C++ in the.NET program, using P/Invoke mode. When writing code, we will first import the DLL file, and then write special C# platform call code based on the C/C++ header file, such as the following:


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

For a detailed process, please refer to my previous article: "1 point difference between C# calling C and C++ functions".

Sometimes, we have in C + + call. NET demand, for example, we in the maintenance of a large C + + applications, it is so old that now need to increase 1 some new features, and these functions in. There has been a NET, you just need to call it, if in order to convenient to use. NET rewrite this C + + application is not too realistic, fortunately, C + + / CLI provides a simple scheme that can directly write in C + +. NET program, So C++/CLI stands for a combination of managed and local programming. You can use local code directly in managed code or vice versa, which combines the efficiency of C++ local code with the power of NET code, which seems to have a lot of potential.

NET programming using C++/CLI

To program C++/CLI, simply do the following:

1, add.NET assembly application;

2. Modify C++ project properties and configure properties - > Common language runtime support - common language runtime support (/clr)

However, in order to maintain the independence of C++ and.NET applications, it is required that the.NET DLL files cannot be placed in the C++ application directory, so step 1 above is not feasible and you need to use reflection in the C++ code to call.NET.

Note that the C++ reflection call in this article is not a reflection function that encapsulates C++ itself, but a reflection call in the C++/CLI code.

First, we create a solution called CppNetTest and add three projects:

1, CppConsoleTest-- 1 C++ console project, changing properties in the project to support CLR;

2, NetApp--1.NET console application, as a comparison of sample code, easy to write C++/CLI code reference;

3, NetLib-- a.NET class library assembly that will be called by reflection from items 1 and 2.

Let's start with a simple.NET class in the NetLib project, which has no complex business logic code inside its methods and is only used for reflection to call tests:


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 = " The name _" + userId;
      userinfo.Birthday = new DateTime(1980, 1, 1);
      return userinfo;
    }
    // return List Or an array, it doesn't matter  C++ call 
    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());
    }
  }
}

In the header file of the CppConsoleTest project, add 1 C++ header file of UserProxy.h, and add the following namespace to the file:


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

This allows us to use reflection-related types.

In the UserProxy class, write the constructor we need first:


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);
    }
}

Note that our C++/CLI class must be of "reference" type, so we need to add the keyword ref, that is:


public ref class UserProxy{}

All.NET reference types, when used, must be followed by a ^ symbol after the name of the type, such as the following.NET string type variable:


String^ assemblyFile; 

A variable with a ^ sign, called a "handle" object in C++/CLI, is used to distinguish it from the "pointer" of C++ native code.

In C++, members of the class use - > Symbolic calls, namespace or static members of a class, with :: calls, such as the code in the constructor above:


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

Note: in this case you need. NET class library project reference PDF. NET SOD framework, in the project of "managing Nuget package" search PDF inside. NET. SOD. Core added this reference.
With the basic syntax of C++ learned, there are no major barriers to writing C++/CLI code.

Reflection is used in C++/CLI

Reflection calls a method of the first.NET class

The following method will reflect one of the easiest methods to call the User class:


public int GetUserID(string IdString){}

This method only has 1 parameter and 1 simple return value. The following is the reflection call code of 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;
}

Notice that one Func has been created < String,int > Using a delegate simplifies our reflection calls and sometimes improves efficiency. Here's one thing to note in this code:


Func<String^, int>::typeid

This is C++/CLI's special syntax for getting the type ID of the "handle" type, which actually results in an Type object, equivalent to C#
typeof(Func<string,int>)

PS: unfortunately, in typeid mode, the type values of the following types cannot be obtained:
typeof(Func < , > ), which causes a lot of confusion when constructing generic objects dynamically.

Consider the reflection of a simple method:


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 = " The name _" + userId;
      userinfo.Birthday = new DateTime(1980, 1, 1);
      return userinfo;
    }
    // return List Or an array, it doesn't matter  C++ call 
    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());
    }
  }
}
0

Note: since DateTime is a value type, you do not need to add a ^ symbol to the type declaration, only a ^ handle symbol to the Func delegate.

With these two simple methods, let's look at how to call this.


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 = " The name _" + userId;
      userinfo.Birthday = new DateTime(1980, 1, 1);
      return userinfo;
    }
    // return List Or an array, it doesn't matter  C++ call 
    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());
    }
  }
}
1

OK, the first C++/CLI code call was successful, and it was a reflection call, a little excited 1.

For more information about reflection and delegation of C++/CLI, please refer to the introduction of MSDN:

https: / / msdn. microsoft. com/zh - cn library / 2 x8kf7zx. Use aspx C + + interoperability (implicit PInvoke)
https: / / msdn. microsoft. com/zh - CN library / 213 x8e7w aspx generic delegate

In the next article, we'll continue to explore the C++/CLI reflex call.NET may have a "deep hole" in it, so I'm just going to use this article as a "gateway" so that you don't get scared and miss out on the challenge.

Above is this site to introduce you in C++ reflection call.NET method (1), I hope to help you, if you have any questions welcome to leave me a message, this site will timely reply to you!


Related articles: