You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 3.1 Linux下调用C原生库Marshal编码异常求助

问题与解决思路

问题描述

在Debian系统的.NET Core 3.1应用中引入第三方C原生库library.so,该库与Windows下对应的library.dll方法签名已确认一致。适配SDK代码后,调用Marshal.PtrToStructure<FoundEntityStruct>时首次循环抛出异常:No mapping for the Unicode character exists in the target multi-byte code page. (0x80070459)。推测是原生库使用ANSI编码而非UTF-8,但修改DllImport的CharSet为Ansi/Unicode/Auto均无效,求解决思路。

代码示例

[DllImport("library.so", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
internal static extern uint FindEntites(out FoundResultsStruct foundResult);
...
internal ArrayList FillCollection()
{
    var innerList = new ArrayList();

    FoundResultsStruct results;
    var code = FindEntites(out results);

    IntPtr ptr = results.Value;

    if (ptr == IntPtr.Zero)
        throw new Exception();

    int num = Marshal.SizeOf(typeof(FoundEntityStruct));

    for (long index = 0; index < results.Count; ++index)
    {
        innerList.Add(new CollectionEntity(Marshal.PtrToStructure<FoundEntityStruct>(ptr))); //异常触发点
        ptr = (IntPtr)((long)ptr + (long)num);
    }

    return innerList;
}

解决思路

  • 手动处理字符串编码,绕过自动Marshaling
    直接在FoundEntityStruct中将字符串字段替换为固定长度byte[],比如:

    [StructLayout(LayoutKind.Sequential)]
    public struct FoundEntityStruct
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public byte[] EntityName;
        // 其他字段...
    }
    

    转换时用原生库实际使用的编码(比如GBK、ISO-8859-1)手动解析:

    var entity = Marshal.PtrToStructure<FoundEntityStruct>(ptr);
    string name = Encoding.GetEncoding("GBK").GetString(entity.EntityName).TrimEnd('\0');
    

    避免依赖.NET跨平台下CharSet的不一致行为。

  • 强制结构体内存布局对齐
    Linux与Windows的结构体默认对齐规则可能不同,在FoundEntityStruct和FoundResultsStruct上显式指定布局和pack值,确保与原生库内存结构完全匹配:

    [StructLayout(LayoutKind.Sequential, Pack = 1)] // 或对应原生库的pack值,如4
    public struct FoundEntityStruct
    {
        // 字段定义
    }
    

    布局不匹配会导致读取错误字节序列,间接触发编码异常。

  • 修正调用约定
    Linux下C原生库默认调用约定为Cdecl,而非Windows常用的StdCall。修改DllImport的调用约定:

    [DllImport("library.so", CallingConvention = CallingConvention.Cdecl)]
    

    错误的调用约定会破坏栈结构,导致读取到无效内存数据。

  • 验证原生库返回的内存字节
    用Marshal.Copy导出内存数据,手动分析字符串编码:

    byte[] buffer = new byte[num];
    Marshal.Copy(ptr, buffer, 0, num);
    // 打印buffer十六进制内容,确认字符串部分的实际编码格式
    

    精准定位原生库使用的编码类型,再针对性处理。

内容的提问来源于stack exchange,提问作者Jonik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 12:50:21