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

文章目录

前言

.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 中显式声明:

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

二、C# 绑定声明

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

c 复制代码
// native-lib.h
int add(int a, int b);
const char* get_version(void);

对应的 C# 绑定代码:

csharp 复制代码
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 中调用

csharp 复制代码
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 和链接器设置

五、进阶:复杂类型传递

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

csharp 复制代码
// 传递结构体
[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 库的核心在于:

  1. 正确放置并配置 SO 文件到项目
  2. 精确声明 DllImport 的函数签名和调用约定
  3. 妥善处理 数据类型的 Marshaling

对于复杂场景,可考虑使用 Native Library Interop 或生成绑定库(如 CppSharp),但简单的 P/Invoke 足以应对大多数需求。

相关推荐
牧濑红莉1 小时前
网络安全竞赛pwn全解及第一道ai的wp
javascript·.net
AI行业学习13 小时前
OpenAI Codex 完整下载 & 安装指南(2026最新版)
linux·开发语言·python·.net·notepad++
非酋讨厌1 天前
.NET 11 Preview 4 正式发布:Runtime-Async 全面启用、Process API 大幅扩展
.net
天天代码码天天1 天前
工控机还是 Win7?我把 PP-OCRv6 做成了 14MB 纯 CPU DLL,.NET 3.5 也能直接调用
.net·win7·pp-ocrv6
找死的豆腐1 天前
基于.NET的Windows窗体编程之WinForms图像控件
windows·.net
sdghterhd2 天前
当 GIS 开发遇上 .NET:一个 GDAL 集成工具库的架构设计与实践
.net
mudtools2 天前
HttpUtils:一个编译时生成的声明式 HTTP 客户端框架
网络·网络协议·http·.net
冰可乐配2 天前
OpenClaw.NET .NET 原生插件开发完全指南:以 Mempalace 插件为范例
.net
财财源源2 天前
.NET 程序保护实战系列01-流水线架构与保护引擎总览
架构·.net