• C#通过dll调用带参数的C++代码


    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]进行赋值... }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    修改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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    检查一下项目属性

    在这里插入图片描述
    ok了之后点击生成,输出中有下面这样的文字:

    1>项目名.vcxproj -> C:\我的路径\项目名\x64\Debug\项目名.dll
    ========== “全部重新生成”: 1 成功,0 失败,0已跳过 ==========
    
    • 1
    • 2

    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);
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    解释一下: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;
    }
    
    • 1
    • 2
    • 3

    本来在单独头文件+限定namespace,是没有问题的,但是跟cpp合在一起,导致代码大段飘红。。。注释掉就好了。
    这是我的问题=。=

  • 相关阅读:
    Python手搓C4.5决策树+Azure Adult数据集分析
    uniapp 监听通知栏消息插件(支持白名单、黑名单、过滤) Ba-NotifyListener
    【MySQL进阶之路丨第十七篇(完结)】一文带你精通MySQL运算符
    部署断点调试下进入Libuv源码的环境
    基于SpringBoot的在线学生请假管理系统的设计与实现毕业设计源码060935
    Fedora Linux 38 下可以安装几个数学软件
    MongoDB之用户与权限管理、备份与恢复管理以及客户端工具的使用
    Linux 在线解压jar包
    javaEE基于springboot的小区社区文化活动报名系统jsp生活服务网站
    [360笔试]记录
  • 原文地址:https://blog.csdn.net/pxy7896/article/details/127536454