最近在工作中的一个项目,需要使用C#编写的SDK, 而我们项目的代码是C++,如何利用C++调用C#SDK便成了一个问题。经过一番谷歌百度,最终采用CLR实现了C++对C#的调用。具体过程如下图所示
假设在C#项目中我们现在有一个API “PrintNameToScreen()”, 我们通过例子一步一步来看如何具体实现。
Step 1. 使用C# 生成dll., 项目名称叫做“DemoHelperLib”;
a. 打开VS2015, 选择Visual C# 的’ClassLibray’选项
b.项目生成后,配置项目属性, 其中 “Platform target”选择‘x64’, 而Outputpath 则是生成dll的文件夹,最终的大型项目一般要配置到主项目的exe路径下,这个后面会说到。
c.修改代码内容,VS默认生成的文件名和类名是class1, 为了使用方便,把类名改成DemoHelper,
并添加以下内容
void PrintNameToScreen(string sName)
{
Console.Write("Hello" + sName);
}
编译,在项目路径下的bin文,可以发现’DemoHelperLib.dll’已经生成,step1 到这里就大工告成啦。
Step2.建立C++ CLR 工程,生成library
a. 用VS2015新建一个visaul C++ CLR class library项目,如下图所示,项目名叫“DemoHelpCLRLib”
b. 打开项目设置,设置‘Use MFC in shared DLL ’
c. 在生成的“DemoHelpCLRLib.h” 头文件中,引入刚才生成的“DemoHelper.dll“的路径,还记得在step1中说的dll生成路径吗? 你可以把该路径直接配置成CLR项目的生成路径,对应本例是“D:\MoFei\Demo\DemoHelpCLRLib\x64\Debug”, 或者手动从bin folder下拷过来,不过不建议这样做,不然每次C#项目有更新,都要重新拷贝一遍,很麻烦的。直接设置好生成路径即可。
在头文件引入
#using "..\x64\Debug\DemoHelpLib.dll"
头文件添加如下代码, 设计一个类‘DemoHelperCLRModule’用来作为管理类,通过gcroot托管对象以实现调用C# API的效果。
// DemoHelpCLRLib.h
#pragma once
#include
#using "..\x64\Debug\DemoHelpLib.dll"
using namespace System;
using namespace System;
using namespace System::Windows;
using namespace System::Runtime;
using namespace System::Runtime::InteropServices;
class DemoHelperCLRModule
{
public:
DemoHelperCLRModule(void);
~DemoHelperCLRModule(void);
protected:
gcroot<DemoHelpLib::DemoHelper^> m_CFXHelper;
public:
bool PrintNameToScreen(CString sName );
};
在源文件中代码如下:
#include "stdafx.h"
#include "DemoHelpCLRLib.h"
//重要,建立CLR托管对象
DemoHelperCLRModule::DemoHelperCLRModule(void)
{
m_CFXHelper = gcnew DemoHelpLib::DemoHelper();
}
DemoHelperCLRModule::~DemoHelperCLRModule(void)
{
}
bool DemoHelperCLRModule::PrintNameToScreen(CString szName)
{
bool bRet(true);
if (m_CFXHelper) //个人习惯,可以不要
{
//CLR 访问要求,建立新对象时必须要用^, 具体可以查询CLR语法。
String^ sMyName = Marshal::PtrToStringAnsi((IntPtr)szName.GetBuffer());
//调用C# API
m_CFXHelper->PrintNameToScreen(sMyName);
}
return bRet;
}
d. 做完上面的步骤,编译过后,在项目路径“x64\Debug”下面我们得到了"DemoHelpCLRLib.dll", 在主项目中添加该dll 和对应头文件,便可以使用了,但通常在实际项目中,我们还会包一层导出管理类"DemoHelperModule"供主项目调用,这样主项目就避免了直接访问CLR托管项目,实现了解耦的目的。体系结构如下图所示:
因此在CLR工程下再添加一对源文件和头文件‘DemoHelpModule’,
在头文件中添加
#pragma once
#include
class DemoHelperCLRModule; //CLR 类前置声明
using namespace std;
class AFX_EXT_CLASS DemoHelpModule //导出类
{
public:
DemoHelpModule(void);
~DemoHelpModule(void);
private:
DemoHelperCLRModule* m_pCLRModule; //CLR 托管类指针,为了访问CLR托管类
public:
bool PrintYourNameToScreen(CString szName);
};
在源文件文件中添加:
#include "StdAfx.h"
#include "DemoHelpModule.h"
#include "DemoHelpCLRLib.h"
DemoHelpModule::DemoHelpModule(void)
{
m_pCLRModule = new DemoHelperCLRModule();
}
DemoHelpModule::~DemoHelpModule(void)
{
if (m_pCLRModule != NULL)
{
delete m_pCLRModule;
m_pCLRModule = NULL;
}
}
bool DemoHelpModule::PrintYourNameToScreen(CString szName)
{
bool bRet = m_pCLRModule->PrintNameToScreen(szName);
return bRet;
}
到这里大功告成,我们已经生成想要的DemoHelpCLR.lib"与 “DemoHelpCLR.dll”,一般默认在x64文件夹下,把这两个文件拷贝到C++主项目中,便可以愉快的调用C# API啦。
Step 3 建立主项目(调用C# 的c++项目)
我们以"Test Demo 为例", 建立一个win32 console application 空项目,我就不放图示过程了,这里主要想说如何把刚在两个我们建立的C# 项目和CLR项目添加进来,然后如何在C#里进行debug。
a. 去到我们已有的CLR项目文件夹‘DemoHelpCLRLib’ 下, 复制项目(注意不是solution)所在文件夹,
把他拷贝到我们的TestDemo 的项目文件夹下, 同样的操作拷贝C#项目文件下的‘DemoHelpLib’文件夹到 'TestDemo’文件夹下,拷贝完成后如图所示
b. 在刚才建立好的空项’TestDemo’中,右击项目(solution)名称,然后添加已有项目,分别把刚才我们的拷贝的C#项目和CLR项目添加进来
添加好后,此项项目列表应该如下图所示,在TestDemo solution下面有3个工程,分别是’DemoHelpCLRLib’,'DemoHelpLib’与‘TestDemo’
C. 配置C#项目dll输出路径,该路径实际上就是我们’TestDemo’的exe生成文件目录,一般就是在’x64/Debug’中
d.配置主项目的依赖头文件路径和依赖库,即我们‘TestDemo’需要用到的lib以及头文件。
首先头文件配置,添加管理类‘DemoHelperModule’头文件所在路径,
e.其次添加依赖库路径以及 ‘DemoHelpCLRLib.lib’
到此大功告成,我们在主项目‘TestDemo’下建一个源文件 ‘TestDemo.cpp’,输入以下测试代码
#include
#include "DemoHelpModule.h"
int main()
{
DemoHelpModule* pTest = new DemoHelpModule;
CString szName = "Tom";
pTest->PrintYourNameToScreen(szName);
system("Pause");
return 0;
}
运行,屏幕上会打印出 ‘HelloTom’
常见编译错误:
1.MFC share library error.
2. Can not resolve symbol
将C++主项目和CLR项目的use of MFC 设置成"Use MFC in a shared DLL" , 将 Character Set 设置统一格式,此例中设为‘Not set’
3.C# dll output 路径设置不正确。
4.主项目没有添加头文件依赖路径
5.主项目没有添加.lib文件依赖路径与lib路径。
6.把主项目设置成为启动项。
请逐一检查上述设置是否正确,然后多编译几次,因为有文件生成,一般第一遍编译有error,再编译一次就好了。
好了到此我们已经可以在C++项目调用C# API了, 那么如何在C#中进行调试呢?只要在C#项目中设置主项目exe路径即可。
在C# 项目设置选项中,在Debug选项那一栏设置主项目exe路径,如下图所示
然后选择以C# debug 方式启动项目,即可在C# API 重加断点进行调试
到此,我们可以愉快的在我们C++项目中使用C# API啦!