Native C++代码和托管.NET代码互操作并不是什么难事, 资料也很多, 但是有些方法复杂繁琐, 本文介绍了一种简单的可行、支持动态加载的基于CLR的交互方法.
部分其它方法可参见 在 Visual C++/Native 代码中调用.NET 程序集
微软类似例子 C++/CLI wrapper for .NET assembly (CppCLINETAssemblyWrapper), 该例子与本文类似, 只是通过编译期引用方式, 不支持动态加载.
1.首先是动态加载目标程序集和类:
try {
auto assembly = Assembly::LoadFrom(L"RLib.dll"); // 加载 RLib.dll, 可指定路径
auto type = assembly->GetType(L"RLib.Core", false, true); // 加载 RLib::Core 类型, 其中RLib是命名空间, Core是类名
if (type != nullptr){ // 类型加载成功, 上面代码指定了类型加载失败不引发异常, 某些case下这非常有用
// 注意此时 type 只是类对象, 并非实例
} //if
} catch (Exception ^ex) {
}
2.其次得到并调用类成员/方法, 本文以静态成员为例:
try {
auto proc_init = type->GetMethod(L"Init"); // 取得 RLib::Core 类型的 public static int Init(int) 方法
auto params = gcnew array<Object ^, 1>(1);
params[0] = gcnew Int32(1); // int参数, 显式装箱
auto ret = safe_cast<Int32 ^>(proc_init->Invoke(nullptr, params)); // 调用并取得返回值, 由于是静态方法, 所以第一个参数指定为 nullptr 即可
} catch (Exception ^ex) {
}
3.至此我们已经完成基本的代码交互, 但是有时候我们需要保存托管对象以便反复调用而不是重复加载, 直接加static修饰是不行的, 编译不过, 解决方案大致有两种, 一是使用GCHandle, 二是使用gcroot, 后者是对前者的包装, 十分简便, 示例如下:
#include <gcroot.h>
static gcroot<Assembly ^> assembly = Assembly::LoadFrom(L"RLib.dll"); // 加载 RLib.dll, 可指定路径
assembly->CodeBase; // 直接使用 -> 操作符访问
assembly.operator Assembly ^ () == nullptr; // 判断引用对象是否为空, 这个需要显式调用转换
这只是最基本的交互方法, 更深层次的操作可以参阅MSDN文档.