2023/1/31 更新:传递int数组
以下是原文
工作上用到一个包,是c++写的,想在c#项目里调用,折腾了好久才终于成功了。踩坑踩了一天,我也是有毒。
环境:win10 + vs2022 + NETFramework4.7.2
制作dll
创建新项目-动态链接库
主要修改以下两个文件:pch.h和pch.cpp
修改pch.cpp
// pch.cpp: 与预编译标头对应的源文件
#include "pch.h"
//这里正常写c++代码,以下函数是我举例
char* getAlign(char* query, int queryLength, char* target, int targetLength) {...实现...}
// 要求返回一个int
int getStartsCount() {...实现...}
// 要求返回一个int[]
void getStartLocations(int* N, const int count) {
for (int i = 0; i < count; i++) { ...对N[i]进行赋值... }
}
修改pch.h
只有extern那行是我写的,其他是自动生成的。
#ifndef PCH_H
#define PCH_H
// 添加要在此处预编译的标头
#include "framework.h"
extern "C" _declspec(dllexport) char* getAlign(char* query, int queryLength, char* target, int targetLength);
extern "C" _declspec(dllexport) int getStartsCount();
extern "C" _declspec(dllexport) void getStartLocations(int* N, const int count);
#endif //PCH_H
检查一下项目属性
ok了之后点击生成,输出中有下面这样的文字:
1>项目名.vcxproj -> C:\我的路径\项目名\x64\Debug\项目名.dll
========== “全部重新生成”: 1 成功,0 失败,0已跳过 ==========
c#中调用
检查项目属性
保持跟dll的一致。
调用
这里的路径当然可以改,因为我在调试,所以偷懒了。最终确定的dll应该加到c#的引用里去。
[DllImport("C:\我的路径\项目名\x64\Debug\项目名.dll")]
public static extern IntPtr getAlign(IntPtr query, int queryLength, IntPtr target, int targetLength);
[DllImport("C:\我的路径\项目名\x64\Debug\项目名.dll")]
public static extern int getStartsCount();
[DllImport("C:\我的路径\项目名\x64\Debug\项目名.dll")]
public static extern void getStartLocations([MarshalAs(UnmanagedType.LPArray,
SizeParamIndex = 1)] int[] Z, int count);
...
private void drawSeqCompCanvas() {
string query = "hello";
string target = "world";
IntPtr p = getAlign(Marshal.StringToHGlobalAnsi(query), query.Length,
Marshal.StringToHGlobalAnsi(target), target.Length);
Console.WriteLine(Marshal.PtrToStringAnsi(p));
int startCount = getStartsCount();
int[] starts = new int[startCount];
getStartLocations(starts, startCount);
}
解释一下:c++中的char*,c#是用IntPtr来接收的,所以无论是参数还是返回值,c++中的char*,在c#中都要改成IntPtr。至于Marshal那两句,就是IntPtr和String的互相转换。
对于int[],因为c++与c#不能传递数组,只能传递指针,所以需要指明长度。SizeParamIndex = 1表示index=1处是数组的长度,如果长度写在别的位置,需要修改。
常见bug
LINK : fatal error LNK1104: 无法打开文件“XXX.dll”
在制作dll时可能会出现这个问题。基本是因为另一个程序在引用这个dll,只要找到占用者,关掉即可。
System.AccessViolationException
c#调用dll出这个问题,很可能是调用dll里函数时,类型错误。比如C/C++中的unsigned short并不对应于C#中的ushort类型,而是UInt16这个类型,以及int[]传入C/C++,要用int* 接收。
我这次打包的问题则是,最开始时getAlign()接受的是string参数,但c++和c#的string似乎不一样,改成char*和IntPtr,问题解决。
参考:https://blog.csdn.net/Mr_L_K/article/details/112800806
重载min
copy代码时没注意,头文件里有下面这种,我直接copy过去了。
static inline int min(const int x, const int y) {
return x < y ? x : y;
}
本来在单独头文件+限定namespace,是没有问题的,但是跟cpp合在一起,导致代码大段飘红。。。注释掉就好了。
这是我的问题=。=