Bootstrap

c# 如何调用C++ 动态链接库DLL文件

c# 如何调用C++ 动态链接库DLL文件

在工作项目中,经常遇到C#需要调用C++代码的情况,C++代码一般封装成动态链接库DLL文件,下面内容记录一下C#工程如何调用DLL。
前文提示:关于DLL的封装可以参考前一篇内容: C++ 如何封装代码成DLL 动态链接库供其他项目或编程语言使用
解决方案工程文件下载:工程文件

1 给C#工程导入C++ DLL文件

1.1 拷贝DLL文件至项目exe所在目录

将前文生成的DLL文件,本实例中为DataTestDLL.dll拷贝至C#工程的debug目录下,使得C#工程的exe文件与dll文件在同一目录,如下图所示:
拷贝DLL文件

1.2 添加项目引用

点击C#项目依赖项,浏览添加DataTestDll.dll文件,如下图所示:
项目依赖项
提示:如果DLL工程与C#工程在同一解决方案下的话,添加项目依赖时,会自动显示可引用的dll文件,此时可直接勾选相应的dll即可。

2 C# 代码编写

C#引用C++ 有托管与非托管两种方式,在实用角度出发:非托管需要一个个声明引用,
但是托管(虽然麻烦)不用声明,只需要调好配置即可,还是比较方便的。在实际项目应用中,可根据dll文件的特性及项目需求选择使用哪种方式,本文内容以非托管方式进行。下面为C#代码:

using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    internal class Program
    {
        [DllImport("DataTestDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void COM_PrintHello();

        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World from C#!");
            COM_PrintHello();
        }
    }
}

简单介绍一下:

[DllImport("DataTestDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void COM_PrintHello();

DllImport的第一个参数UCamer.dll是动态库dll的路径,此dll放在程序运行的根目录。CallingConvention 参数是c#调用c++的方式 是个枚举 msdn解释如下:
Cdecl 调用方清理堆栈。这使您能够调用具有 varargs 的函数(如 Printf),使之可用于接受可变数目的参数的方法。
FastCall 不支持此调用约定。
StdCall 被调用方清理堆栈。这是使用平台 invoke 调用非托管函数的默认约定。
ThisCall 第一个参数是 this 指针,它存储在寄存器 ECX 中。其他参数被推送到堆栈上。此调用约定用于对从非托管 DLL 导出的类调用方法。
Winapi 此成员实际上不是调用约定,而是使用了默认平台调用约定。例如,在 Windows 上默认为 StdCall,在 Windows CE.NET 上默认为 Cdecl。

2.1 运行结果

运行结果
我们可以看C#首先打印了“Hello, World from C#!”内容,然后调用前一节 C++ 如何封装代码成DLL 动态链接库供其他项目或编程语言使用中DataTestDLL.cpp中COM_PrintHello()函数打印的“Hello World from DLL!”内容,C#调用C++ 动态链接库DLL文件成功。

#include "DataTestDLL.h"
#include <iostream>

COM_API void COM_PrintHello()
{
	std::cout << "Hello World from DLL!\n";
}

COM_API int COM_Add(int a, int b)
{
	return a + b;
}

2.2 调用有参及有返回值函数

在前一节 C++ 如何封装代码成DLL 动态链接库供其他项目或编程语言使用中DataTestDLL.cpp中定义了COM_API int COM_Add(int a, int b)函数,在C#中的调用方式如下所示:
1、添加引用声明:

        [DllImport("DataTestDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int COM_Add(int a, int b);

2、调用函数

            int sum = COM_Add(2, 3);
           Console.WriteLine("sum is {0}", sum);

3、查看运行结果:
运行结果
我们看到运行结果正确。

2.3 完整代码

using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    internal class Program
    {
        [DllImport("DataTestDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void COM_PrintHello();

        [DllImport("DataTestDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int COM_Add(int a, int b);

        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World from C#!");
            COM_PrintHello();
            int sum = COM_Add(2, 3);
            Console.WriteLine("sum is {0}", sum);
        }
    }
}

3 总结

本文主要记录了C#项目以非托管方式进行调用C++ DLL文件。解下来将记录C# 调用C++ DLL 中复杂函数时数据处理过程,如返回值或参数为数组、指针、引用、结构体,结构体指针等,这些在C++已经让人有些头疼的东西,如何在C#中处理,欢迎持续关注并留言讨论。

;