终于讲完了后端代码,要涉及到前端的内容了。作为C++写的后台,而在Unity中是用C#中作为脚本语言。因此后台的代码想在前端进行调用,需要讲C++部分打包成Dll的动态链接库,约定好对应的格式,即可在C#中调用。
首先需要了解C++/C#之前的主要区别,两种语言在语法上和关键字上很相似。这里体现的最主要的区别C#是托管堆代码,C++是非托管堆代码。在数据分配上C++每当用new等关键字申请空间时,编译器会在堆上为其分配内存,在使用完成后需要delete等回收空间,尤其注意在局部函数中申请的空间,一旦丢失了指向申请内存的指针,那么就会造成内存泄漏,即申请的内存空间和数据依然放在堆中,但是再也没办法使用和回收它(因为失去了地址去定位),当程序一直运行时,无效内存会越来越多,最终导致内存溢出崩溃(这就是我不怎么习惯用C++的一种原因,虽然现在已经有了auto_ptr等) 。
而C#中的托管堆代码即将内存管理交给了OS和编译器,当一段内存没有被任何指针或变量引用后,就会由system.GC()自动回收 。基于这两种语言的特性,在C++中设计函数参数类型和数据交换时要考虑兼容性。
这里列举一些常用的数据类型对应:
| Windows Data Type | .NET Data Type |
| BOOL, BOOLEAN | Boolean or Int32 |
| BSTR | String |
| BYTE | Byte |
| CHAR | Char |
| DOUBLE | Double |
| DWORD | Int32 or UInt32 |
| FLOAT | Single |
| HANDLE (and all other handle types, such as HFONT and HMENU) | IntPtr, UintPtr or HandleRef |
| HRESULT | Int32 or UInt32 |
| INT | Int32 |
| LANGID | Int16 or UInt16 |
| LCID | Int32 or UInt32 |
| LONG | Int32 |
| LPARAM | IntPtr, UintPtr or Object |
| LPCSTR | String |
| LPCTSTR | String |
| LPCWSTR | String |
| LPSTR | String or StringBuilder* |
| LPTSTR | String or StringBuilder |
| LPWSTR | String or StringBuilder |
| LPVOID | IntPtr, UintPtr or Object |
| LRESULT | IntPtr |
| SAFEARRAY | .NET array type |
| SHORT | Int16 |
| TCHAR | Char |
| UCHAR | SByte |
| UINT | Int32 or UInt32 |
| ULONG | Int32 or UInt32 |
| VARIANT | Object |
| VARIANT_BOOL | Boolean |
| WCHAR | Char |
| WORD | Int16 or UInt16 |
| WPARAM | IntPtr, UintPtr or Object |
这里只用了一些比较常用的。比如整数类型DWORd(C++)对应uint(C#),字符串传值时使用char*(C++)对应string/stringBuilder(C#)。
其次是在C++中导出Dll,用vs可以很简单的创建一个C++Dll工程。
然后在.cpp文件写好函数,这里拿项目中需要得到一个string的哈希值做例子
DWORD Hash4(const string& tar) //计算哈希值函数
{
DWORD64 ans = 0;
for (int i = 0; i < tar.length(); i++) {
ans = ((ans >> 8) & 0xf) ^ ((ans << 4) ^ tar[i]);
}
return ans & 0xfff;
}
DWORD GetHash(char* tar)
{
string temp = tar;
return Hash4(temp);
}
在.h中补充函数原型,添加上extern "C" _declspec(dllimport)语句
#ifdef DL1_API #else #define DL1_API extern "C" _declspec(dllimport) //交给客户端使用时 #endif DL1_API DWORD GetHash(char* tar);
最后在C#中使用[DllImport]导入, static extern 并对上函数原型参数列表,中间的名称为打包出来的Dll文件名,注意如果是在unity中想使用dll,需要放在对应的assert/plugin文件夹中,即可加载dll数据库。只是普通的C#控制台程序和执行文件同目录即可
注意制力用了char*与c#中的string交换,c++中的string不论是传出还是传入数据都是和C#对不上的,所以需要用到char*,中间还会涉及到一些编码问题。而c#中string和stringBuilder都可以在C++中被接受到。
[DllImport("Dll2")]
public static extern uint GetHash(string tar); //需要对应上参数类型和函数名
如果需要C#传入一个字符串,在C++中接收并进行修改,然后再由C#中得到结果,则需要用到stringBuilder,stringBuilder与string最大的不同在于stringBuilder可以预先分配空间,能够被多次的修改,并且不产生新的未使用对象(而string拷贝多了都是创建一个新的对象,非常耗时间)。在传值的时候建议使用stringBuilder,这里再拿程序中用到的搜索功能样例。
C++中函数原型:
void articleInfo(char* 文章名, char* 数据库地址, char* 结果);//输入文章名,返回文章的所有相关信息
C#中:
using System.Text;
StringBuilder stringBuilder = new StringBuilder(50000);
[DllImport("Dll1")]
public static extern void articleInfo(String articleName, String saveUrl, StringBuilder pt);
至此,一个简单的Dll程序就这样设计完成,这里展示下程序中实际用到的所有接口。
class DLL
{
public Thread thread;
StringBuilder stringBuilder = new StringBuilder(50000);
[DllImport("Dll1")]
public static extern void articleInfo(String articleName, String saveUrl, StringBuilder pt);
[DllImport("Dll1")]
public static extern void main_Reader(String authorName, String saveUrl, StringBuilder pt);
[DllImport("Dll1")]
public static extern bool CheckFilePathCorrect_Compatible(string filePath, StringBuilder refS);
[DllImport("Dll1")]
public static extern bool CheckFilePathCorrect(string filePath);
[DllImport("Dll1")]
public static extern void CoAuthor(string search_name, string _saveUrl, StringBuilder pt);
[DllImport("Dll1")]
public static extern void initial_fuzzy(string filePath);
[DllImport("Dll1")]
public static extern int fuzzy_search(string filePath, uint threadNum, string content);
[DllImport("Dll1")]
public static extern void release_fuzzy_searcher();
[DllImport("Dll1")]
public static extern void Keyword_Sort(uint year, string filePath); //年份排序
[DllImport("Dll2")]
public static extern uint GetHash(string tar); //需要对应上参数类型和函数名
[DllImport("Dll2")]
public static extern bool ChechFilePathCorrect_dblp(string saveUrl);
[DllImport("Dll1")]
public static extern bool initial_readers(uint totalThread, uint maxThread, bool debugText, string saveUrl);//建库
[DllImport("Dll1")]
public static extern bool Author_Sort(string saveUrl); //作者排序
[DllImport("Dll1")]
public static extern int Full_word_match_fuzzy_search(string filePath, uint threadNum, string content);
[DllImport("Dll1")]
public static extern uint Format_Hash4(string tar); //哈希值
//C++中的原型
}



