欢迎光临
我们一直在努力

【MAUI】在 .NET MAUI 中调用原生 SO 库

文章目录

    • 前言
    • 一、准备 SO 库
    • 二、C# 绑定声明
    • 三、在 MAUI 中调用
    • 四、常见陷阱与解决
    • 五、进阶:复杂类型传递
    • 总结

前言

.NET MAUI 作为跨平台框架,允许开发者通过 P/Invoke (Platform Invoke) 或 MAUI 的特定绑定机制 调用原生动态链接库(Android 的 .so 文件)。本文聚焦 Android 平台,提供清晰可落地的实现方案。


一、准备 SO 库

将编译好的 SO 文件按架构分类放入项目:

YourProject/
├── Platforms/
│ └── Android/
│ └── lib/ # 自定义目录存放 SO 文件
│ ├── arm64-v8a/
│ │ └── libdemo.so
│ ├── armeabi-v7a/
│ │ └── libdemo.so
│ └── x86_64/
│ └── libdemo.so

关键点:确保 SO 文件的 Build Action 设置为 AndroidNativeLibrary,或在 .csproj 中显式声明:

<ItemGroup>
<AndroidNativeLibrary Include="Platforms\\Android\\lib\\arm64-v8a\\libdemo.so" />
<AndroidNativeLibrary Include="Platforms\\Android\\lib\\armeabi-v7a\\libdemo.so" />
</ItemGroup>


二、C# 绑定声明

使用 DllImport 特性声明外部函数。假设 SO 库导出了以下 C 函数:

// native-lib.h
int add(int a, int b);
const char* get_version(void);

对应的 C# 绑定代码:

using System.Runtime.InteropServices;

public static partial class NativeLib
{
private const string LibName = "demo"; // 对应 libdemo.so

[DllImport(LibName, EntryPoint = "add", CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int a, int b);

[DllImport(LibName, EntryPoint = "get_version", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetVersionInternal();

// 封装 Marshal 转换,提供类型安全的公开接口
public static string GetVersion()
{
IntPtr ptr = GetVersionInternal();
return Marshal.PtrToStringUTF8(ptr) ?? string.Empty;
}
}

注意要点:

  • EntryPoint 必须与 C 函数签名完全匹配(区分大小写)
  • 字符串/指针类型需手动 Marshal 转换
  • CallingConvention 需与编译 SO 时的设置一致(通常为 Cdecl)

三、在 MAUI 中调用

public partial class MainPage : ContentPage
{
private void OnInvokeClicked(object sender, EventArgs e)
{
try
{
int result = NativeLib.Add(10, 20);
string version = NativeLib.GetVersion();

ResultLabel.Text = $"计算结果: {result}\\n版本: {version}";
}
catch (DllNotFoundException ex)
{
ResultLabel.Text = $"SO 库加载失败: {ex.Message}";
}
catch (EntryPointNotFoundException ex)
{
ResultLabel.Text = $"函数未找到: {ex.Message}";
}
}
}


四、常见陷阱与解决

问题现象原因解决方案
DllNotFoundException SO 文件未打包或架构不匹配 检查 .csproj 配置,确认设备架构与 SO 文件对应
EntryPointNotFoundException 函数名修饰(mangling)或签名不符 使用 extern "C" 导出 C 风格函数,核对大小写
中文/特殊字符乱码 编码不匹配 C 端使用 UTF-8,C# 端使用 Marshal.PtrToStringUTF8
Release 模式崩溃 SO 未随 APK 分发 检查 AndroidPackageFormat 和链接器设置

五、进阶:复杂类型传递

对于结构体或回调函数,需更严谨的互操作定义:

// 传递结构体
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int X;
public int Y;
}

[DllImport(LibName)]
public static extern double CalculateDistance(Point p1, Point p2);

// 回调函数(需保持委托引用防 GC)
public delegate void ProgressCallback(int percent);

[DllImport(LibName)]
public static extern void ProcessData(byte[] data, int length, ProgressCallback callback);


总结

MAUI 调用 SO 库的核心在于:

  • 正确放置并配置 SO 文件到项目
  • 精确声明 DllImport 的函数签名和调用约定
  • 妥善处理 数据类型的 Marshaling
  • 对于复杂场景,可考虑使用 Native Library Interop 或生成绑定库(如 CppSharp),但简单的 P/Invoke 足以应对大多数需求。

    赞(0)
    未经允许不得转载:171主机测评 » 【MAUI】在 .NET MAUI 中调用原生 SO 库
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址