项目用C#开发,但是有一些希望重用之前的C++代码,于是研究了如何在C#中调用C++的DLL。
C++中的函数声明
view source
print?
1 | extern "C" __declspec(dllexport) int __stdcall testfunc(char* astr,int* a); |
extern ”C”
通常来说,C++编译器可能会改变函数和变量的名字,从而导致严重的链接程序问题。例如,假设使用C++编写一个DLL,当创建DLL时,Microsoft的编译器就会改变函数的名字。函数名将被设置一个前导下划线,再加上一个@符号的前缀,后随一个数字,表示作为参数传递给函数的字节数。例如,下面的函数是作为DLL的输出节中的_MyFunc@8输出的:
view source
print?
1 | __declspec(dllexport) LONG __stdcall MyFunc(int a, int b); |
如果用另一个供应商的工具创建了一个可执行模块,它将设法链接到一个名叫MyFunc的函数,该函数在Microsoft编译器已有的DLL中并不存在,因此链接将失败。
使用extern “C”关键字可以使编译器按照C语言的方式编译DLL文件,即编译时不改变函数名。
__declspec(dllexport)
在 32 位编译器版本中,可以使用__declspec(dllexport) 关键字从DLL导出数据、函数、类或类成员函数。__declspec(dllexport) 会将导出指令添加到对象文件中,因此不需要使用.def文件。
若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:
view source
print?
1 | __declspec(dllexport) void __cdecl Function1(void); |
__stdcall
表明被调用方清理堆栈。
C#中的函数声明
view source
print?
1 | using System.Runtime.InteropServices; |
2 | … |
5 | { |
6 | [DllImport(@"E:\Projects\testdll\debug\testdll.dll")] |
7 | public static extern int testfunc(StringBuilder abuf,ref int a); |
8 | } |
using System.Runtime.InteropServices;
System.Runtime.InteropServices 命名空间提供各种各样支持 COM interop 及平台调用服务的成员,使程序可以与非托管代码进行交互操作。
[DllImport(“dllfile path”)]
代码中DllImport关键字作用是告诉编译器入口点在哪里,并将打包函数捆绑在这个类中。在声明的时候还可以添加几个属性:
view source
print?
1 | [DllImport("MyDLL.dll", |
2 | EntryPoint="mySum", |
3 | CharSet=CharSet.Auto, |
4 | CallingConvention=CallingConvention.StdCall)] |
EntryPoint: 指定要调用的 DLL 入口点。默认入口点名称是托管方法的名称 。
CharSet: 控制名称重整和封送 String 参数的方式 (默认是UNICODE)
CallingConvention指示入口点的函数调用约定(默认WINAPI)
注意:必须在标记为”static”和”extern”的方法上指定”DllImport”属性。
数据传递方法
1.基本数据类型的传递
函数参数和返回值可以是C#和C++的各种基本数据类型,如int, float, double, char(注意不是char*)等。
示例:
C#代码:
view source
print?
01 | using System; |
02 | using System.Text; |
03 | using System.Runtime.InteropServices; |
04 | |
07 | [DllImport(@"E:\Projects\testdll\debug\testdll.dll")] |
08 | public static extern int testfunc(int a,float b,double c,char d); |
09 | |
10 | static void Main(string[] args) |
13 | float b = 12; |
14 | double c = 12.34; |
15 | char d = 'A'; |
16 | testfunc(a,b,c,d); |
17 | Console.ReadKey(); |
18 | } |
C++代码:
view source
print?
01 |
#include <iostream></iostream>
|
02 | using namespace std; |
05 | { |
06 | _declspec(dllexport) int __stdcall testfunc(int a,float b,double c,char d) |
2.向DLL传入字符串
C#中使用string定义字符串,将字符串对象名传给DLL。
注意:在DLL中更改字符串的值,C#中的值不会改变。
C#代码:
view source
print?
01 | using System; |
02 | using System.Text; |
03 | using System.Runtime.InteropServices; |
04 | |
07 | [DllImport(@"E:\Projects\testdll\debug\testdll.dll")] |
08 | public static extern int testfunc(string a); |
09 | |
10 | static void Main(string[] args) |
11 | { |
12 | string a="Hello World!"; |
13 | testfunc(a); |
14 | Console.ReadKey(); |
C++代码:
view source
print?
01 | #include <iostream></iostream> |
02 | using namespace std; |
05 | { |
06 | _declspec(dllexport) int __stdcall testfunc(char* astr) |
09 | *astr='A';//更改字符串的数据 |
10 | cout
|
3.DLL传出字符串
C#中使用StringBuilder对象创建变长数组,并设置StringBuilder的Capacity为数组最大长度。将此对象名传递给DLL,使用char*接收。
C#代码:
01 | using System; |
02 | using System.Text; |
03 | using System.Runtime.InteropServices; |
04 | |
07 | [DllImport(@"E:\Projects\testdll\debug\testdll.dll")] |
08 | public static extern int testfunc(StringBuilder abuf); |
09 | |
10 | static void Main(string[] args) |
11 | { |
12 | StringBuilder abuf=new StringBuilder(); |
13 | abuf.Capacity = 100;//设置字符串最大长度 |
14 | testfunc(abuf); |
15 | Console.ReadKey(); |
16 | } |
C++代码:
view source
print?
01 | #include <iostream></iostream> |
02 | using namespace std; |
05 | { |
06 | _declspec(dllexport) int __stdcall testfunc(char* astr) |
09 | *astr++='b';//C#中abuf随astr改变 |
10 | *astr='\0'; |
4.DLL传递结构体(需要在C#中重新定义,不推荐使用)
C#中使用StructLayout重新定义需要使用的结构体。
注意:在DLL改变结构体成员的值,C#中随之改变。
C#代码:
view source
print?
01 | using System; |
02 | using System.Text; |
03 | using System.Runtime.InteropServices; |
04 | |
05 | [StructLayout(LayoutKind.Sequential)] |
06 | public struct Point |
13 | { |
14 | [DllImport(@"E:\Projects\testdll\debug\testdll.dll")] |
15 | public static extern int testfunc(Point p); |
16 | |
17 | static void Main(string[] args) |
18 | { |
19 | Point p; |
20 | p.x = 12.34; |
21 | p.y = 43.21; |
22 | testfunc(p); |
23 | Console.ReadKey(); |
24 | } |
C++代码:
view source
print?
01 | #include <iostream></iostream> |
02 | using namespace std; |
11 | { |
12 | _declspec(dllexport) int __stdcall testfunc(Point p) |
分享到:
相关推荐
C#调用C++编写的dll,一般都是直接用dllimport,这个资料很多。C++调用C#编写的dll,一般方法都是先用托管C++将C#的dll进行一次封装,然后由非托管C++调用封装好的dll。
在VS2010上使用C#调用非托管C++生成的DLL文件
在C#编程中,常会用到C++中的dll类库,简单介绍一下它们的对应关系。
C#调用非托管DLL,C++生成非托管组件,系统源码含非托管组件生成C++源码。。。。。。 C#调用非托管DLL,C++生成非托管组件,系统源码含非托管组件生成C++源码。。。。。。
附件是c#调用c++托管代码,最后调用c++ DLL代码。
C# 使用托管的方式调用C++ dll,实现简单的加减法
当C#调用C++dll时,需要传递自定义结构的数组,该怎么办? 示例给出了方案
C#与C调用C++,非托管,含struct调用及enum公用,完整项目,可直接编译或运行。 项目将所有工程的输出统一到Debug。 vs2015+Framework4.6,注意工程的配置。 所有东西自写,只是出于样例参考的作用,所以代码规范...
c++ 通过 clr 方式 封装 原生 c++ dll , 由c# 对其调用 通过创建c++ clr项目dll_test_net,其内部封装纯c++ dll (dll_test_cpp),然后c#项目 test_net调用dll_test_net。 所有项目由vs2008创建。 防入坑提示: clr...
因为业务需要学习C#调用C++的库 但是C# 是在托管机制的运行 C++ 在非托管机制下运行 托管 是主机自己回收内存,统一释放。 非托管 自己使用内存自己释放。 环境 VS2017 大致实现思路:首先创建C++项目 实现...
托管、非托管C++库在C#中调用的测试程序
实例代码说明如何在托管的C#代码中调用非托管的Win32 API或者自己用C/C++写的Dll中的函数,以及如何传递输入、输出字符串参数,结构类型参数等问题。
c++生成的DLL供c#调用,此处只是个方法,没有具体实现的代码
但C++ kernel32.dll LoadLibrary只能加载指定路径非托管DLL..有了kernel.dll现在好了.可以像C#一样轻松从内存或资源里加载非托管DLL.. 从指定文件byte[]内存数组加载非托管DLL API 此DLL接口 FARPROC ...
在网上收索了一个关于使用C++调用C#的例子,当时觉就开始在我的机子上使用,花了几个小时,成功了,还写成了一个类,欢迎大家使用此类
主要介绍了C#调用C++DLL传递结构体数组的终极解决方案的相关资料,需要的朋友可以参考下
Delphi7静态动态调用VS2019 C#开发的非托管DLL且整合DLL
自己研究钩子和Dll写的,包括C#和C++中钩子的使用和Dll打包的方法,例子融合了托管和非托管的Dll如何打包和使用。例子都比较全面。适合新手学习交流。
本资源详细描述了如何在C#工程中使用VC++生成的dll动态库函数,注意采用的是非托管库的使用,C++类的直接使用慎入
标准C++生成的动态链接库dll,在C#只能调用其方法,不能导出类。...此源码为首先用托管C++为媒介,将标准C++链接库封装成.net的dll,再由C#直接调用的例子。 源码保证能运行,希望对大家粘合C#和C++代码有帮助。