在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 = ;
int.TryParse(IdString, out result);
return result;
} public DateTime GetUserBirthday(int userId)
{
return new DateTime(, , );
} public IUserInfo GetUserByID(int userId)
{
IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>();
userinfo.ID = userId;
userinfo.Name = "姓名_" + userId;
userinfo.Birthday = new DateTime(, , ); return userinfo;
} //返回List或者数组,不影响 C++调用
public List<IUserInfo> GetUsers(string likeName)
{
List<IUserInfo> users = new List<NetLib.IUserInfo>();
for (int i = ; i < ; 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("");
    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(一)的更多相关文章
- 在C++中反射调用.NET(二)
		
反射调用返回复杂对象的.NET方法 定义数据接口 上一篇在C++中反射调用.NET(一)中,我们简单的介绍了如何使用C++/CLI并且初步使用了反射调用.NET程序集的简单方法,今天我们看看如何在C+ ...
 - 在C++中反射调用.NET(三)
		
在.NET与C++之间传输集合数据 上一篇<在C++中反射调用.NET(二)>中,我们尝试了反射调用一个返回DTO对象的.NET方法,今天来看看如何在.NET与C++之间传输集合数据. 使 ...
 - [LinqPad妙用]-在Net MVC中反射调用LinqPad中的Dump函数
		
LinqPad有个非常强大的Dump函数.这篇讲解一下如何将Dump函数应用在.Net MVC Web开发中. 先看效果: 一.用.Net Reflector反编译LinqPad.exe,找出Dump ...
 - Java实现Qt的SIGNAL-SLOT机制(保存到Map中,从而将它们关联起来,收到信号进行解析,最后反射调用)
		
SIGNAL-SLOT是Qt的一大特色,使用起来十分方便.在传统的AWT和Swing编程中,我们都是为要在 监听的对象上添加Listener监听器.被监听对象中保存有Listener的列表,当相关事件 ...
 - 尝试解决在构造函数中同步调用Dns.GetHostAddressesAsync()引起的线程死锁
		
(最终采用的是方法4) 问题详情见:.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长 看看在 Linux 与 Windows 上发生线程死锁的后果. Linux: Microsoft ...
 - C#如何反射出委托的签名,如何使用反射调用委托
		
本文阐述C#中如何反射出委托的签名,假如我们有委托FooDelegate定义如下 delegate double FooDelegate (string param, bool condition); ...
 - JNI中C调用Java方法
		
背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...
 - 从vs2010的UnitTestFramework类库提取私有方法反射调用的方法
		
背景 年龄大点的程序员都知道在vs2010中创建单元测试非常的简单,鼠标定位在方法名字,右键创建单元测试,就会创建一个测试方法,即使是在私有方法上也可以创建测试方法. VS2010以后就没这么简单了, ...
 - python 反射调用
		
因为目前在写一个python的项目,用到了Python的反射机制,所以做一下笔记,把写项目过程中的感悟记下来. 先简单介绍下Demo用到的函数: sys.path 是python的模块的路径集,是一个 ...
 
随机推荐
- Myeclipse创建新项目
			
1. 打开myeclipse, 配置mysql server preference里找到 show view-- DB Browser, 新建数据库驱动. 1. URL填写: jdbc:mysql:/ ...
 - iOS之NSDictionary和NSArray以及NSMutableDictionary和NSMutableArray:将不再是问题
			
字典的key就相当于数组的下标,怎样操作数组你就学会了怎样才做字典:来感受一把 需要注意的是读取文件的类型要集合.plist文件的rooty:否则将不能读入文件 // 1.全国省市:得到的是省和市 / ...
 - (中等) POJ 1054 The Troublesome Frog,记忆化搜索。
			
Description In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a we ...
 - 从内存中加载DLL Delphi版(转)
			
源:从内存中加载DLL DELPHI版 原文 : http://www.2ccc.com/article.asp?articleid=5784 MemLibrary.pas //从内存中加载DLL D ...
 - 在Action类中获得HttpServletResponse对象的四种方法
			
在struts1.xAction类的execute方法中,有四个参数,其中两个就是response和request.而在Struts2中,并没有任何参数,因此,就不能简单地从execute方法获得Ht ...
 - VSC#2010打开视图编辑器假死/卡死
			
最近写项目代码的时候写C#,VSC#2010刚配置好,打开之前同学写的项目的设计界面结果...我擦,卡死了,重复几次都是这样. 于是上网搜发现是VS一个bug,打一个VSSP1补丁就好了~ http: ...
 - 制作毛玻璃效果                                                    分类:            ios技术             2015-07-14 09:03    240人阅读    评论(0)    收藏
			
//添加一个图片 UIImageView *imageview = [[UIImageView alloc]init]; imageview.frame = CGRectMake(10 ...
 - Linux下部署
			
moto66@163.com 0144586wei 郭 2015/3/23 星期一 9:29:38 服务器密码:0144586Wei120.26.62.247
 - iOS开发 调用系统相机和相册                                                    分类:            ios技术             2015-03-30 15:52    65人阅读    评论(0)    收藏
			
调用系统相机和相册 (iPad,iPhone) 打开相机:(iPad,iPhone) //先设定sourceType为相机,然后判断相机是否可用(ipod)没相机,不可用将sourceType设定为 ...
 - Linux文件锁学习-flock, lockf, fcntl
			
参考 linux中fcntl().lockf.flock的区别 这三个函数的作用都是给文件加锁,那它们有什么区别呢? 首先flock和fcntl是系统调用,而lockf是库函数.lockf实际上是f ...