C#与C++动态链接库DLL参数互传方式

 更新时间:2022年11月08日 09:08:00   作者:weixin_46846685  
这篇文章主要介绍了C#与C++动态链接库DLL参数互传方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

一、C#中导入C++动态链接库

从界面程序开发的角度来说,C#语言效率较C++高,且通过WPF开发出的程序界面更为美观,但在开发实际项目中有时不可避免的需要使用C++程序库,通常的做法是将C++程序编译为动态链接库,及DLL文件,然后在C#中进行导入调用。

导出C++程序通常的做法是使用_declspec(dllexport) /_declspec(dllimport)来导入导出,C++示例代码如下:

#include <iostream>
using namespace std;
extern "C" _declspec(dllexport) void TestDll(char* Path, char* result);
_declspec(dllexport) void TestDll(char* Path, char* result)
{
***//函数功能具体实现
}

以VS2019中编译Dll为例,打开项目属性窗口,点击配置属性——常规,将配置类型选择为动态库(.dll),然后点击配置属性——高级,将目标文件扩展名选择为.dll,然后设置好解决方案配置和平台后生成dll文件。

导出为Dll文件后,例如导出文件名为TestDll.dll,在C#中调用示例代码如下:

[DllImport("TestDll.dll", EntryPoint = "TestDll")]
static extern void TestDll(string Path, [Out, MarshalAs(UnmanagedType.LPArray)] char[] result);

下面具体讲如何在C#与C++之间实现参数传递。

二、C#传入字符串参数

参数传递主要涉及C#调用dll文件时传入dll参数和调用结束dll文件传出参数。通常传入dll的参数类型为整数类型,整数数组类型和字符串类型。

在C#和C++中整数类型通常都为int类型,在参数传入时直接传入即可。

但对于字符串类型,C#中为string类型,而C++中通常是使用字符数组来存储字符串,即char[]或char*类型,而C++中也使用std::string类型来存储字符串,但在实际使用过程中发现当C++中接收参数类型是该类型时会报出错误,

示例代码如下:

//C++代码
#include <iostream>
using namespace std;
extern "C" _declspec(dllexport) void TestDll(char* Path);
_declspec(dllexport) void TestDll(char* Path)
{
***//函数功能具体实现
}
//C#代码
[DllImport("TestDll.dll", EntryPoint = "TestDll")]
static extern void TestDll(string Path);
string path = "hello";
TestDll(path);

上述代码中,C#传入字符串类型为string类型,而C++接收参数类型为char*类型,经实际测试,中文字符和英文字符都可以正确传输。

三、C++传出字符串参数

C++传出字符串参数较C#传入更为复杂,因C++中字符存储是以指针形式,所以可以通过如下方式来实现:C#传入一个数组参数,传入后C++对该数组指针进行赋值,然后传出。

实现方式有两种,一种为C#传入char数组类型,一种为C#传入byte数组类型,示例代码如下:

//C++代码
#include <iostream>
using namespace std;
extern "C" _declspec(dllexport) void TestDll(char* result);
_declspec(dllexport) void TestDll(char* result)
{
char s[20]="hello";
memcpy(result, s, strlen(s));
}

memcpy为C++内存拷贝函数,使用时需要注意strlen与sizeof函数的区别,两者都是获得变量的字节数,不同的是strlen只适用于char*类型,当遇到’\0’字节时停止计数,而sizeof适用于多种类型和对象,当用于数组类型时获得的时初始化时分配的字节数,而不是实际使用的字节数。

//C#代码
[DllImport("TestDll.dll", EntryPoint = "TestDll")]
static extern void TestDll([Out, MarshalAs(UnmanagedType.LPArray)] char[] result);
char[] result = new char[100];
TestDll(result);
string re = new string(result);
Console.WriteLine(re);

在C#中传入char[]即字符数组类型,待C++中对result赋值完成后再取出result,对字符串re赋值,这样就实现了C++字符串参数的传出。

需要注意的是在C#中数组是直接使用的,而在C++中返回的是数组的指针,[Out, MarshalAs(UnmanagedType.LPArray)]用来转化这两种不同的类型。

上述实现方式为传入char数组类型方式,但如果C++传出的字符串包含中文字符,那么可能在C#中会显示乱码,因为中文字符为UTF-8编码。

下面介绍使用byte数组方式,C++中代码不变,C#中代码如下:

//C#代码
[DllImport("TestDll.dll", EntryPoint = "TestDll")]
static extern void TestDll(ref byte t);
byte[] t = new byte[100];
TestDll(path, ref t[0]);
string re = Encoding.UTF8.GetString(t);
Console.WriteLine(re);

byte数组方式传输中文字符不算出现乱码情况。

四、C++传出vector<char*>参数

vector类型为C++中类似于列表的数据类型,能够自由添加、插入、删除元素,与C#中List类型功能相似。

但当要在C++传出vector类型时,在C#端的接收数据类型却不能为List,否则会报出错误,而应该使用C#中的IntPtr类型,示例代码如下:

//C++代码
#include <iostream>
using namespace std;
extern "C" _declspec(dllexport) void TestDll(int& num, char** &result);
_declspec(dllexport) void TestDll(int& num, char** &result)
{
std::vector<char*> res;
char* tmp = new char[5];
char s[20]="hello";
memcpy(tmp, s, strlen(s));
res.push_back(tmp);
num = 1;
result = res.data();
}

在上述C++代码中主要通过res.data()函数实现将vector<char*>类型转换为char**类型,从而方便C#端读取,而num变量而表示vector类型的元素个数,C#端代码如下:

//C#代码
[DllImport("TestDll.dll", EntryPoint = "TestDll")]
static unsafe extern void TestDll(ref int num, ref IntPtr t);
int num = 0;
IntPtr data = IntPtr.Zero;
TestDll(ref num, ref data);
for (int i = 0; i < num; ++i)
{
   int size = Marshal.SizeOf(typeof(IntPtr));
   IntPtr intPtr = Marshal.ReadIntPtr(data, size * i);
   string datastr = Marshal.PtrToStringAnsi(intPtr);
   Console.WriteLine(datastr);
}

C#代码中根据num个数依次从内存中读取字符串,从内存中读取字符串函数为Marshal.PtrToStringAnsi(intPtr),需要注意的是如果C++传出的包含中文字符,那么在C#端可能显示乱码。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Unity中的Tilemap流程分析

    Unity中的Tilemap流程分析

    这篇文章给大家介绍Unity中的Tilemap流程分析,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-07-07
  • Js中的substring,substr与C#中的Substring比较

    Js中的substring,substr与C#中的Substring比较

    本篇文章主要是对Js中的substring,substr与C#中的Substring进行了比较。需要的朋友可以过来参考下,希望对大家有所帮助
    2014-01-01
  • C#检查foreach判读是否为null的方法

    C#检查foreach判读是否为null的方法

    这篇文章主要介绍了C#如何检查foreach判读其是否为null,文中给出了示例代码,介绍的很详细,需要的朋友可以参考下方法
    2016-09-09
  • C#编程实现Excel文档中搜索文本内容的方法及思路

    C#编程实现Excel文档中搜索文本内容的方法及思路

    有了在Word文档中编程实现搜索文本的经验,在Excel中实现这个功能也并非难事。
    2013-07-07
  • C#实现窗体全屏的两种方法

    C#实现窗体全屏的两种方法

    这篇文章主要为大家详细介绍了C#实现窗体全屏的两种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • C#生成exe并用process运行的流程步骤

    C#生成exe并用process运行的流程步骤

    这篇文章主要介绍了C#生成exe并用process运行的流程步骤,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-08-08
  • C#版Tesseract库的使用技巧

    C#版Tesseract库的使用技巧

    本文给大家分享C#版Tesseract库的使用技巧,在这里大家需要注意一下tesseract的识别语言包要自己下载后包含到项目里面,并设置为始终复制,或者直接把这个文件包放到运行程序目录(bin\debug)下的,具体实现代码跟随小编一起学习下吧
    2021-05-05
  • 详解C# 虚方法virtual

    详解C# 虚方法virtual

    这篇文章主要介绍了C# 虚方法virtual的相关资料,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-04-04
  • Unity Shader实现纹理遮罩效果

    Unity Shader实现纹理遮罩效果

    这篇文章主要为大家详细介绍了Unity Shader实现纹理遮罩效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • C# 泛型数组学习小结

    C# 泛型数组学习小结

    C# 泛型数组学习中我们需要注意什么事项呢?C# 泛型数组的使用又是如何呢?那么本文就向你详细介绍这方面的内容
    2012-09-09

最新评论