创建c++/CLI 库
创建过程主要包括三个步骤:核心C++项目,C++/CLI库,以及使用C++/CLI库的c#项目。这种方法用法应用的代表之一:游戏引擎,可以用c#编写脚本,例如Unity3D或者Xenko。但是游戏引擎会在短时间内处理大量数据,用c#编写并不是最好的办法,因此用c++编写核心功能,通过c++/CLI封装一层提供给c#.
- 环境配置
如图所示,修改VS安装,选择指示部分即“c++桌面安装”----“C++/CLI support”。 - 创建c++核心项目
打开VS创建新项目,C++项目,选择“空项目”,一般命名为“Core”,主要是核心功能实现,当然可以用其他名称。
然后设置此项目的相关属性,右击项目----点击“属性”,在常规下选择“配置类型为”----“静态库.lib”,生成静态库,被引用在c++/cli项目中,如图所示
然后在里面添加你的核心功能代码的头文件及源文件,这里添加的例子代码:
//Entity.h
#pragma once
namespace Core
{
class Entity
{
public:
const char* m_Name;
private:
float m_XPos, m_YPos;
public:
Entity(const char* name, float xPos, float yPos);
void Move(float deltaX, float deltaY);
inline float GetXPosition() const { return m_XPos; };
inline float GetYPosition() const { return m_YPos; };
};
}
//Entity.cpp
#include "Entity.h"
#include <iostream>
namespace Core
{
Entity::Entity(const char* name, float xPos, float yPos)
: m_Name(name), m_XPos(xPos), m_YPos(yPos)
{
std::cout << "Created the Entity object!" << std::endl;
}
void Entity::Move(float deltaX, float deltaY)
{
m_XPos += deltaX;
m_YPos += deltaY;
std::cout << "Moved " << m_Name << " to (" << m_XPos << ", " << m_YPos << ")." << std::endl;
}
}
这里还创建了命名为Core.h的文件,这个文件将被包含在C++/CLI项目中,因为例子中核心项目文件比较少,所以这个文件看起来不是很重要,但当文件比较多时,很有必要
//Core.h
#pragma once
#include "Entity.h"
- C++/CLI库的创建
创建C++/CLI库,这里很容易出错,在当前解决方案中新增项目,选择“c++”语言,搜索"crl",选择“CRL空项目(.NET Core)”,如图所示
项目名称为Wapper,创建完项目之后,添加对核心项目Core的引用,右键项目名称—“属性”—“项目引用”----选择Core,之后转到属性—“C/C++”—“预编译头”,然后将第一个选项更改为“不使用预编译头”。这样就设置好这个库的设置,开始文件的添加:
//ManagedObject.h
#pragma once
using namespace System;
namespace CLI {
template<class T>
public ref class ManagedObject
{
protected:
T* m_Instance;
public:
ManagedObject(T* instance)
: m_Instance(instance)
{
}
virtual ~ManagedObject()
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
!ManagedObject()
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
T* GetInstance()
{
return m_Instance;
}
};
}
ManagedObject将充当我们将在此项目中创建的所有包装器类的超类。它的唯一目的是持有一个指向Core项目中非托管对象的指针。该类包含一个析构函数(〜ManagedObject)(每当您使用delete关键字删除对象时都会调用该析构函数)和一个终结器(!ManagedObject),而该终结器在销毁C++/CLI对象时由垃圾回收器调用。
然后添加Entity.h及Entity.cpp
//Entity.h
#pragma once
#include "ManagedObject.h"
#include "../Core/Core.h"
using namespace System;
namespace CLI
{
public ref class Entity : public ManagedObject<Core::Entity>
{
public:
Entity(String^ name, float xPos, float yPos);
void Move(float deltaX, float deltaY);
property float XPosition
{
public:
float get()
{
return m_Instance->GetXPosition();
}
private:
void set(float value)
{
}
}
property float YPosition
{
public:
float get()
{
return m_Instance->GetYPosition();
}
private:
void set(float value)
{
}
}
};
}
//Entity.cpp
#include "Entity.h"
namespace CLI
{
Entity::Entity(String^ name, float xPos, float yPos)
: ManagedObject(new Core::Entity(string_to_char_array(name), xPos, yPos))
{
Console::WriteLine("Creating a new Entity-wrapper object!");
}
void Entity::Move(float deltaX, float deltaY)
{
Console::WriteLine("The Move method from the Wrapper was called!");
m_Instance->Move(deltaX, deltaY);
}
}
除了将.NET字符串转换为C ++ const char *之外,您可能还需要将.NET数组转换为C ++数组。即使它们看起来相同,它们之间也有很大的区别:.NET数组是对象,而C ++数组只是指向第一个元素的指针。
using namespace System::Runtime::InteropServices;
static const char* string_to_char_array(String^ string)
{
const char* str = (const char*)(Marshal::StringToHGlobalAnsi(string)).ToPointer();
return str;
}
static void int_array_conversion(array<int>^ data)
{
pin_ptr<unsigned int> arrayPin = &data[0];
unsigned int size = data->Length;
}
- 创建C#项目使用以上C++/CLI库
在此解决方案中再添加一个C#项目,并测试我们是否能够访问C ++功能。右键单击该解决方案,然后添加一个新的C#控制台应用程序;名称为“SandBox”。
然后与之前同样的方法,将Wrapper项目引用添加至SandBox中。测试代码:
using System;
using CLI;
namespace Sandbox
{
class Program
{
static void Main(string[] args)
{
Entity e = new Entity("The Wallman", 20, 35);
e.Move(5, -10);
Console.WriteLine(e.XPosition + " " + e.YPosition);
Console.Read();
}
}
}
这样就完成了,让我们看看结果吧,yeah!成功了,赶快创建你的项目吧