C#跨平台开发时,有时候需要调用C/C++编写的动态链接库(DLL)来完成一些特定功能,这时我们需要使用P/Invoke (Platform Invoke)机制来调用DLL。下面是使用C/C++生成动态链接库供C#跨平台项目调用的完整攻略:
步骤一:创建DLL项目
首先,在Visual Studio中创建一个动态链接库项目,可以选择C++/CLI(DLL)模板。以生成一个名为 "NativeLibrary" 的DLL为例。代码如下:
// NativeLibrary.h
#pragma once
#ifdef NATIVELIBRARY_EXPORTS
#define NATIVELIBRARY_API __declspec(dllexport)
#else
#define NATIVELIBRARY_API __declspec(dllimport)
#endif
extern "C" NATIVELIBRARY_API int add(int a, int b);
// NativeLibrary.cpp
#include "pch.h"
#include "framework.h"
#include "NativeLibrary.h"
extern "C" NATIVELIBRARY_API int add(int a, int b)
{
return a + b;
}
在Add函数声明上有一个宏定义“__declspec(dllexport)”用来指定在编译为DLL时需要导出该函数。
步骤二:生成DLL
在Visual Studio中将项目生成为DLL,生成后可以在项目目录下找到一个名为 "NativeLibrary.dll" 的DLL文件。
步骤三:使用DLL
在C#跨平台项目中引用该DLL文件,并设置其为“复制到输出目录”。然后使用P/Invoke机制来调用DLL的导出函数。
例如,我们在C#中定义一个与导出函数 "add" 匹配的方法,在类中添加以下代码:
// NativeLibraryImport.cs
using System.Runtime.InteropServices;
public static class NativeLibraryImport
{
[DllImport("NativeLibrary")]
public static extern int add(int a, int b);
}
几点说明:
- 在调用DLL函数前,必须将DLL文件复制到与可执行文件相同的目录中,或将DLL路径设为环境变量。
- DllImport的第一个参数是要调用的DLL文件名(不需要扩展名),而第二个参数是要调用的函数名。如果DLL文件名和函数名不同于导出名称(如在上面的例子中),则应指定正确的名称。
- 参数和返回值的类型应该和DLL导出函数的参数和返回值类型匹配。
测试如下:
using System;
class Program
{
static void Main()
{
int result = NativeLibraryImport.add(1, 2);
Console.WriteLine(result);
}
}
输出结果为3。
示例二
下面再介绍一个使用P/Invoke调用带回调函数的DLL的示例。
定义DLL项目中的my_dll.h文件如下:
// my_dll.h
#pragma once
#include <functional>
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
typedef int(*my_callback)(int);
using MyCallback = std::function<int(int)>;
extern "C" DLL_API void use_callback_c(my_callback func, int arg);
extern "C" DLL_API void use_callback_cpp(MyCallback func, int arg);
my_callback是纯C的函数指针类型,而MyCallback是C++11标准中的“函数对象”,做法类似于C#的“委托”。
// my_dll.cpp
#include "pch.h"
#include "framework.h"
#include "my_dll.h"
extern "C" DLL_API void use_callback_c(my_callback func, int arg)
{
int result = func(arg);
printf("use_callback_c result=%d\n", result);
}
extern "C" DLL_API void use_callback_cpp(MyCallback func, int arg)
{
int result = func(arg);
printf("use_callback_cpp result=%d\n", result);
}
在C#中定义一个委托类型,用于接收回调函数:
// MyCallback.cs
public delegate int MyCallback(int arg);
在C#中调用DLL时,可以使用P/Invoke来传递回调函数,代码如下:
// MyDllImport.cs
using System;
using System.Runtime.InteropServices;
public static class MyDllImport
{
[DllImport("my_dll")]
public static extern void use_callback_c(MyCallback callback, int arg);
[DllImport("my_dll")]
public static extern void use_callback_cpp(MyCallback callback, int arg);
}
测试如下:
static int callback(int arg)
{
return arg * 2;
}
static void Main(string[] args)
{
MyCallback cb = new MyCallback(callback);
MyDllImport.use_callback_c(cb, 2);
MyDllImport.use_callback_cpp(cb, 3);
}
输出结果为:
use_callback_c result=4
use_callback_cpp result=6
这种方法可以用于在C#跨平台程序中调用使用C/C++编写的任何DLL。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#跨平台开发之使用C/C++生成的动态链接库 - Python技术站